diff --git a/packages/api/package.json b/packages/api/package.json index 1dd3b9897..7d6e9927e 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -84,6 +84,7 @@ }, "devDependencies": { "@playwright/test": "1.40.1", + "@tbd54566975/dwn-sdk-js": "0.2.18", "@types/chai": "4.3.6", "@types/eslint": "8.44.2", "@types/mocha": "10.0.1", diff --git a/packages/api/src/web5.ts b/packages/api/src/web5.ts index 7193a988d..9b08dd30e 100644 --- a/packages/api/src/web5.ts +++ b/packages/api/src/web5.ts @@ -184,7 +184,7 @@ export class Web5 { // Initialize, if necessary, and start the agent. if (await userAgent.firstLaunch()) { - await userAgent.initialize({ password, recoveryPhrase }); + recoveryPhrase = await userAgent.initialize({ password, recoveryPhrase }); } await userAgent.start({ password }); diff --git a/packages/api/tests/web5.spec.ts b/packages/api/tests/web5.spec.ts index ef22d6bb6..9438fc7b2 100644 --- a/packages/api/tests/web5.spec.ts +++ b/packages/api/tests/web5.spec.ts @@ -26,6 +26,24 @@ describe('Web5', () => { await testHarness.closeStorage(); }); + describe('connect()', () => { + it('accepts an externally created DID', async () => { + const testIdentity = await testHarness.createIdentity({ + name : 'Test', + testDwnUrls : ['https://dwn.example.com'] + }); + + // Call connect() with the custom agent. + const { web5, did } = await Web5.connect({ + agent : testHarness.agent, + connectedDid : testIdentity.did.uri + }); + + expect(did).to.exist; + expect(web5).to.exist; + }); + }); + describe('constructor', () => { it('instantiates Web5 API with provided Web5Agent and connectedDid', async () => { // Create a new Identity. @@ -41,23 +59,73 @@ describe('Web5', () => { expect(web5).to.have.property('dwn'); expect(web5).to.have.property('vc'); }); + + it('supports a single agent with multiple Web5 instances and different DIDs', async () => { + // Create two identities, each of which is stored in a new tenant. + const careerIdentity = await testHarness.agent.identity.create({ + metadata : { name: 'Social' }, + didMethod : 'jwk' + }); + const socialIdentity = await testHarness.agent.identity.create({ + metadata : { name: 'Social' }, + didMethod : 'jwk' + }); + + // Instantiate a Web5 instance with the "Career" Identity, write a record, and verify the result. + const web5Career = new Web5({ agent: testHarness.agent, connectedDid: careerIdentity.did.uri }); + expect(web5Career).to.exist; + + // Instantiate a Web5 instance with the "Social" Identity, write a record, and verify the result. + const web5Social = new Web5({ agent: testHarness.agent, connectedDid: socialIdentity.did.uri }); + expect(web5Social).to.exist; + }); }); - describe('connect()', () => { - it('accepts an externally created DID', async () => { - const testIdentity = await testHarness.createIdentity({ - name : 'Test', - testDwnUrls : ['https://dwn.example.com'] + describe('scenarios', () => { + it('writes records with multiple identities under management', async () => { + // First launch and initialization. + await testHarness.agent.initialize({ password: 'test' }); + + // Start the Agent, which will decrypt and load the Agent's DID from the vault. + await testHarness.agent.start({ password: 'test' }); + + // Create two identities, each of which is stored in a new tenant. + const careerIdentity = await testHarness.agent.identity.create({ + metadata : { name: 'Social' }, + didMethod : 'jwk' + }); + const socialIdentity = await testHarness.agent.identity.create({ + metadata : { name: 'Social' }, + didMethod : 'jwk' }); - // Call connect() with the custom agent. - const { web5, did } = await Web5.connect({ - agent : testHarness.agent, - connectedDid : testIdentity.did.uri + // Instantiate a Web5 instance with the "Career" Identity, write a record, and verify the result. + const web5Career = new Web5({ agent: testHarness.agent, connectedDid: careerIdentity.did.uri }); + const careerResult = await web5Career.dwn.records.write({ + data : 'Hello, world!', + message : { + schema : 'foo/bar', + dataFormat : 'text/plain' + } }); + expect(careerResult.status.code).to.equal(202); + expect(careerResult.record).to.exist; + expect(careerResult.record?.author).to.equal(careerIdentity.did.uri); + expect(await careerResult.record?.data.text()).to.equal('Hello, world!'); - expect(did).to.exist; - expect(web5).to.exist; + // Instantiate a Web5 instance with the "Social" Identity, write a record, and verify the result. + const web5Social = new Web5({ agent: testHarness.agent, connectedDid: socialIdentity.did.uri }); + const socialResult = await web5Social.dwn.records.write({ + data : 'Hello, everyone!', + message : { + schema : 'foo/bar', + dataFormat : 'text/plain' + } + }); + expect(socialResult.status.code).to.equal(202); + expect(socialResult.record).to.exist; + expect(socialResult.record?.author).to.equal(socialIdentity.did.uri); + expect(await socialResult.record?.data.text()).to.equal('Hello, everyone!'); }); }); }); @@ -69,10 +137,13 @@ describe('Web5', () => { keyDerivationWorkFactor : 1, store : new MemoryStore() }); - const { web5 } = await Web5.connect({ agentVault }); + const { web5, recoveryPhrase } = await Web5.connect({ agentVault }); expect(web5).to.exist; expect(web5.agent).to.be.instanceOf(Web5UserAgent); + // Verify recovery phrase is a 12-word string. + expect(recoveryPhrase).to.be.a('string'); + expect(recoveryPhrase.split(' ')).to.have.lengthOf(12); }); }); }); \ No newline at end of file diff --git a/packages/identity-agent/.vscode/launch.json b/packages/identity-agent/.vscode/launch.json index bc398db79..8ff014b0e 100644 --- a/packages/identity-agent/.vscode/launch.json +++ b/packages/identity-agent/.vscode/launch.json @@ -8,12 +8,15 @@ "type": "node", "request": "launch", "name": "test:node", - "runtimeExecutable": "${workspaceFolder:root}/node_modules/.bin/mocha", + "runtimeExecutable": "${workspaceFolder:identity-agent}/node_modules/.bin/mocha", "console": "internalConsole", "preLaunchTask": "build tests", "skipFiles": [ "/**" - ] + ], + "env": { + "TEST_DWN_URL": "http://localhost:3000" + } } ] } \ No newline at end of file diff --git a/packages/identity-agent/README.md b/packages/identity-agent/README.md new file mode 100644 index 000000000..8fb2a118a --- /dev/null +++ b/packages/identity-agent/README.md @@ -0,0 +1,326 @@ +# Web5 Identity Agent + +| A library for building Web5 identity management applications | +| ------------------------------------------------------------ | + +[![NPM Package][identity-agent-npm-badge]][identity-agent-npm-link] +[![NPM Downloads][identity-agent-downloads-badge]][identity-agent-npm-link] + +[![Build Status][identity-agent-build-badge]][identity-agent-build-link] +[![Open Issues][identity-agent-issues-badge]][identity-agent-issues-link] +[![Code Coverage][identity-agent-coverage-badge]][identity-agent-coverage-link] + +--- + +- [Introduction](#introduction) +- [Getting Started](#getting-started) + - [Node.js](#nodejs) + - [Web Browsers](#web-browsers) + - [React Native](#react-native) +- [Contributing](#contributing) +- [Core Concepts](#core-concepts) + - [Launching an Identity Agent](#launching-an-identity-agent) + - [Creating an End User Identity](#creating-an-end-user-identity) +- [Customization](#customization) + - [Using Non-default Data Stores](#using-non-default-data-stores) + +--- + + + +The Identity Agent SDK is a component of the +[Web5 JS](https://github.com/TBD54566975/web5-js) platform, created to simplify the development of +applications that manage multiple Web5 identities on behalf of a single entity. Identity agents, +sometimes called "wallets", are typically +native desktop (e.g., [Electron](https://www.electronjs.org)) or +mobile (e.g, [React Native](https://reactnative.dev)) apps that are installed by end users to +manage one or more decentralized identities. + +## Getting Started + +This JavaScript library was designed for modern development runtimes, including Node.js, web +browsers, and React Native. The package is distributed as `@web5/identity-agent` via +[npmjs.com][identity-agent-npm-link], [jsdelivr.com][identity-agent-jsdelivr-link], +[unpkg.com][identity-agent-unpkg-link], and [github.com][identity-agent-repo-link]. + +### Node.js + +This library is designed and tested for the _active_ (`v20`) and _maintenance_ +(`v18`) [LTS releases](https://nodejs.org/en/about/previous-releases) of Node.js + +Install the latest version of `@web5/identity-agent` using `npm` or your preferred package manager: + +```shell +npm install @web5/identity-agent +``` + +Example ESM import: + +```js +import { Ed25519 } from "@web5/identity-agent"; +``` + +Example CJS require: + +```js +const { Ed25519 } = require("@web5/identity-agent"); +``` + +### Web Browsers + +A polyfilled distribution is published to [jsdelivr.com][identity-agent-jsdelivr-browser] and +[unpkg.com][identity-agent-unpkg-browser] that can imported in a module ` + + +``` + + + +> [!IMPORTANT] +> The `@web5/identity-agent` library depends on the +> [Web Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API) in web browsers. +> Web Crypto is available in all modern browsers but is accessible only in a +> [secure context](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts). +> +> This means you will not be able to use many `@web5/identity-agent` features in a browser unless +> the page is served over `https://` or `wss://`. Locally-delivered resources such as those with +> `http://127.0.0.1`, `http://localhost`, or `http://*.localhost` URLs are also considered to have +> been delivered securely. + +### React Native + +For React Native, you may need a +[polyfill](https://github.com/LinusU/react-native-get-random-values) for `crypto.getRandomValues`. + +```js +import "react-native-get-random-values"; +``` + +## Contributing + +We welcome you to join our open source community. Whether you're new to open source or a seasoned +contributor, there's a place for you here. From coding to documentation, every contribution matters. +Check out our [contribution guide][contributing-link] for ways to get started. + +For help, discussion about best practices, or to chat with others building on Web5 join our +[Discord Server][discord-link]: + +[![discord-badge]][discord-link] + +Remember, contributing is not just about code; it's about building together. Join us in shaping the +future of the Web! + +## Core Concepts + +### Launching an Identity Agent + +An [agent](https://developer.tbd.website/docs/web5/learn/agents) is software that acts on behalf of +a user to manage identity, public or private data, and interactions with other apps or services in a +network. Identity Agents are a specific type of agent focused on simplifying the management of a +person's online identities, since they often have several. + +Many people already use multiple personas when interacting with others online depending on +whether the context is family, career, or social. This might take the form of a work issued email +that you use for career apps and a personal email that you use when interacting with family and +friends. The same is true with Web5 identities except that the unique identifier is a +[Decentralized Identifier (DID)](https://developer.tbd.website/docs/web5/learn/decentralized-identifiers) +rather than an email address. Developers can create identity management apps, sometimes called +"wallets", that focus on making it easy to connect Web5 apps to one or more identities and keep track +of which apps have access to your data. + +Every time an Identity Agent app runs on a computing device, whether desktop or mobile, the +following should occur: + +1. Check whether the agent was previously initialized. +2. If this is the first run on this device, initialize the agent. +3. Load the agent's DID and keys to prepare for use. + +An example implementation of these steps would be: + +```ts +import { Web5IdentityAgent } from '@web5/identity-agent' + +// Create a Web5 Identity Agent instance. +const agent = await Web5IdentityAgent.create(); + +// Prompt the end user to enter a password, which prevents unauthorized access to the encrypted +// identity vault that secures the Agent's DID and cryptographic keys. +const password = /* input by user */ + + +// If its the first launch, initialize the identity vault for the agent, and if not provided, return +// the generated recovery phrase. +let recoveryPhrase: string; +if (await agent.firstLaunch()) { + // Unless provided, a 12-word recovery phrase will automatically be generated and the Agent's + // cryptographic keys deterministically generated from the phrase. + recoveryPhrase = await agent.initialize({ password, recoveryPhrase }); +} + +// On every launch unlock the identity vault using the provided password and load the Agent's DID +// and keys from encrypted vault storage. +await userAgent.start({ password }); +``` + +### Creating an End User Identity + +In Web5 apps, a user’s unique identifier - like an email address - is called a +[Decentralized Identifier (DID)](https://developer.tbd.website/docs/web5/learn/decentralized-identifiers). +You can think of an Identity as an isolated space uniquely identified by a DID that stores and +manages the data relevant to a particular context or use case. + +For example, a person might have one identity for work-related apps and data, another for +personal/family, and yet another for their social media persona. + +By organizing cryptographic keys, profile information, data, and credentials into separate +identities, an Identity Agent can help users maintain greater control over their personal +information and how it is shared with others. Users can choose which identity to share with +different parties, depending on the context and level of trust. + +The follow code example walks through how to create a new identity, manage it with the Identity +Agent, and store data in the +[Decentralized Web Node (DWN)](https://developer.tbd.website/docs/web5/learn/decentralized-web-nodes/) +data store controlled by the newly created identity. + +```ts +import { getTechPreviewDwnEndpoints, Web5 } from '@web5/api'; + +// Retrieve publicly addressable DWNs that other network participants can use to exchange messages +// and data with the new Identity. +const serviceEndpointNodes = await getTechPreviewDwnEndpoints(); + +// Generate a new Identity for the end-user. +const careerIdentity = await agent.identity.create({ + didMethod : 'dht', + metadata : { name: 'Alice' }, + didOptions : { + services: [ + { + id : 'dwn', + type : 'DecentralizedWebNode', + serviceEndpoint : serviceEndpointNodes, + enc : '#enc', + sig : '#sig', + } + ], + verificationMethods: [ + { + algorithm : 'Ed25519', + id : 'sig', + purposes : ['assertionMethod', 'authentication'] + }, + { + algorithm : 'secp256k1', + id : 'enc', + purposes : ['keyAgreement'] + } + ] + } +}); + +// Enable management by this Identity Manager. +await agent.identity.manage({ portableIdentity: await identity.export() }); +``` + +### Writing Data to an Identity's Data Store + +The [Web5 API](https://github.com/TBD54566975/web5-js/tree/main/packages/api) makes it simple to +store data in an identity's DWN data store by handling all of the message and data preparation and +processing steps. Using the `careerIdentity` created earlier, a simple message payload can be +written as follows: + +```ts +// Instantiate a Web5 instance with the "Career" Identity. +const web5Career = new Web5({ agent, connectedDid: careerIdentity.did.uri }); + +// Write a simple text record. +const { record, status } = await web5Career.dwn.records.write({ + data : 'Message', + message : { + dataFormat : 'text/plain' + } +}); + +console.log(status.code) // Output: 202 + +const recordData = await record?.data.text(); +console.log(recordData); // Output: Message +``` + +## Customization + +### Using Non-default Data Stores + +By default, `Web5IdentityAgent` uses a [LevelDB](https://github.com/Level/level) store for both the +agent's identity vault and the DID resolver cache. For testing and prototyping purposes it may be +desirable to use an in-memory store that doesn't persist data. There are also runtime environments, +such as React Native, that don't support using the [level](https://www.npmjs.com/package/level) +package. Any implementation of the +[`KeyValueStore`](https://github.com/TBD54566975/web5-js/blob/5f364bc0d859e28f1388524ebe8ef152a71727c4/packages/common/src/types.ts#L4-L43) +interface can be substituted for the default identity vault and DID resolver cache. + +For example, to use the in-memory `KeyValueStore` implementation from `@web5/common`: + +```ts +import { MemoryStore } from '@web5/common'; +import { DidDht, DidJwk } from '@web5/dids'; +import { Web5IdentityAgent } from '@web5/identity-agent'; +import { AgentDidApi, DidResolverCacheLevel, DwnDidStore } from '@web5/agent'; + +// Instantiate Identity Vault with an in-memory store. +const agentVault = new HdIdentityVault({ + keyDerivationWorkFactor: 210_000, + store: new MemoryStore() +}); + +// Instantiate DID API with an in-memory resolver cache. +const didApi = new AgentDidApi({ + didMethods : [DidDht, DidJwk], + resolverCache : new DidResolverCacheMemory(), + store : new DwnDidStore() +}); + +// Create a Web5 Identity Agent instance. +const agent = await Web5IdentityAgent.create({ agentVault, didApi}); +``` + +## Project Resources + +| Resource | Description | +| --------------------------------------- | ----------------------------------------------------------------------------- | +| [CODEOWNERS][codeowners-link] | Outlines the project lead(s) | +| [CODE OF CONDUCT][code-of-conduct-link] | Expected behavior for project contributors, promoting a welcoming environment | +| [CONTRIBUTING][contributing-link] | Developer guide to build, test, run, access CI, chat, discuss, file issues | +| [GOVERNANCE][governance-link] | Project governance | +| [LICENSE][license-link] | Apache License, Version 2.0 | + +[identity-agent-npm-badge]: https://img.shields.io/npm/v/@web5/identity-agent.svg?style=flat&color=blue&santize=true +[identity-agent-npm-link]: https://www.npmjs.com/package/@web5/identity-agent +[identity-agent-downloads-badge]: https://img.shields.io/npm/dt/@web5/identity-agent?&color=blue +[identity-agent-build-badge]: https://img.shields.io/github/actions/workflow/status/TBD54566975/web5-js/tests-ci.yml?branch=main&label=build +[identity-agent-build-link]: https://github.com/TBD54566975/web5-js/actions/workflows/tests-ci.yml +[identity-agent-coverage-badge]: https://img.shields.io/codecov/c/gh/TBD54566975/web5-js/main?style=flat&token=YI87CKF1LI +[identity-agent-coverage-link]: https://app.codecov.io/github/TBD54566975/web5-js/tree/main/packages%2Fcrypto-aws-kms +[identity-agent-issues-badge]: https://img.shields.io/github/issues/TBD54566975/web5-js/package:%20identity-agent?label=issues +[identity-agent-issues-link]: https://github.com/TBD54566975/web5-js/issues?q=is%3Aopen+is%3Aissue+label%3A"package%3A+identity-agent" +[identity-agent-repo-link]: https://github.com/TBD54566975/web5-js/tree/main/packages/identity-agent +[identity-agent-jsdelivr-link]: https://www.jsdelivr.com/package/npm/@web5/identity-agent +[identity-agent-jsdelivr-browser]: https://cdn.jsdelivr.net/npm/@web5/identity-agent/dist/browser.mjs +[identity-agent-unpkg-link]: https://unpkg.com/@web5/identity-agent +[identity-agent-unpkg-browser]: https://unpkg.com/@web5/identity-agent/dist/browser.mjs +[codeowners-link]: https://github.com/TBD54566975/web5-js/blob/main/CODEOWNERS +[code-of-conduct-link]: https://github.com/TBD54566975/web5-js/blob/main/CODE_OF_CONDUCT.md +[contributing-link]: https://github.com/TBD54566975/web5-js/blob/main/CONTRIBUTING.md +[governance-link]: https://github.com/TBD54566975/web5-js/blob/main/GOVERNANCE.md +[license-link]: https://github.com/TBD54566975/web5-js/blob/main/LICENSE +[discord-badge]: https://img.shields.io/discord/937858703112155166?color=5865F2&logo=discord&logoColor=white +[discord-link]: https://discord.com/channels/937858703112155166/969272658501976117 diff --git a/packages/identity-agent/package.json b/packages/identity-agent/package.json index 7235f22a3..484f452b2 100644 --- a/packages/identity-agent/package.json +++ b/packages/identity-agent/package.json @@ -1,6 +1,6 @@ { "name": "@web5/identity-agent", - "version": "0.2.6", + "version": "0.3.0", "type": "module", "main": "./dist/cjs/index.js", "module": "./dist/esm/index.js", @@ -68,10 +68,10 @@ "node": ">=20.9.0" }, "dependencies": { - "@web5/agent": "0.2.6", - "@web5/common": "0.2.3", - "@web5/crypto": "0.2.2", - "@web5/dids": "0.2.4" + "@web5/agent": "0.3.0", + "@web5/common": "0.2.4", + "@web5/crypto": "0.4.0", + "@web5/dids": "0.4.1" }, "devDependencies": { "@playwright/test": "1.40.1", @@ -85,7 +85,6 @@ "@typescript-eslint/parser": "6.4.0", "@web/test-runner": "0.18.0", "@web/test-runner-playwright": "0.11.0", - "@web5/api": "0.8.4", "c8": "9.0.0", "chai": "4.3.10", "chai-as-promised": "7.1.1", diff --git a/packages/identity-agent/src/identity-agent.ts b/packages/identity-agent/src/identity-agent.ts index aa660628d..78c642ce6 100644 --- a/packages/identity-agent/src/identity-agent.ts +++ b/packages/identity-agent/src/identity-agent.ts @@ -4,261 +4,240 @@ import type { VcResponse, DidResponse, DwnResponse, - SyncManager, - AppDataStore, + DidInterface, + DwnInterface, SendVcRequest, SendDwnRequest, ProcessVcRequest, - Web5ManagedAgent, ProcessDwnRequest, + Web5PlatformAgent, } from '@web5/agent'; import { LevelStore } from '@web5/common'; -import { EdDsaAlgorithm } from '@web5/crypto'; -import { DidIonMethod, DidKeyMethod, DidResolver } from '@web5/dids'; +import { BearerDid, DidDht, DidJwk, DidResolverCacheLevel } from '@web5/dids'; import { - LocalKms, - DidManager, - DidMessage, - DwnManager, - KeyManager, - DidStoreDwn, - KeyStoreDwn, - AppDataVault, + AgentDidApi, + AgentDwnApi, + DwnDidStore, + DwnKeyStore, + AgentSyncApi, Web5RpcClient, - IdentityManager, - IdentityStoreDwn, - SyncManagerLevel, - PrivateKeyStoreDwn, - cryptoToPortableKeyPair, + AgentCryptoApi, + AgentKeyManager, + HdIdentityVault, + LocalKeyManager, + SyncEngineLevel, + AgentIdentityApi, + DwnIdentityStore, } from '@web5/agent'; -export type IdentityAgentOptions = { - agentDid: string; - appData: AppDataStore; - didManager: DidManager; - didResolver: DidResolver; - dwnManager: DwnManager; - identityManager: IdentityManager; - keyManager: KeyManager; +/** + * Initialization parameters for {@link Web5IdentityAgent}, including an optional recovery phrase that + * can be used to derive keys to encrypt the vault and generate a DID. + */ +export type AgentInitializeParams = { + /** + * The password used to secure the Agent vault. + * + * The password selected should be strong and securely managed to prevent unauthorized access. + */ + password: string; + + /** + * An optional recovery phrase used to deterministically generate the cryptographic keys for the + * Agent vault. + * + * Supplying this phrase enables the vault's contents to be restored or replicated across devices. + * If omitted, a new phrase is generated, which should be securely recorded for future recovery needs. + */ + recoveryPhrase?: string; + }; + +export type AgentStartParams = { + /** + * The password used to unlock the previously initialized Agent vault. + */ + password: string; + } + +export type AgentParams = { + /** Optional. The Decentralized Identifier (DID) representing this Web5 User Agent. */ + agentDid?: BearerDid; + /** Encrypted vault used for managing the Agent's DID and associated keys. */ + agentVault: HdIdentityVault; + /** Provides cryptographic capabilties like signing, encryption, hashing and key derivation. */ + cryptoApi: AgentCryptoApi; + /** Specifies the local path to be used by the Agent's persistent data stores. */ + dataPath?: string; + /** Facilitates DID operations including create, update, and resolve. */ + didApi: AgentDidApi; + /** Facilitates DWN operations including processing and sending requests. */ + dwnApi: AgentDwnApi; + /** Facilitates decentralized Identity operations including create, import, and export. */ + identityApi: AgentIdentityApi; + /** Responsible for securely managing the cryptographic keys of the agent. */ + keyManager: TKeyManager; + /** Remote procedure call (RPC) client used to communicate with other Web5 services. */ rpcClient: Web5Rpc; - syncManager: SyncManager; + /** Facilitates data synchronization of DWN records between nodes. */ + syncApi: AgentSyncApi; } -export class IdentityAgent implements Web5ManagedAgent { - agentDid: string; - appData: AppDataStore; - didManager: DidManager; - didResolver: DidResolver; - dwnManager: DwnManager; - identityManager: IdentityManager; - keyManager: KeyManager; - rpcClient: Web5Rpc; - syncManager: SyncManager; - - constructor(options: IdentityAgentOptions) { - this.agentDid = options.agentDid; - this.appData = options.appData; - this.didManager = options.didManager; - this.didResolver = options.didResolver; - this.dwnManager = options.dwnManager; - this.identityManager = options.identityManager; - this.keyManager = options.keyManager; - this.rpcClient = options.rpcClient; - this.syncManager = options.syncManager; +export class Web5IdentityAgent implements Web5PlatformAgent { + public crypto: AgentCryptoApi; + public did: AgentDidApi; + public dwn: AgentDwnApi; + public identity: AgentIdentityApi; + public keyManager: TKeyManager; + public rpc: Web5Rpc; + public sync: AgentSyncApi; + public vault: HdIdentityVault; + + private _agentDid?: BearerDid; + + constructor(params: AgentParams) { + this._agentDid = params.agentDid; + this.crypto = params.cryptoApi; + this.did = params.didApi; + this.dwn = params.dwnApi; + this.identity = params.identityApi; + this.keyManager = params.keyManager; + this.rpc = params.rpcClient; + this.sync = params.syncApi; + this.vault = params.agentVault; // Set this agent to be the default agent. - this.didManager.agent = this; - this.dwnManager.agent = this; - this.identityManager.agent = this; + this.did.agent = this; + this.dwn.agent = this; + this.identity.agent = this; this.keyManager.agent = this; - this.syncManager.agent = this; + this.sync.agent = this; } - static async create(options: Partial = {}): Promise { - let { - agentDid, appData, didManager, didResolver, dwnManager, - identityManager, keyManager, rpcClient, syncManager - } = options; - - if (agentDid === undefined) { - // An Agent DID was not specified, so set to empty string. - agentDid = ''; + get agentDid(): BearerDid { + if (this._agentDid === undefined) { + throw new Error( + 'Web5UserAgent: The "agentDid" property is not set. Ensure the agent is properly ' + + 'initialized and a DID is assigned.' + ); } + return this._agentDid; + } - if (appData === undefined) { - // A custom AppDataStore implementation was not specified, so - // instantiate a LevelDB backed secure AppDataVault. - appData = new AppDataVault({ - store: new LevelStore({ location: 'data/agent/vault' }) - }); - } + set agentDid(did: BearerDid) { + this._agentDid = did; + } - if (didManager === undefined) { - // A custom DidManager implementation was not specified, so - // instantiate a default with in-memory store. - didManager = new DidManager({ - didMethods : [DidIonMethod, DidKeyMethod], - store : new DidStoreDwn() - }); - } + /** + * If any of the required agent components are not provided, instantiate default implementations. + */ + public static async create({ + dataPath = 'DATA/AGENT', + agentDid, agentVault, cryptoApi, didApi, dwnApi, identityApi, keyManager, rpcClient, syncApi + }: Partial = {} + ): Promise { + + agentVault ??= new HdIdentityVault({ + keyDerivationWorkFactor : 210_000, + store : new LevelStore({ location: `${dataPath}/VAULT_STORE` }) + }); - if (didResolver === undefined) { - // A custom DidManager implementation was not specified, so - // instantiate a default with in-memory store. - didResolver = new DidResolver({ didResolvers: [DidIonMethod, DidKeyMethod] }); - } + cryptoApi ??= new AgentCryptoApi(); - if (dwnManager === undefined) { - // A custom DwnManager implementation was not specified, so - // instantiate a default. - dwnManager = await DwnManager.create({ didResolver }); - } + didApi ??= new AgentDidApi({ + didMethods : [DidDht, DidJwk], + resolverCache : new DidResolverCacheLevel({ location: `${dataPath}/DID_RESOLVERCACHE` }), + store : new DwnDidStore() + }); - if (identityManager === undefined) { - // A custom IdentityManager implementation was not specified, so - // instantiate a default that uses a DWN store. - identityManager = new IdentityManager({ - store: new IdentityStoreDwn() - }); - } + dwnApi ??= new AgentDwnApi({ + dwn: await AgentDwnApi.createDwn({ dataPath, didResolver: didApi }) + }); - if (keyManager === undefined) { - // A custom KeyManager implementation was not specified, so - // instantiate a default with KMSs. - const localKmsDwn = new LocalKms({ - kmsName : 'local', - keyStore : new KeyStoreDwn({ schema: 'https://identity.foundation/schemas/web5/kms-key' }), - privateKeyStore : new PrivateKeyStoreDwn() - }); - const localKmsMemory = new LocalKms({ - kmsName: 'memory' - }); - keyManager = new KeyManager({ - kms: { - local : localKmsDwn, - memory : localKmsMemory - }, - store: new KeyStoreDwn({ schema: 'https://identity.foundation/schemas/web5/managed-key' }) - }); - } + identityApi ??= new AgentIdentityApi({ store: new DwnIdentityStore() }); - if (rpcClient === undefined) { - // A custom RPC Client implementation was not specified, so - // instantiate a default. - rpcClient = new Web5RpcClient(); - } + keyManager ??= new LocalKeyManager({ keyStore: new DwnKeyStore() }); - if (syncManager === undefined) { - // A custom SyncManager implementation was not specified, so - // instantiate a LevelDB-backed default. - syncManager = new SyncManagerLevel(); - } + rpcClient ??= new Web5RpcClient(); - // Instantiate the Identity Agent. - const agent = new IdentityAgent({ + syncApi ??= new AgentSyncApi({ syncEngine: new SyncEngineLevel({ dataPath }) }); + + // Instantiate the Agent using the provided or default components. + return new Web5IdentityAgent({ agentDid, - appData, - didManager, - didResolver, - dwnManager, - identityManager, + agentVault, + cryptoApi, + didApi, + dwnApi, keyManager, + identityApi, rpcClient, - syncManager + syncApi }); - - return agent; } - async firstLaunch(): Promise { - // Check whether data vault is already initialized. - const { initialized } = await this.appData.getStatus(); - return initialized === false; + public async firstLaunch(): Promise { + // Check whether data vault is already initialize + return await this.vault.isInitialized() === false; } /** - * Executed once the first time the Identity Agent is launched. - * The passphrase should be input by the end-user. + * Initializes the User Agent with a password, and optionally a recovery phrase. + * + * This method is typically called once, the first time the Agent is launched, and is responsible + * for setting up the agent's operational environment, cryptographic key material, and readiness + * for processing Web5 requests. + * + * The password is used to secure the Agent vault, and the recovery phrase is used to derive the + * cryptographic keys for the vault. If a recovery phrase is not provided, a new recovery phrase + * will be generated and returned. The password should be chosen and entered by the end-user. */ - async initialize(options: { passphrase: string }) { - const { passphrase } = options; - - // Generate an Ed25519 key pair for the Identity Agent. - const agentKeyPair = await new EdDsaAlgorithm().generateKey({ - algorithm : { name: 'EdDSA', namedCurve: 'Ed25519' }, - extractable : true, - keyUsages : ['sign', 'verify'] - }); + public async initialize({ password, recoveryPhrase }: AgentInitializeParams): Promise { + // Initialize the Agent vault. + recoveryPhrase = await this.vault.initialize({ password, recoveryPhrase }); - /** Initialize the AppDataStore with the Identity Agent's - * private key and passphrase, which also unlocks the data vault. */ - await this.appData.initialize({ - passphrase : passphrase, - keyPair : agentKeyPair, - }); + return recoveryPhrase; } - async processDidRequest(request: DidRequest): Promise { - switch (request.messageType) { - case DidMessage.Resolve: { - const { didUrl, resolutionOptions } = request.messageOptions; - const result = await this.didResolver.resolve(didUrl, resolutionOptions); - return { result }; - } - - default: { - return this.didManager.processRequest(request); - } - } + async processDidRequest( + request: DidRequest + ): Promise> { + return this.did.processRequest(request); } - async processDwnRequest(request: ProcessDwnRequest): Promise { - return this.dwnManager.processRequest(request); + public async processDwnRequest( + request: ProcessDwnRequest + ): Promise> { + return this.dwn.processRequest(request); } - async processVcRequest(_request: ProcessVcRequest): Promise { + public async processVcRequest(_request: ProcessVcRequest): Promise { throw new Error('Not implemented'); } - async sendDidRequest(_request: DidRequest): Promise { + public async sendDidRequest( + _request: DidRequest + ): Promise> { throw new Error('Not implemented'); } - async sendDwnRequest(request: SendDwnRequest): Promise { - return this.dwnManager.sendRequest(request); + public async sendDwnRequest( + request: SendDwnRequest + ): Promise> { + return this.dwn.sendRequest(request); } - async sendVcRequest(_request: SendVcRequest): Promise { + public async sendVcRequest(_request: SendVcRequest): Promise { throw new Error('Not implemented'); } - async start(options: { passphrase: string }) { - const { passphrase } = options; - - if (await this.firstLaunch()) { - // 1A. Agent's first launch so initialize. - await this.initialize({ passphrase }); - } else { - // 1B. Agent was previously initialized. - // Unlock the data vault and cache the vault unlock key (VUK) in memory. - await this.appData.unlock({ passphrase }); + public async start({ password }: AgentInitializeParams): Promise { + // If the Agent vault is locked, unlock it. + if (this.vault.isLocked()) { + await this.vault.unlock({ password }); } - // 2. Set the Identity Agent's root did:key identifier. - this.agentDid = await this.appData.getDid(); - - // 3. Import the Identity Agent's signing key pair to KeyManager. - const defaultSigningKey = cryptoToPortableKeyPair({ - cryptoKeyPair: { - privateKey : await this.appData.getPrivateKey(), - publicKey : await this.appData.getPublicKey() - }, - keyData: { - alias : await this.didManager.getDefaultSigningKey({ did: this.agentDid }), - kms : 'memory' - } - }); - await this.keyManager.setDefaultSigningKey({ key: defaultSigningKey }); + // Set the Agent's DID. + this.agentDid = await this.vault.getDid(); } } \ No newline at end of file diff --git a/packages/identity-agent/tests/identity-agent.spec.ts b/packages/identity-agent/tests/identity-agent.spec.ts index 4debdcf52..b71aa7739 100644 --- a/packages/identity-agent/tests/identity-agent.spec.ts +++ b/packages/identity-agent/tests/identity-agent.spec.ts @@ -1,11 +1,12 @@ -import * as sinon from 'sinon'; -import chai, { expect } from 'chai'; -import chaiAsPromised from 'chai-as-promised'; -import { TestManagedAgent } from '@web5/agent'; +import type { BearerIdentity, PortableIdentity } from '@web5/agent'; -import { IdentityAgent } from '../src/identity-agent.js'; +import { expect } from 'chai'; +import { DidDht } from '@web5/dids'; +import { Convert } from '@web5/common'; +import { DidInterface, DwnInterface, PlatformAgentTestHarness } from '@web5/agent'; -chai.use(chaiAsPromised); +import { testDwnUrl } from './utils/test-config.js'; +import { Web5IdentityAgent } from '../src/identity-agent.js'; // NOTE: @noble/secp256k1 requires globalThis.crypto polyfill for node.js <=18: https://github.com/paulmillr/noble-secp256k1/blob/main/README.md#usage // Remove when we move off of node.js v18 to v20, earliest possible time would be Oct 2023: https://github.com/nodejs/release#release-schedule @@ -15,163 +16,415 @@ import { webcrypto } from 'node:crypto'; // @ts-ignore if (!globalThis.crypto) globalThis.crypto = webcrypto; -describe('IdentityAgent', () => { +let testDwnUrls: string[] = [testDwnUrl]; + +describe('Web5IdentityAgent', () => { + + describe('agentDid', () => { + it('throws an error if accessed before the Agent is initialized', async () => { + // @ts-expect-error - Initializing with empty object to test error. + const identityAgent = new Web5IdentityAgent({ didApi: {}, dwnApi: {}, identityApi: {}, keyManager: {}, syncApi: {} }); + try { + identityAgent.agentDid; + throw new Error('Expected an error'); + } catch (error: any) { + expect(error.message).to.include('"agentDid" property is not set'); + } + }); + }); + + describe('create()', () => { + it('should create an instance with default parameters when none are provided', async () => { + const identityAgent = await Web5IdentityAgent.create({ dataPath: '__TESTDATA__/USERAGENT' }); + + expect(identityAgent).to.be.an.instanceof(Web5IdentityAgent); + expect(identityAgent.crypto).to.exist; + expect(identityAgent.did).to.exist; + expect(identityAgent.dwn).to.exist; + expect(identityAgent.identity).to.exist; + expect(identityAgent.keyManager).to.exist; + expect(identityAgent.rpc).to.exist; + expect(identityAgent.sync).to.exist; + expect(identityAgent.vault).to.exist; + }); + }); const agentStoreTypes = ['dwn', 'memory'] as const; agentStoreTypes.forEach((agentStoreType) => { describe(`with ${agentStoreType} data stores`, () => { - let testAgent: TestManagedAgent; + let testHarness: PlatformAgentTestHarness; before(async () => { - testAgent = await TestManagedAgent.create({ - agentClass : IdentityAgent, + testHarness = await PlatformAgentTestHarness.setup({ + agentClass : Web5IdentityAgent, agentStores : agentStoreType }); }); beforeEach(async () => { - await testAgent.clearStorage(); + await testHarness.clearStorage(); + await testHarness.createAgentDid(); }); after(async () => { - await testAgent.clearStorage(); - await testAgent.closeStorage(); + await testHarness.clearStorage(); + await testHarness.closeStorage(); }); describe('firstLaunch()', () => { it('returns true the first time the Identity Agent runs', async () => { - await expect(testAgent.agent.firstLaunch()).to.eventually.be.true; + const result = await testHarness.agent.firstLaunch(); + expect(result).to.be.true; }); it('returns false after Identity Agent initialization', async () => { - await expect(testAgent.agent.firstLaunch()).to.eventually.be.true; + let result = await testHarness.agent.firstLaunch(); + expect(result).to.be.true; - await testAgent.agent.start({ passphrase: 'test' }); - await expect(testAgent.agent.firstLaunch()).to.eventually.be.false; + await testHarness.agent.initialize({ password: 'test' }); + + result = await testHarness.agent.firstLaunch(); + expect(result).to.be.false; }); }); describe('initialize()', () => { - it('initializes the AppData store and stores the vault key set', async () => { - await testAgent.agent.initialize({ passphrase: 'test' }); + it('generates and returns a 12-word mnenomic if one is not provided', async () => { + // Initialize the vault. + const generatedRecoveryPhrase = await testHarness.agent.initialize({ + password: 'dumbbell-krakatoa-ditty' + }); + + // Verify that the vault is initialized and is unlocked. + expect(generatedRecoveryPhrase).to.be.a('string'); + if (typeof generatedRecoveryPhrase !== 'string') throw new Error('type guard'); + expect(generatedRecoveryPhrase.split(' ')).to.have.lengthOf(12); + }); + + it('accepts a recovery phrase', async () => { + const predefinedRecoveryPhrase = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about'; - // Confirm the AppData store was initialized. - const { initialized } = await testAgent.agent.appData.getStatus(); - expect(initialized).to.be.true; + // Initialize the vault with a recovery phrase. + const returnedRecoveryPhrase = await testHarness.agent.initialize({ + password : 'dumbbell-krakatoa-ditty', + recoveryPhrase : predefinedRecoveryPhrase + }); - // Confirm the vault key set was stored. - const storedVaultKeySet = await testAgent.appDataStore.get('vaultKeySet'); - expect(storedVaultKeySet).to.exist; - expect(storedVaultKeySet).to.be.a.string; + // Verify that the vault is initialized and is unlocked. + expect(returnedRecoveryPhrase).to.equal(predefinedRecoveryPhrase); }); }); - describe('start()', () => { - it('initializes the AppData store the first time the Identity Agent runs', async () => { - // const agent = await IdentityAgent.create({ appData, dwnManager }); - let initializeSpy = sinon.spy(testAgent.agent, 'initialize'); - let unlockSpy = sinon.spy(testAgent.agent.appData, 'unlock'); - - // Execute agent.start() for the first time. - await testAgent.agent.start({ passphrase: 'test' }); - - // Confirm agent.initialize() was called. - expect(initializeSpy.called).to.be.true; - - // Confirm agent.appData.unlock() was not called. - expect(unlockSpy.called).to.be.false; - - // Confirm the AppData store was initialized. - const { initialized } = await testAgent.agent.appData.getStatus(); - expect(initialized).to.be.true; - - // Confirm the vault key set was stored. - const storedVaultKeySet = await testAgent.appDataStore.get('vaultKeySet'); - expect(storedVaultKeySet).to.exist; - expect(storedVaultKeySet).to.be.a.string; - - // Confirm the AppData store was unlocked. - // @ts-expect-error because a private variable is being intentionally accessed. - const vaultUnlockKey = testAgent.agent.appData._vaultUnlockKey; - expect(vaultUnlockKey).to.exist; - expect(vaultUnlockKey).to.be.a('Uint8Array'); - - // Confirm the Agent's key pair was stored in KeyManager. - if (testAgent.agent.agentDid === undefined) throw new Error(); // Type guard. - const signingKeyId = await testAgent.agent.didManager.getDefaultSigningKey({ did: testAgent.agent.agentDid }); - if (!signingKeyId) throw new Error('Type guard'); - const agentKeyPair = await testAgent.agent.keyManager.getKey({ keyRef: signingKeyId }); - expect(agentKeyPair).to.exist; - expect(agentKeyPair).to.have.property('privateKey'); - expect(agentKeyPair).to.have.property('publicKey'); - - initializeSpy.restore(); - unlockSpy.restore(); + describe('processDidRequest()', () => { + it('processes a DID Create request', async () => { + const didCreateResponse = await testHarness.agent.processDidRequest({ + messageType : DidInterface.Create, + messageParams : { method: 'jwk' } + }); + + expect(didCreateResponse).to.exist; + expect(didCreateResponse).to.have.property('ok', true); + expect(didCreateResponse).to.have.property('status'); + expect(didCreateResponse.status).to.have.property('code', 201); + expect(didCreateResponse.status).to.have.property('message', 'Created'); + expect(didCreateResponse).to.have.property('result'); + expect(didCreateResponse.result).to.have.property('uri'); + expect(didCreateResponse.result).to.have.property('document'); + expect(didCreateResponse.result).to.have.property('metadata'); }); - it('unlocks the AppData store on subsequent Identity Agent runs', async () => { - // Execute agent.start() for the first time. - await testAgent.agent.start({ passphrase: 'test' }); - - let initializeSpy = sinon.spy(testAgent.agent, 'initialize'); - let unlockSpy = sinon.spy(testAgent.agent.appData, 'unlock'); - - // Execute agent.start() for the second time. - await testAgent.agent.start({ passphrase: 'test' }); - - // Confirm agent.initialize() was not called. - expect(initializeSpy.called).to.be.false; - - // Confirm agent.appData.unlock() was called. - expect(unlockSpy.called).to.be.true; - - // Confirm the vault key set was stored. - const storedVaultKeySet = await testAgent.appDataStore.get('vaultKeySet'); - expect(storedVaultKeySet).to.exist; - expect(storedVaultKeySet).to.be.a.string; - - // Confirm the AppData store was unlocked. - // @ts-expect-error because a private variable is being intentionally accessed. - const vaultUnlockKey = testAgent.agent.appData._vaultUnlockKey; - expect(vaultUnlockKey).to.exist; - expect(vaultUnlockKey).to.be.a('Uint8Array'); + it('processes a DID Resolve request', async () => { + const didResolveResponse = await testHarness.agent.processDidRequest({ + messageType : DidInterface.Resolve, + messageParams : { didUri: testHarness.agent.agentDid.uri } + }); + + expect(didResolveResponse).to.exist; + expect(didResolveResponse).to.have.property('ok', true); + expect(didResolveResponse).to.have.property('status'); + expect(didResolveResponse.status).to.have.property('code', 200); + expect(didResolveResponse.status).to.have.property('message', 'OK'); + expect(didResolveResponse).to.have.property('result'); + expect(didResolveResponse.result).to.have.property('didDocument'); + expect(didResolveResponse.result).to.have.property('didDocumentMetadata'); + expect(didResolveResponse.result).to.have.property('didResolutionMetadata'); + }); + }); - // Confirm the Agent's key pair was stored in KeyManager. - if (testAgent.agent.agentDid === undefined) throw new Error(); // Type guard. - const signingKeyId = await testAgent.agent.didManager.getDefaultSigningKey({ did: testAgent.agent.agentDid }); - if (!signingKeyId) throw new Error('Type guard'); - const agentKeyPair = await testAgent.agent.keyManager.getKey({ keyRef: signingKeyId }); - expect(agentKeyPair).to.exist; - expect(agentKeyPair).to.have.property('privateKey'); - expect(agentKeyPair).to.have.property('publicKey'); + if (agentStoreType === 'dwn') { + let alice: BearerIdentity; - initializeSpy.restore(); - unlockSpy.restore(); + beforeEach(async () => { + alice = await testHarness.agent.identity.create({ + metadata : { name: 'Alice' }, + didMethod : 'jwk' + }); }); - it('unlocks the AppData store', async () => { - // const agent = await IdentityAgent.create({ appData, dwnManager }); - let initializeSpy = sinon.spy(testAgent.agent, 'initialize'); - let unlockSpy = sinon.spy(testAgent.agent.appData, 'unlock'); + describe('processDwnRequest()', () => { + it('processes a Records Write request', async () => { + // Create test data to write. + const dataBytes = Convert.string('Hello, world!').toUint8Array(); + + // Attempt to process the RecordsWrite + let writeResponse = await testHarness.agent.processDwnRequest({ + author : alice.did.uri, + target : alice.did.uri, + messageType : DwnInterface.RecordsWrite, + messageParams : { + dataFormat: 'text/plain' + }, + dataStream: new Blob([dataBytes]) + }); + + // Verify the response. + expect(writeResponse).to.have.property('message'); + expect(writeResponse).to.have.property('messageCid'); + expect(writeResponse).to.have.property('reply'); + + const writeMessage = writeResponse.message; + expect(writeMessage).to.have.property('authorization'); + expect(writeMessage).to.have.property('descriptor'); + expect(writeMessage).to.have.property('recordId'); + + const writeReply = writeResponse.reply; + expect(writeReply).to.have.property('status'); + expect(writeReply.status.code).to.equal(202); + }); + }); - await testAgent.agent.start({ passphrase: 'test' }); + describe('processVcRequest()', () => { + it('throws an error', async () => { + try { + await testHarness.agent.processVcRequest({}); + throw new Error('Expected an error'); + } catch (error) { + expect(error).to.have.property('message', 'Not implemented'); + } + }); + }); - // Confirm agent.initialize() was called. - expect(initializeSpy.called).to.be.true; + describe('sendDidRequest()', () => { + it('throws an error', async () => { + try { + await testHarness.agent.sendDidRequest({ + messageType : DidInterface.Create, + messageParams : { method: 'jwk' } + }); + throw new Error('Expected an error'); + } catch (error) { + expect(error).to.have.property('message', 'Not implemented'); + } + }); + }); - // Confirm agent.appData.unlock() was not called. - expect(unlockSpy.called).to.be.false; + describe('sendDwnRequest()', () => { + beforeEach(async () => { + const testPortableIdentity: PortableIdentity = { + portableDid: { + uri : 'did:dht:ugkhixpk56o9izfp4ucc543scj5ajcis3rkh43yueq98qiaj8tgy', + document : { + id : 'did:dht:ugkhixpk56o9izfp4ucc543scj5ajcis3rkh43yueq98qiaj8tgy', + verificationMethod : [ + { + id : 'did:dht:ugkhixpk56o9izfp4ucc543scj5ajcis3rkh43yueq98qiaj8tgy#0', + type : 'JsonWebKey', + controller : 'did:dht:ugkhixpk56o9izfp4ucc543scj5ajcis3rkh43yueq98qiaj8tgy', + publicKeyJwk : { + crv : 'Ed25519', + kty : 'OKP', + x : 'mZXKvarfofrcrdTYzes2YneEsrbJFc1kE0O-d1cJPEw', + kid : 'EAlW6h08kqdLGEhR_o6hCnZpYpQ8QJavMp3g0BJ35IY', + alg : 'EdDSA', + }, + }, + { + id : 'did:dht:ugkhixpk56o9izfp4ucc543scj5ajcis3rkh43yueq98qiaj8tgy#sig', + type : 'JsonWebKey', + controller : 'did:dht:ugkhixpk56o9izfp4ucc543scj5ajcis3rkh43yueq98qiaj8tgy', + publicKeyJwk : { + crv : 'Ed25519', + kty : 'OKP', + x : 'iIWijzQnfb_Jk4yRjISV6ci8EtyHn0fIxg0TVCh7wkE', + kid : '8QSlw4ct9taIgh23EUGLM0ELaukQ1VogIuBGrQ_UIsk', + alg : 'EdDSA', + }, + }, + { + id : 'did:dht:ugkhixpk56o9izfp4ucc543scj5ajcis3rkh43yueq98qiaj8tgy#enc', + type : 'JsonWebKey', + controller : 'did:dht:ugkhixpk56o9izfp4ucc543scj5ajcis3rkh43yueq98qiaj8tgy', + publicKeyJwk : { + kty : 'EC', + crv : 'secp256k1', + x : 'P5FoqXk9W11i8FWyTpIvltAjV09FL9Q5o76wEHcxMtI', + y : 'DgoLVlLKbjlaUja4RTjdxzqAy0ITOEFlCXGKSpu8XQs', + kid : 'hXXhIgfXRVIYqnKiX0DIL7ZGy0CBJrFQFIYxmRkAB-A', + alg : 'ES256K', + }, + }, + ], + authentication: [ + 'did:dht:ugkhixpk56o9izfp4ucc543scj5ajcis3rkh43yueq98qiaj8tgy#0', + 'did:dht:ugkhixpk56o9izfp4ucc543scj5ajcis3rkh43yueq98qiaj8tgy#sig', + ], + assertionMethod: [ + 'did:dht:ugkhixpk56o9izfp4ucc543scj5ajcis3rkh43yueq98qiaj8tgy#0', + 'did:dht:ugkhixpk56o9izfp4ucc543scj5ajcis3rkh43yueq98qiaj8tgy#sig', + ], + capabilityDelegation: [ + 'did:dht:ugkhixpk56o9izfp4ucc543scj5ajcis3rkh43yueq98qiaj8tgy#0', + ], + capabilityInvocation: [ + 'did:dht:ugkhixpk56o9izfp4ucc543scj5ajcis3rkh43yueq98qiaj8tgy#0', + ], + keyAgreement: [ + 'did:dht:ugkhixpk56o9izfp4ucc543scj5ajcis3rkh43yueq98qiaj8tgy#enc', + ], + service: [ + { + id : 'did:dht:ugkhixpk56o9izfp4ucc543scj5ajcis3rkh43yueq98qiaj8tgy#dwn', + type : 'DecentralizedWebNode', + serviceEndpoint : testDwnUrls, + enc : '#enc', + sig : '#sig', + }, + ], + }, + metadata: { + published : true, + versionId : '1708160454', + }, + privateKeys: [ + { + crv : 'Ed25519', + d : 'gXu7HmJgvZFWgNf_eqF-eDAFegd0OLe8elAIXXGMgoc', + kty : 'OKP', + x : 'mZXKvarfofrcrdTYzes2YneEsrbJFc1kE0O-d1cJPEw', + kid : 'EAlW6h08kqdLGEhR_o6hCnZpYpQ8QJavMp3g0BJ35IY', + alg : 'EdDSA', + }, + { + crv : 'Ed25519', + d : 'SiUL1QDp6X2QnvJ1Q7hRlpo3ZhiVjRlvINocOzYPaBU', + kty : 'OKP', + x : 'iIWijzQnfb_Jk4yRjISV6ci8EtyHn0fIxg0TVCh7wkE', + kid : '8QSlw4ct9taIgh23EUGLM0ELaukQ1VogIuBGrQ_UIsk', + alg : 'EdDSA', + }, + { + kty : 'EC', + crv : 'secp256k1', + d : 'b2gb-OfB5X4G3xd16u19MXNkamDP5lsT6bVsDN4aeuY', + x : 'P5FoqXk9W11i8FWyTpIvltAjV09FL9Q5o76wEHcxMtI', + y : 'DgoLVlLKbjlaUja4RTjdxzqAy0ITOEFlCXGKSpu8XQs', + kid : 'hXXhIgfXRVIYqnKiX0DIL7ZGy0CBJrFQFIYxmRkAB-A', + alg : 'ES256K', + }, + ], + }, + metadata: { + name : 'Alice', + tenant : 'did:dht:ugkhixpk56o9izfp4ucc543scj5ajcis3rkh43yueq98qiaj8tgy', + uri : 'did:dht:ugkhixpk56o9izfp4ucc543scj5ajcis3rkh43yueq98qiaj8tgy' + } + }; + + await testHarness.preloadResolverCache({ + didUri : testPortableIdentity.portableDid.uri, + resolutionResult : { + didDocument : testPortableIdentity.portableDid.document, + didDocumentMetadata : testPortableIdentity.portableDid.metadata, + didResolutionMetadata : {} + } + }); + + alice = await testHarness.agent.identity.import({ + portableIdentity: testPortableIdentity + }); + + // Ensure the DID is published to the DHT. This step is necessary while the DHT Gateways + // operated by TBD are regularly restarted and DIDs are no longer persisted. + await DidDht.publish({ did: alice.did }); + }); + + it('processes a Records Write request', async () => { + // Create test data to write. + const dataBytes = Convert.string('Hello, world!').toUint8Array(); + + // Attempt to process the RecordsWrite + let writeResponse = await testHarness.agent.sendDwnRequest({ + author : alice.did.uri, + target : alice.did.uri, + messageType : DwnInterface.RecordsWrite, + messageParams : { + dataFormat: 'text/plain' + }, + dataStream: new Blob([dataBytes]) + }); + + // Verify the response. + expect(writeResponse).to.have.property('message'); + expect(writeResponse).to.have.property('messageCid'); + expect(writeResponse).to.have.property('reply'); + + const writeMessage = writeResponse.message; + expect(writeMessage).to.have.property('authorization'); + expect(writeMessage).to.have.property('descriptor'); + expect(writeMessage).to.have.property('recordId'); + + const writeReply = writeResponse.reply; + expect(writeReply).to.have.property('status'); + expect(writeReply.status.code).to.equal(202); + }); + }); - // @ts-expect-error because a private variable is being intentionally accessed. - const vaultUnlockKey = testAgent.agent.appData._vaultUnlockKey; - expect(vaultUnlockKey).to.exist; - expect(vaultUnlockKey).to.be.a('Uint8Array'); + describe('sendVcRequest()', () => { + it('throws an error', async () => { + try { + await testHarness.agent.sendVcRequest({}); + throw new Error('Expected an error'); + } catch (error) { + expect(error).to.have.property('message', 'Not implemented'); + } + }); + }); - initializeSpy.restore(); - unlockSpy.restore(); + describe('subsequent launches', () => { + it('can access stored identifiers after second launch', async () => { + // First launch and initialization. + await testHarness.agent.initialize({ password: 'test' }); + + // Start the Agent, which will decrypt and load the Agent's DID from the vault. + await testHarness.agent.start({ password: 'test' }); + + // Create and persist a new Identity (with DID and Keys). + const socialIdentity = await testHarness.agent.identity.create({ + metadata : { name: 'Social' }, + didMethod : 'jwk' + }); + + // Simulate terminating and restarting an app. + await testHarness.closeStorage(); + testHarness = await PlatformAgentTestHarness.setup({ + agentClass : Web5IdentityAgent, + agentStores : 'dwn' + }); + await testHarness.agent.start({ password: 'test' }); + + // Try to get the identity and verify it exists. + const storedIdentity = await testHarness.agent.identity.get({ + didUri : socialIdentity.did.uri, + tenant : socialIdentity.did.uri + }); + + expect(storedIdentity).to.exist; + expect(storedIdentity!.did).to.have.property('uri', socialIdentity.did.uri); + }); }); - }); + } }); }); + }); \ No newline at end of file diff --git a/packages/identity-agent/tests/managing-identities.spec.ts b/packages/identity-agent/tests/managing-identities.spec.ts index 92258ceca..616fbe5ad 100644 --- a/packages/identity-agent/tests/managing-identities.spec.ts +++ b/packages/identity-agent/tests/managing-identities.spec.ts @@ -1,14 +1,8 @@ -import { Web5 } from '@web5/api'; -import chai, { expect } from 'chai'; -import chaiAsPromised from 'chai-as-promised'; -import { TestManagedAgent } from '@web5/agent'; +import { expect } from 'chai'; +import { PlatformAgentTestHarness } from '@web5/agent'; -import { IdentityAgent } from '../src/identity-agent.js'; +import { Web5IdentityAgent } from '../src/identity-agent.js'; -chai.use(chaiAsPromised); - -// NOTE: @noble/secp256k1 requires globalThis.crypto polyfill for node.js <=18: https://github.com/paulmillr/noble-secp256k1/blob/main/README.md#usage -// Remove when we move off of node.js v18 to v20, earliest possible time would be Oct 2023: https://github.com/nodejs/release#release-schedule // NOTE: @noble/secp256k1 requires globalThis.crypto polyfill for node.js <=18: https://github.com/paulmillr/noble-secp256k1/blob/main/README.md#usage // Remove when we move off of node.js v18 to v20, earliest possible time would be Oct 2023: https://github.com/nodejs/release#release-schedule import { webcrypto } from 'node:crypto'; @@ -21,201 +15,123 @@ describe('Managing Identities', () => { agentStoreTypes.forEach((agentStoreType) => { describe(`with ${agentStoreType} data stores`, () => { - let testAgent: TestManagedAgent; + let testHarness: PlatformAgentTestHarness; before(async () => { - testAgent = await TestManagedAgent.create({ - agentClass : IdentityAgent, + testHarness = await PlatformAgentTestHarness.setup({ + agentClass : Web5IdentityAgent, agentStores : agentStoreType }); }); beforeEach(async () => { - await testAgent.clearStorage(); + await testHarness.clearStorage(); + await testHarness.createAgentDid(); }); after(async () => { - await testAgent.clearStorage(); - await testAgent.closeStorage(); + await testHarness.clearStorage(); + await testHarness.closeStorage(); }); describe('initial identity creation', () => { it('can create three identities', async () => { - // Start agent for the first time. - await testAgent.agent.start({ passphrase: 'test' }); + // First launch and initialization. + await testHarness.agent.initialize({ password: 'test' }); + + // Start the Agent, which will decrypt and load the Agent's DID from the vault. + await testHarness.agent.start({ password: 'test' }); // Create three identities, each of which is stored in a new tenant. - const careerIdentity = await testAgent.agent.identityManager.create({ - name : 'Social', - didMethod : 'key', - kms : 'local' + const careerIdentity = await testHarness.agent.identity.create({ + metadata : { name: 'Social' }, + didMethod : 'jwk' }); - const familyIdentity = await testAgent.agent.identityManager.create({ - name : 'Social', - didMethod : 'key', - kms : 'local' + const familyIdentity = await testHarness.agent.identity.create({ + metadata : { name: 'Social' }, + didMethod : 'jwk' }); - const socialIdentity = await testAgent.agent.identityManager.create({ - name : 'Social', - didMethod : 'key', - kms : 'local' + const socialIdentity = await testHarness.agent.identity.create({ + metadata : { name: 'Social' }, + didMethod : 'jwk' }); // Verify the Identities were stored in each new Identity's tenant. - const storedCareerIdentity = await testAgent.agent.identityManager.get({ did: careerIdentity.did, context: careerIdentity.did }); - const storedFamilyIdentity = await testAgent.agent.identityManager.get({ did: familyIdentity.did, context: familyIdentity.did }); - const storedSocialIdentity = await testAgent.agent.identityManager.get({ did: socialIdentity.did, context: socialIdentity.did }); - expect(storedCareerIdentity).to.have.property('did', careerIdentity.did); - expect(storedFamilyIdentity).to.have.property('did', familyIdentity.did); - expect(storedSocialIdentity).to.have.property('did', socialIdentity.did); + const storedCareerIdentity = await testHarness.agent.identity.get({ didUri: careerIdentity.did.uri, tenant: careerIdentity.did.uri }); + const storedFamilyIdentity = await testHarness.agent.identity.get({ didUri: familyIdentity.did.uri, tenant: familyIdentity.did.uri }); + const storedSocialIdentity = await testHarness.agent.identity.get({ didUri: socialIdentity.did.uri, tenant: socialIdentity.did.uri }); + expect(storedCareerIdentity!.did).to.have.property('uri', careerIdentity.did.uri); + expect(storedFamilyIdentity!.did).to.have.property('uri', familyIdentity.did.uri); + expect(storedSocialIdentity!.did).to.have.property('uri', socialIdentity.did.uri); }).timeout(30000); // Tests that should only run for DWN-backed stores that provide multi-tenancy. if (agentStoreType === 'dwn') { it('supports tenant isolation between Identity Agent and Identities under management', async () => { - // Start agent for the first time. - await testAgent.agent.start({ passphrase: 'test' }); + // First launch and initialization. + await testHarness.agent.initialize({ password: 'test' }); + + // Start the Agent, which will decrypt and load the Agent's DID from the vault. + await testHarness.agent.start({ password: 'test' }); // Create three identities, each of which is stored in a new tenant. - const careerIdentity = await testAgent.agent.identityManager.create({ - name : 'Career', - didMethod : 'key', - kms : 'local' + const careerIdentity = await testHarness.agent.identity.create({ + metadata : { name: 'Social' }, + didMethod : 'jwk' }); - const familyIdentity = await testAgent.agent.identityManager.create({ - name : 'Family', - didMethod : 'key', - kms : 'local' + const familyIdentity = await testHarness.agent.identity.create({ + metadata : { name: 'Social' }, + didMethod : 'jwk' }); - const socialIdentity = await testAgent.agent.identityManager.create({ - name : 'Social', - didMethod : 'key', - kms : 'local' + const socialIdentity = await testHarness.agent.identity.create({ + metadata : { name: 'Social' }, + didMethod : 'jwk' }); - // Import just the Identity metadata for the new identities to the Identity Agent's tenant. - await testAgent.agent.identityManager.import({ identity: careerIdentity, context: testAgent.agent.agentDid }); - await testAgent.agent.identityManager.import({ identity: familyIdentity, context: testAgent.agent.agentDid }); - await testAgent.agent.identityManager.import({ identity: socialIdentity, context: testAgent.agent.agentDid }); - - // Verify the Identities were stored in each new Identity's tenant. - const storedCareerIdentity = await testAgent.agent.identityManager.get({ did: careerIdentity.did, context: careerIdentity.did }); - const storedFamilyIdentity = await testAgent.agent.identityManager.get({ did: familyIdentity.did, context: familyIdentity.did }); - const storedSocialIdentity = await testAgent.agent.identityManager.get({ did: socialIdentity.did, context: socialIdentity.did }); - expect(storedCareerIdentity).to.have.property('did', careerIdentity.did); - expect(storedFamilyIdentity).to.have.property('did', familyIdentity.did); - expect(storedSocialIdentity).to.have.property('did', socialIdentity.did); + // Manage the newly created identities with the Identity Agent. + await testHarness.agent.identity.manage({ portableIdentity: await careerIdentity.export() }); + await testHarness.agent.identity.manage({ portableIdentity: await familyIdentity.export() }); + await testHarness.agent.identity.manage({ portableIdentity: await socialIdentity.export() }); // Verify the Identities were ALSO stored in the Identity Agent's tenant. - const storedIdentities = await testAgent.agent.identityManager.list(); + const storedIdentities = await testHarness.agent.identity.list(); expect(storedIdentities).to.have.length(3); // Verify the DIDs were only stored in the new Identity's tenant. - let storedCareerDid = await testAgent.agent.didManager.get({ didRef: careerIdentity.did, context: careerIdentity.did }); + let storedCareerDid = await testHarness.agent.did.get({ didUri: careerIdentity.did.uri, tenant: careerIdentity.did.uri }); expect(storedCareerDid).to.exist; - storedCareerDid = await testAgent.agent.didManager.get({ didRef: careerIdentity.did }); + storedCareerDid = await testHarness.agent.did.get({ didUri: careerIdentity.did.uri }); expect(storedCareerDid).to.not.exist; - let storedFamilyDid = await testAgent.agent.didManager.get({ didRef: familyIdentity.did, context: familyIdentity.did }); + let storedFamilyDid = await testHarness.agent.did.get({ didUri: familyIdentity.did.uri, tenant: familyIdentity.did.uri }); expect(storedFamilyDid).to.exist; - storedFamilyDid = await testAgent.agent.didManager.get({ didRef: familyIdentity.did }); + storedFamilyDid = await testHarness.agent.did.get({ didUri: familyIdentity.did.uri }); expect(storedFamilyDid).to.not.exist; - let storedSocialDid = await testAgent.agent.didManager.get({ didRef: socialIdentity.did, context: socialIdentity.did }); + let storedSocialDid = await testHarness.agent.did.get({ didUri: socialIdentity.did.uri, tenant: socialIdentity.did.uri }); expect(storedSocialDid).to.exist; - storedSocialDid = await testAgent.agent.didManager.get({ didRef: socialIdentity.did }); + storedSocialDid = await testHarness.agent.did.get({ didUri: socialIdentity.did.uri }); expect(storedSocialDid).to.not.exist; // Verify keys were stored in Identity Agent's DWN. - const careerKey = await testAgent.agent.keyManager.getKey({ - keyRef: await testAgent.agent.didManager.getDefaultSigningKey({ did: careerIdentity.did }) ?? '' // Type guard. - }); - expect(careerKey).to.exist; - const familyKey = await testAgent.agent.keyManager.getKey({ - keyRef: await testAgent.agent.didManager.getDefaultSigningKey({ did: familyIdentity.did }) ?? '' // Type guard. - }); - expect(familyKey).to.exist; - const socialKey = await testAgent.agent.keyManager.getKey({ - keyRef: await testAgent.agent.didManager.getDefaultSigningKey({ did: socialIdentity.did }) ?? '' // Type guard. - }); - expect(socialKey).to.exist; - }).timeout(30000); - } - }); - - describe('Using Web5 API', async () => { - it('should instantiate Web5 API with provided Web5Agent and DID', async () => { - // Start agent for the first time. - await testAgent.agent.start({ passphrase: 'test' }); - - // Create two identities, each of which is stored in a new tenant. - const careerIdentity = await testAgent.agent.identityManager.create({ - name : 'Career', - didMethod : 'key', - kms : 'local' - }); - const socialIdentity = await testAgent.agent.identityManager.create({ - name : 'Social', - didMethod : 'key', - kms : 'local' + const careerVm = await testHarness.agent.did.getSigningMethod({ didUri: careerIdentity.did.uri }); + const careerKeyUri = await testHarness.agent.keyManager.getKeyUri({ key: careerVm!.publicKeyJwk! }); + const careerPublicKey = await testHarness.agent.keyManager.getPublicKey({ keyUri: careerKeyUri }); + expect(careerPublicKey).to.exist; + + const familyVm = await testHarness.agent.did.getSigningMethod({ didUri: familyIdentity.did.uri }); + const familyKeyUri = await testHarness.agent.keyManager.getKeyUri({ key: familyVm!.publicKeyJwk! }); + const familyPublicKey = await testHarness.agent.keyManager.getPublicKey({ keyUri: familyKeyUri }); + expect(familyPublicKey).to.exist; + + const socialVm = await testHarness.agent.did.getSigningMethod({ didUri: socialIdentity.did.uri }); + const socialKeyUri = await testHarness.agent.keyManager.getKeyUri({ key: socialVm!.publicKeyJwk! }); + const socialPublicKey = await testHarness.agent.keyManager.getPublicKey({ keyUri: socialKeyUri }); + expect(socialPublicKey).to.exist; }); - - // Instantiate a Web5 instance with the "Career" Identity, write a record, and verify the result. - const web5Career = new Web5({ agent: testAgent.agent, connectedDid: careerIdentity.did }); - expect(web5Career).to.exist; - - // Instantiate a Web5 instance with the "Social" Identity, write a record, and verify the result. - const web5Social = new Web5({ agent: testAgent.agent, connectedDid: socialIdentity.did }); - expect(web5Social).to.exist; - }).timeout(30000); - - it('Can write records using an Identity under management', async () => { - // Start agent for the first time. - await testAgent.agent.start({ passphrase: 'test' }); - - // Create two identities, each of which is stored in a new tenant. - const careerIdentity = await testAgent.agent.identityManager.create({ - name : 'Social', - didMethod : 'key', - kms : 'local' - }); - const socialIdentity = await testAgent.agent.identityManager.create({ - name : 'Social', - didMethod : 'key', - kms : 'local' - }); - - // Instantiate a Web5 instance with the "Career" Identity, write a record, and verify the result. - const web5Career = new Web5({ agent: testAgent.agent, connectedDid: careerIdentity.did }); - const careerResult = await web5Career.dwn.records.write({ - data : 'Hello, world!', - message : { - schema : 'foo/bar', - dataFormat : 'text/plain' - } - }); - expect(careerResult.status.code).to.equal(202); - expect(careerResult.record).to.exist; - expect(careerResult.record?.author).to.equal(careerIdentity.did); - expect(await careerResult.record?.data.text()).to.equal('Hello, world!'); - - // Instantiate a Web5 instance with the "Social" Identity, write a record, and verify the result. - const web5Social = new Web5({ agent: testAgent.agent, connectedDid: socialIdentity.did }); - const socialResult = await web5Social.dwn.records.write({ - data : 'Hello, everyone!', - message : { - schema : 'foo/bar', - dataFormat : 'text/plain' - } - }); - expect(socialResult.status.code).to.equal(202); - expect(socialResult.record).to.exist; - expect(socialResult.record?.author).to.equal(socialIdentity.did); - expect(await socialResult.record?.data.text()).to.equal('Hello, everyone!'); - }).timeout(30000); - + } }); }); }); diff --git a/packages/identity-agent/tests/utils/test-config.ts b/packages/identity-agent/tests/utils/test-config.ts new file mode 100644 index 000000000..11c53fa07 --- /dev/null +++ b/packages/identity-agent/tests/utils/test-config.ts @@ -0,0 +1,5 @@ +const DEFAULT_TEST_DWN_URL = 'http://localhost:3000'; + +const getTestDwnUrl = () => process.env.TEST_DWN_URL || DEFAULT_TEST_DWN_URL; + +export const testDwnUrl = getTestDwnUrl(); \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ed6594ab7..6df744d06 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -157,6 +157,9 @@ importers: '@playwright/test': specifier: 1.40.1 version: 1.40.1 + '@tbd54566975/dwn-sdk-js': + specifier: 0.2.18 + version: 0.2.18 '@types/chai': specifier: 4.3.6 version: 4.3.6 @@ -670,17 +673,17 @@ importers: packages/identity-agent: dependencies: '@web5/agent': - specifier: 0.2.6 - version: 0.2.6 + specifier: 0.3.0 + version: link:../agent '@web5/common': - specifier: 0.2.3 - version: 0.2.3 + specifier: 0.2.4 + version: link:../common '@web5/crypto': - specifier: 0.2.2 - version: 0.2.2 + specifier: 0.4.0 + version: link:../crypto '@web5/dids': - specifier: 0.2.4 - version: 0.2.4 + specifier: 0.4.1 + version: link:../dids devDependencies: '@playwright/test': specifier: 1.40.1 @@ -715,9 +718,6 @@ importers: '@web/test-runner-playwright': specifier: 0.11.0 version: 0.11.0 - '@web5/api': - specifier: 0.8.4 - version: 0.8.4 c8: specifier: 9.0.0 version: 9.0.0 @@ -1426,15 +1426,6 @@ packages: resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} dev: true - /@decentralized-identity/ion-pow-sdk@1.0.17: - resolution: {integrity: sha512-vk7DTDM8aKDbFyu1ad/qkoRrGL4q+KvNeL/FNZXhkWPaDhVExBN/qGEoRLf1YSfFe+myto3+4RYTPut+riiqnw==} - dependencies: - buffer: 6.0.3 - cross-fetch: 3.1.5 - hash-wasm: 4.9.0 - transitivePeerDependencies: - - encoding - /@decentralized-identity/ion-sdk@1.0.1: resolution: {integrity: sha512-+P+DXcRSFjsEsI5KIqUmVjpzgUT28B2lWpTO+IxiBcfibAN/1Sg20NebGTO/+serz2CnSZf95N2a1OZ6eXypGQ==} dependencies: @@ -1806,20 +1797,12 @@ packages: multiformats: 13.1.0 murmurhash3js-revisited: 3.0.0 - /@noble/ciphers@0.1.4: - resolution: {integrity: sha512-d3ZR8vGSpy3v/nllS+bD/OMN5UZqusWiQqkyj7AwzTnhXFH72pF5oB4Ach6DQ50g5kXxC28LdaYBEpsyv9KOUQ==} - /@noble/ciphers@0.3.0: resolution: {integrity: sha512-ldbrnOjmNRwFdXcTM6uXDcxpMIFrbzAWNnpBPp4oTJTFF0XByGD6vf45WrehZGXRQTRVV+Zm8YP+EgEf+e4cWA==} /@noble/ciphers@0.4.1: resolution: {integrity: sha512-QCOA9cgf3Rc33owG0AYBB9wszz+Ul2kramWN8tXG44Gyciud/tbkEqvxRF/IpqQaBpRBNi9f4jdNxqB2CQCIXg==} - /@noble/curves@1.1.0: - resolution: {integrity: sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==} - dependencies: - '@noble/hashes': 1.3.1 - /@noble/curves@1.3.0: resolution: {integrity: sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==} dependencies: @@ -1828,10 +1811,6 @@ packages: /@noble/ed25519@2.0.0: resolution: {integrity: sha512-/extjhkwFupyopDrt80OMWKdLgP429qLZj+z6sYJz90rF2Iz0gjZh2ArMKPImUl13Kx+0EXI2hN9T/KJV0/Zng==} - /@noble/hashes@1.3.1: - resolution: {integrity: sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==} - engines: {node: '>= 16'} - /@noble/hashes@1.3.3: resolution: {integrity: sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==} engines: {node: '>= 16'} @@ -2539,37 +2518,6 @@ packages: jwt-decode: 3.1.2 dev: true - /@tbd54566975/dwn-sdk-js@0.2.10: - resolution: {integrity: sha512-CoKO8+NciwWNzD4xRoAAgeElqQCXKM4Fc+zEHsUWD0M3E9v67hRWiTHI6AenUfQv1RSEB2H4GHUeUOHuEV72uw==} - engines: {node: '>= 18'} - dependencies: - '@ipld/dag-cbor': 9.0.3 - '@js-temporal/polyfill': 0.4.4 - '@noble/ed25519': 2.0.0 - '@noble/secp256k1': 2.0.0 - abstract-level: 1.0.3 - ajv: 8.12.0 - blockstore-core: 4.2.0 - cross-fetch: 4.0.0 - eciesjs: 0.4.5 - interface-blockstore: 5.2.3 - interface-store: 5.1.2 - ipfs-unixfs-exporter: 13.1.5 - ipfs-unixfs-importer: 15.1.5 - level: 8.0.0 - lodash: 4.17.21 - lru-cache: 9.1.2 - ms: 2.1.3 - multiformats: 11.0.2 - randombytes: 2.1.0 - readable-stream: 4.4.2 - ulidx: 2.1.0 - uuid: 8.3.2 - varint: 6.0.0 - transitivePeerDependencies: - - encoding - - supports-color - /@tbd54566975/dwn-sdk-js@0.2.18: resolution: {integrity: sha512-71K8izqimtsforMBISl4s8VR5El6VtZd6xjs/s7eYsOww3H/wbdVC5KBXeCvGDlfBoTOk62R5eEUm+WEgaOMrw==} engines: {node: '>= 18'} @@ -2602,39 +2550,6 @@ packages: - encoding - supports-color - /@tbd54566975/dwn-sdk-js@0.2.8: - resolution: {integrity: sha512-oiKk+ekAQO94bUkt6yk+xkDY8uCGmNC+rKaYQLhAoTrhYrczeRSuDT04F5/vPBT5K6NfAoRcQsIyBmvgRCUvgA==} - engines: {node: '>= 18'} - dependencies: - '@ipld/dag-cbor': 9.0.3 - '@js-temporal/polyfill': 0.4.4 - '@noble/ed25519': 2.0.0 - '@noble/secp256k1': 2.0.0 - abstract-level: 1.0.3 - ajv: 8.12.0 - blockstore-core: 4.2.0 - cross-fetch: 4.0.0 - eciesjs: 0.4.5 - flat: 5.0.2 - interface-blockstore: 5.2.3 - interface-store: 5.1.2 - ipfs-unixfs-exporter: 13.1.5 - ipfs-unixfs-importer: 15.1.5 - level: 8.0.0 - lodash: 4.17.21 - lru-cache: 9.1.2 - ms: 2.1.3 - multiformats: 11.0.2 - randombytes: 2.1.0 - readable-stream: 4.4.2 - ulidx: 2.1.0 - uuid: 8.3.2 - varint: 6.0.0 - transitivePeerDependencies: - - encoding - - supports-color - dev: true - /@tbd54566975/dwn-sql-store@0.2.10: resolution: {integrity: sha512-x3tTosQFBwn2Ltqj2FZ84Ma297ekUFeXSyOSfhO6rMHexU1RNmSvuV3YBLfDpBWZUr0ffF1GiJ6zMHKXdoF2jw==} engines: {node: '>=18'} @@ -3258,73 +3173,6 @@ packages: - utf-8-validate dev: true - /@web5/agent@0.2.5: - resolution: {integrity: sha512-Z9JY/43Yrg0xKK26y/iZFdHNtVf/k9XLxw8mXP5zfYidrqfAgVR0i4LKA7qZKfUSxC7/uaD/STYYIKpNByd/cw==} - engines: {node: '>=18.0.0'} - dependencies: - '@tbd54566975/dwn-sdk-js': 0.2.8 - '@web5/common': 0.2.2 - '@web5/crypto': 0.2.2 - '@web5/dids': 0.2.3 - level: 8.0.0 - readable-stream: 4.4.2 - readable-web-to-node-stream: 3.0.2 - transitivePeerDependencies: - - encoding - - supports-color - dev: true - - /@web5/agent@0.2.6: - resolution: {integrity: sha512-qBbFH7b08Sw8JCpPQ+ClbblL++hkSR25e1XNF46ToVTJJldLspLTcXjr2Oui1bpJZ3jppnZ9nvcQPfymtdM5fg==} - engines: {node: '>=18.0.0'} - dependencies: - '@tbd54566975/dwn-sdk-js': 0.2.10 - '@web5/common': 0.2.3 - '@web5/crypto': 0.2.2 - '@web5/dids': 0.2.4 - level: 8.0.0 - readable-stream: 4.4.2 - readable-web-to-node-stream: 3.0.2 - ulidx: 2.1.0 - transitivePeerDependencies: - - encoding - - supports-color - dev: false - - /@web5/api@0.8.4: - resolution: {integrity: sha512-3kqh7KQeeffsOBi+K5Vhpa59+ZgdI1em16drrRd7h59Jj4m8a09Enhw7WjWNLre7rTG84hQqB9+rpS1VHnOaKQ==} - engines: {node: '>=18.0.0'} - dependencies: - '@tbd54566975/dwn-sdk-js': 0.2.10 - '@web5/agent': 0.2.5 - '@web5/common': 0.2.2 - '@web5/crypto': 0.2.2 - '@web5/dids': 0.2.4 - '@web5/user-agent': 0.2.5 - level: 8.0.0 - ms: 2.1.3 - readable-stream: 4.4.2 - readable-web-to-node-stream: 3.0.2 - transitivePeerDependencies: - - encoding - - supports-color - dev: true - - /@web5/common@0.2.1: - resolution: {integrity: sha512-Tt5P17HgQCx+Epw0IHnhRKqp5UU3E4xtsE8PkdghOBnvntBB0op5P6efvR1WqmJft5+VunDHt3yZAZstuqQkNg==} - engines: {node: '>=18.0.0'} - dependencies: - level: 8.0.0 - multiformats: 11.0.2 - - /@web5/common@0.2.2: - resolution: {integrity: sha512-dRn6SmALExeTLMTK/W5ozGarfaddK+Lraf5OjuIGLAaLfcX1RWx3oDMoY5Hr9LjfxHJC8mGXB8DnKflbeYJRgA==} - engines: {node: '>=18.0.0'} - dependencies: - level: 8.0.0 - multiformats: 11.0.2 - readable-stream: 4.4.2 - /@web5/common@0.2.3: resolution: {integrity: sha512-WTbIS6l5inrQTS5cwOoQP5KEuUHZqOWCKCDAA62qGUn4QyySolog9Gt2HCNUEClIzzk/d5DHcpBgLCadHoJ6rQ==} engines: {node: '>=18.0.0'} @@ -3333,15 +3181,6 @@ packages: multiformats: 11.0.2 readable-stream: 4.4.2 - /@web5/crypto@0.2.2: - resolution: {integrity: sha512-vHFg0wXQSQXrwuBNQyDHnmSZchfTfO6/Sv+7rDsNkvofs+6lGTE8CZ02cwUYMeIwTRMLer12c+fMfzYrXokEUQ==} - engines: {node: '>=18.0.0'} - dependencies: - '@noble/ciphers': 0.1.4 - '@noble/curves': 1.1.0 - '@noble/hashes': 1.3.1 - '@web5/common': 0.2.1 - /@web5/crypto@0.4.0: resolution: {integrity: sha512-GIKn2CizQKeATvHQqmC4ky26b3q2pJOd2GjIYsOSw/3Y3QIEm3holDGqu9FHs6kacmr6u0Pv5ELvccs58+cKEg==} engines: {node: '>=18.0.0'} @@ -3351,44 +3190,6 @@ packages: '@noble/hashes': 1.3.3 '@web5/common': 0.2.3 - /@web5/dids@0.2.3: - resolution: {integrity: sha512-Y3PHOavNkSyjBxZQEpKE6XueaqemBO5w0UMOnFh4xH6+5B43ENEE4LHIqVyn2bCpfEBGLXENgDZYqyJphBu0pA==} - engines: {node: '>=18.0.0'} - deprecated: Upgrade to @latest - dependencies: - '@decentralized-identity/ion-pow-sdk': 1.0.17 - '@decentralized-identity/ion-sdk': 1.0.1 - '@web5/common': 0.2.2 - '@web5/crypto': 0.2.2 - did-resolver: 4.1.0 - dns-packet: 5.6.1 - level: 8.0.0 - ms: 2.1.3 - pkarr: 1.1.1 - z32: 1.0.1 - transitivePeerDependencies: - - encoding - - supports-color - dev: true - - /@web5/dids@0.2.4: - resolution: {integrity: sha512-e+m+xgpiM8ydTJgWcPdwmjILLMZYdl2kwahlO22mK0azSKVrg1klpGrUODzqkrWrQ5O0tnOyqEy39FcD5Sy11w==} - engines: {node: '>=18.0.0'} - dependencies: - '@decentralized-identity/ion-pow-sdk': 1.0.17 - '@decentralized-identity/ion-sdk': 1.0.1 - '@web5/common': 0.2.2 - '@web5/crypto': 0.2.2 - did-resolver: 4.1.0 - dns-packet: 5.6.1 - level: 8.0.0 - ms: 2.1.3 - pkarr: 1.1.1 - z32: 1.0.1 - transitivePeerDependencies: - - encoding - - supports-color - /@web5/dids@0.4.0: resolution: {integrity: sha512-e+QcrKWlWPJVBbpz4QKrdcgPoj+uC8YUXt4tGKj1mC4kV0rCtIioMLw9Djg+54pp3VXl3eGKxju98UEddyZwqA==} engines: {node: '>=18.0.0'} @@ -3446,19 +3247,6 @@ packages: - utf-8-validate dev: true - /@web5/user-agent@0.2.5: - resolution: {integrity: sha512-qv5M698C5HSvq30xUgLWtcsbZppjfOH5qZthpTRx4ItL5UWA/eQ9DsQiQeb4vet3uIUy3NHRDIQezclOdwYErw==} - engines: {node: '>=18.0.0'} - dependencies: - '@web5/agent': 0.2.5 - '@web5/common': 0.2.2 - '@web5/crypto': 0.2.2 - '@web5/dids': 0.2.3 - transitivePeerDependencies: - - encoding - - supports-color - dev: true - /@webassemblyjs/ast@1.11.6: resolution: {integrity: sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==} dependencies: @@ -3834,6 +3622,7 @@ packages: /b4a@1.6.6: resolution: {integrity: sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==} + dev: true /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -3857,15 +3646,6 @@ packages: engines: {node: '>=10.0.0'} dev: true - /bencode@2.0.3: - resolution: {integrity: sha512-D/vrAD4dLVX23NalHwb8dSvsUsxeRPO8Y7ToKA015JQYq69MLDOMkC0uGZYA/MPpltLO8rt8eqFC2j8DxjTZ/w==} - - /bencode@3.1.1: - resolution: {integrity: sha512-btsxX9201yoWh45TdqYg6+OZ5O1xTYKTYSGvJndICDFtznE/9zXgow8yjMvvhOqKKuzuL7h+iiCMpfkG8+QuBA==} - engines: {node: '>=12.20.0'} - dependencies: - uint8-util: 2.2.4 - /bencode@4.0.0: resolution: {integrity: sha512-AERXw18df0pF3ziGOCyUjqKZBVNH8HV3lBxnx5w0qtgMIk4a1wb9BkcCQbkp9Zstfrn/dzRwl7MmUHHocX3sRQ==} engines: {node: '>=12.20.0'} @@ -3895,21 +3675,6 @@ packages: resolution: {integrity: sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==} dev: true - /bittorrent-dht@11.0.5: - resolution: {integrity: sha512-R09D6uNaziRqsc+B/j5QzkjceTak+wH9vcNLnkmt8A52EWF9lQwBP0vvCKgSA3AJOYYl+41n3osA2KYYn/z5uQ==} - engines: {node: '>=12.20.0'} - dependencies: - bencode: 4.0.0 - debug: 4.3.4(supports-color@8.1.1) - k-bucket: 5.1.0 - k-rpc: 5.1.0 - last-one-wins: 1.0.4 - lru: 3.1.0 - randombytes: 2.1.0 - record-cache: 1.2.0 - transitivePeerDependencies: - - supports-color - /bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} dependencies: @@ -3925,18 +3690,6 @@ packages: inherits: 2.0.4 readable-stream: 3.6.2 - /blake2b-wasm@2.4.0: - resolution: {integrity: sha512-S1kwmW2ZhZFFFOghcx73+ZajEfKBqhP82JMssxtLVMxlaPea1p9uoLiUZ5WYyHn0KddwbLc+0vh4wR0KBNoT5w==} - dependencies: - b4a: 1.6.6 - nanoassert: 2.0.0 - - /blake2b@2.1.4: - resolution: {integrity: sha512-AyBuuJNI64gIvwx13qiICz6H6hpmjvYS5DGkG6jbXMOT8Z3WUJ3V1X0FlhIoT1b/5JtHE3ki+xjtMvu1nn+t9A==} - dependencies: - blake2b-wasm: 2.4.0 - nanoassert: 2.0.0 - /blockstore-core@4.2.0: resolution: {integrity: sha512-F8BCobc75D+9/+hUD+5cixbU6zmZA+lBgNiuBkNlJqRgmAaBBvLOQF6Ad9Jei0Nvmy2a1jaF4CiN76W1apIghA==} engines: {node: '>=16.0.0', npm: '>=7.0.0'} @@ -4222,11 +3975,6 @@ packages: resolution: {integrity: sha512-xAuZbCDUOZxCe/ZJuIrnlG1Bk1R0qhwCXdnPYxVmqBSqm9M3BeE3G6Qoj5Zq+8epas36bT3vjiInDTJ6BVH6Rg==} hasBin: true - /chacha20-universal@1.0.4: - resolution: {integrity: sha512-/IOxdWWNa7nRabfe7+oF+jVkGjlr2xUL4J8l/OvzZhj+c9RpMqoo3Dq+5nU1j/BflRV4BKnaQ4+4oH1yBpQG1Q==} - dependencies: - nanoassert: 2.0.0 - /chai-as-promised@7.1.1(chai@4.3.10): resolution: {integrity: sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==} peerDependencies: @@ -4273,10 +4021,6 @@ packages: supports-color: 7.2.0 dev: true - /chalk@5.3.0: - resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} - engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - /charenc@0.0.2: resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==} dev: true @@ -4321,17 +4065,6 @@ packages: resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} dev: true - /chrome-dgram@3.0.6: - resolution: {integrity: sha512-bqBsUuaOiXiqxXt/zA/jukNJJ4oaOtc7ciwqJpZVEaaXwwxqgI2/ZdG02vXYWUhHGziDlvGMQWk0qObgJwVYKA==} - dependencies: - inherits: 2.0.4 - run-series: 1.1.9 - - /chrome-dns@1.0.1: - resolution: {integrity: sha512-HqsYJgIc8ljJJOqOzLphjAs79EUuWSX3nzZi2LNkzlw3GIzAeZbaSektC8iT/tKvLqZq8yl1GJu5o6doA4TRbg==} - dependencies: - chrome-net: 3.3.4 - /chrome-launcher@0.15.2: resolution: {integrity: sha512-zdLEwNo3aUVzIhKhTtXfxhdvZhUghrnmkvcAq2NoDd+LeOHKf03H5jwZ8T/STsAlzyALkBVK552iaG1fGf1xVQ==} engines: {node: '>=12.13.0'} @@ -4345,11 +4078,6 @@ packages: - supports-color dev: true - /chrome-net@3.3.4: - resolution: {integrity: sha512-Jzy2EnzmE+ligqIZUsmWnck9RBXLuUy6CaKyuNMtowFG3ZvLt8d+WBJCTPEludV0DHpIKjAOlwjFmTaEdfdWCw==} - dependencies: - inherits: 2.0.4 - /chrome-trace-event@1.0.3: resolution: {integrity: sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==} engines: {node: '>=6.0'} @@ -4560,13 +4288,6 @@ packages: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} dev: true - /cross-fetch@3.1.5: - resolution: {integrity: sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==} - dependencies: - node-fetch: 2.6.7 - transitivePeerDependencies: - - encoding - /cross-fetch@4.0.0: resolution: {integrity: sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==} dependencies: @@ -4762,9 +4483,6 @@ packages: resolution: {integrity: sha512-hyWmRrexdhbZ1tcJUGpO95ivbRhWXz++F4Ko+n21AY5PNln2ovoJw+8ZMNDTtip+CNFQfrtLVh/w4009dXO/eQ==} dev: true - /did-resolver@4.1.0: - resolution: {integrity: sha512-S6fWHvCXkZg2IhS4RcVHxwuyVejPR7c+a4Go0xbQ9ps5kILa8viiYQgrM4gfTyeTjJ0ekgJH9gk/BawTpmkbZA==} - /diff@5.0.0: resolution: {integrity: sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==} engines: {node: '>=0.3.1'} @@ -4790,12 +4508,6 @@ packages: path-type: 4.0.0 dev: true - /dns-packet@5.6.1: - resolution: {integrity: sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==} - engines: {node: '>=6'} - dependencies: - '@leichtgewicht/ip-codec': 2.0.4 - /doctrine@3.0.0: resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} engines: {node: '>=6.0.0'} @@ -5629,11 +5341,6 @@ packages: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} dev: true - /graceful-goodbye@1.3.0: - resolution: {integrity: sha512-hcZOs20emYlTM7MmUE0FpuZcjlk2GPsR+UYTHDeWxtGjXcbh2CawGi8vlzqsIvspqAbot7mRv3sC/uhgtKc4hQ==} - dependencies: - safety-catch: 1.0.2 - /graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} dev: true @@ -5685,9 +5392,6 @@ packages: safe-buffer: 5.2.1 dev: true - /hash-wasm@4.9.0: - resolution: {integrity: sha512-7SW7ejyfnRxuOc7ptQHSf4LDoZaWOivfzqw+5rpcQku0nHfmicPKE51ra9BiRLAmT8+gGLestr1XroUkqdjL6w==} - /hash.js@1.1.7: resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} dependencies: @@ -6313,26 +6017,6 @@ packages: /jwt-decode@3.1.2: resolution: {integrity: sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==} - /k-bucket@5.1.0: - resolution: {integrity: sha512-Fac7iINEovXIWU20GPnOMLUbjctiS+cnmyjC4zAUgvs3XPf1vo9akfCHkigftSic/jiKqKl+KA3a/vFcJbHyCg==} - dependencies: - randombytes: 2.1.0 - - /k-rpc-socket@1.11.1: - resolution: {integrity: sha512-8xtA8oqbZ6v1Niryp2/g4GxW16EQh5MvrUylQoOG+zcrDff5CKttON2XUXvMwlIHq4/2zfPVFiinAccJ+WhxoA==} - dependencies: - bencode: 2.0.3 - chrome-dgram: 3.0.6 - chrome-dns: 1.0.1 - chrome-net: 3.3.4 - - /k-rpc@5.1.0: - resolution: {integrity: sha512-FGc+n70Hcjoa/X2JTwP+jMIOpBz+pkRffHnSl9yrYiwUxg3FIgD50+u1ePfJUOnRCnx6pbjmVk5aAeB1wIijuQ==} - dependencies: - k-bucket: 5.1.0 - k-rpc-socket: 1.11.1 - randombytes: 2.1.0 - /keygrip@1.1.0: resolution: {integrity: sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==} engines: {node: '>= 0.6'} @@ -6421,9 +6105,6 @@ packages: engines: {node: '>=14.0.0'} dev: true - /last-one-wins@1.0.4: - resolution: {integrity: sha512-t+KLJFkHPQk8lfN6WBOiGkiUXoub+gnb2XTYI2P3aiISL+94xgZ1vgz1SXN/N4hthuOoLXarXfBZPUruyjQtfA==} - /layerr@2.0.1: resolution: {integrity: sha512-z0730CwG/JO24evdORnyDkwG1Q7b7mF2Tp1qRQ0YvrMMARbt1DFG694SOv439Gm7hYKolyZyaB49YIrYIfZBdg==} @@ -6564,12 +6245,6 @@ packages: resolution: {integrity: sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ==} engines: {node: 14 || >=16.14} - /lru@3.1.0: - resolution: {integrity: sha512-5OUtoiVIGU4VXBOshidmtOsvBIvcQR6FD/RzWSvaeHyxCGB+PCUCu+52lqMfdc0h/2CLvHhZS4TwUmMQrrMbBQ==} - engines: {node: '>= 0.4.0'} - dependencies: - inherits: 2.0.4 - /make-dir@4.0.0: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} @@ -6861,9 +6536,6 @@ packages: lru-cache: 7.18.3 dev: true - /nanoassert@2.0.0: - resolution: {integrity: sha512-7vO7n28+aYO4J+8w96AzhmU8G+Y/xpPDJz/se19ICsqj/momRbb9mh9ZUtkoJ5X3nTnPdhEJyc0qnM6yAsHBaA==} - /nanocolors@0.2.13: resolution: {integrity: sha512-0n3mSAQLPpGLV9ORXT5+C/D4mwew7Ebws69Hx4E2sgz2ZA5+32Q80B9tL8PbL7XHnRDiAxH/pnrUJ9a4fkTNTA==} dev: true @@ -6932,17 +6604,6 @@ packages: lodash: 4.17.21 dev: true - /node-fetch@2.6.7: - resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==} - engines: {node: 4.x || >=6.0.0} - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true - dependencies: - whatwg-url: 5.0.0 - /node-fetch@2.7.0: resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} engines: {node: 4.x || >=6.0.0} @@ -7389,20 +7050,6 @@ packages: engines: {node: '>=8.6'} dev: true - /pkarr@1.1.1: - resolution: {integrity: sha512-X27LKqf83X3WuJd2Z9qdfVxkmfOu6HUbY0pm11LqeBbFmgmZRPgOxJG8bKiIsmmD6Vjc25j45KHYflF2lfodyQ==} - hasBin: true - dependencies: - bencode: 3.1.1 - bittorrent-dht: 11.0.5 - chalk: 5.3.0 - dns-packet: 5.6.1 - graceful-goodbye: 1.3.0 - sodium-universal: 4.0.0 - z32: 1.0.1 - transitivePeerDependencies: - - supports-color - /pkg-dir@5.0.0: resolution: {integrity: sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==} engines: {node: '>=10'} @@ -7742,6 +7389,7 @@ packages: engines: {node: '>=8'} dependencies: readable-stream: 3.6.2 + dev: false /readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} @@ -7750,11 +7398,6 @@ packages: picomatch: 2.3.1 dev: true - /record-cache@1.2.0: - resolution: {integrity: sha512-kyy3HWCez2WrotaL3O4fTn0rsIdfRKOdQQcEJ9KpvmKmbffKVvwsloX063EgRUlpJIXHiDQFhJcTbZequ2uTZw==} - dependencies: - b4a: 1.6.6 - /regexp.prototype.flags@1.5.2: resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} engines: {node: '>= 0.4'} @@ -7878,9 +7521,6 @@ packages: queue-microtask: 1.2.3 dev: true - /run-series@1.1.9: - resolution: {integrity: sha512-Arc4hUN896vjkqCYrUXquBFtRZdv1PfLbTYP71efP6butxyQ0kWpiNJyAgsxscmQg1cqvHY32/UCBzXedTpU2g==} - /rxjs@7.8.1: resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} dependencies: @@ -7917,9 +7557,6 @@ packages: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} dev: true - /safety-catch@1.0.2: - resolution: {integrity: sha512-C1UYVZ4dtbBxEtvOcpjBaaD27nP8MlvyAQEp2fOTOEe6pfUpk1cDUxij6BR1jZup6rSyUTaBBplK7LanskrULA==} - /schema-utils@3.3.0: resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} engines: {node: '>= 10.13.0'} @@ -8027,30 +7664,6 @@ packages: safe-buffer: 5.2.1 dev: true - /sha256-universal@1.2.1: - resolution: {integrity: sha512-ghn3muhdn1ailCQqqceNxRgkOeZSVfSE13RQWEg6njB+itsFzGVSJv+O//2hvNXZuxVIRyNzrgsZ37SPDdGJJw==} - dependencies: - b4a: 1.6.6 - sha256-wasm: 2.2.2 - - /sha256-wasm@2.2.2: - resolution: {integrity: sha512-qKSGARvao+JQlFiA+sjJZhJ/61gmW/3aNLblB2rsgIxDlDxsJPHo8a1seXj12oKtuHVgJSJJ7QEGBUYQN741lQ==} - dependencies: - b4a: 1.6.6 - nanoassert: 2.0.0 - - /sha512-universal@1.2.1: - resolution: {integrity: sha512-kehYuigMoRkIngCv7rhgruLJNNHDnitGTBdkcYbCbooL8Cidj/bS78MDxByIjcc69M915WxcQTgZetZ1JbeQTQ==} - dependencies: - b4a: 1.6.6 - sha512-wasm: 2.3.4 - - /sha512-wasm@2.3.4: - resolution: {integrity: sha512-akWoxJPGCB3aZCrZ+fm6VIFhJ/p8idBv7AWGFng/CZIrQo51oQNsvDbTSRXWAzIiZJvpy16oIDiCCPqTe21sKg==} - dependencies: - b4a: 1.6.6 - nanoassert: 2.0.0 - /shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -8104,11 +7717,6 @@ packages: supports-color: 7.2.0 dev: true - /siphash24@1.3.1: - resolution: {integrity: sha512-moemC3ZKiTzH29nbFo3Iw8fbemWWod4vNs/WgKbQ54oEs6mE6XVlguxvinYjB+UmaE0PThgyED9fUkWvirT8hA==} - dependencies: - nanoassert: 2.0.0 - /slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} @@ -8147,36 +7755,6 @@ packages: smart-buffer: 4.2.0 dev: true - /sodium-javascript@0.8.0: - resolution: {integrity: sha512-rEBzR5mPxPES+UjyMDvKPIXy9ImF17KOJ32nJNi9uIquWpS/nfj+h6m05J5yLJaGXjgM72LmQoUbWZVxh/rmGg==} - dependencies: - blake2b: 2.1.4 - chacha20-universal: 1.0.4 - nanoassert: 2.0.0 - sha256-universal: 1.2.1 - sha512-universal: 1.2.1 - siphash24: 1.3.1 - xsalsa20: 1.2.0 - - /sodium-native@4.0.10: - resolution: {integrity: sha512-vrJQt4gASntDbnltRRk9vN4rks1SehjM12HkqQtu250JtWT+/lo8oEOa1HvSq3+8hzJdYcCJuLR5qRGxoRDjAg==} - requiresBuild: true - dependencies: - node-gyp-build: 4.8.0 - - /sodium-universal@4.0.0: - resolution: {integrity: sha512-iKHl8XnBV96k1c75gwwzANFdephw/MDWSjQAjPmBE+du0y3P23Q8uf7AcdcfFsYAMwLg7WVBfSAIBtV/JvRsjA==} - dependencies: - blake2b: 2.1.4 - chacha20-universal: 1.0.4 - nanoassert: 2.0.0 - sha256-universal: 1.2.1 - sha512-universal: 1.2.1 - siphash24: 1.3.1 - sodium-javascript: 0.8.0 - sodium-native: 4.0.10 - xsalsa20: 1.2.0 - /source-map-js@1.0.2: resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} engines: {node: '>=0.10.0'} @@ -9051,9 +8629,6 @@ packages: resolution: {integrity: sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==} dev: true - /xsalsa20@1.2.0: - resolution: {integrity: sha512-FIr/DEeoHfj7ftfylnoFt3rAIRoWXpx2AoDfrT2qD2wtp7Dp+COajvs/Icb7uHqRW9m60f5iXZwdsJJO3kvb7w==} - /xtend@4.0.2: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} @@ -9143,8 +8718,3 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} dev: true - - /z32@1.0.1: - resolution: {integrity: sha512-Uytfqf6VEVchHKZDw0NRdCViOARHP84uzvOw0CXCMLOwhgHZUL9XibpEPLLQN10mCVLxOlGCQWbkV7km7yNYcw==} - dependencies: - b4a: 1.6.6 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index d9b7946c8..bf5d0d166 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -7,5 +7,5 @@ packages: - "packages/agent" - "packages/user-agent" - "packages/proxy-agent" - - "packages/api" - "packages/identity-agent" + - "packages/api"