From 74d9fc25153c6245ac077994a182917b25f1c36e Mon Sep 17 00:00:00 2001 From: cycraig Date: Thu, 16 Jun 2022 13:20:53 +0200 Subject: [PATCH 01/89] Bump examples version in GitHub Actions (#906) --- .github/actions/release/bump-versions/action.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/actions/release/bump-versions/action.yml b/.github/actions/release/bump-versions/action.yml index da119ef373..46641368fe 100644 --- a/.github/actions/release/bump-versions/action.yml +++ b/.github/actions/release/bump-versions/action.yml @@ -53,6 +53,14 @@ runs: run: | cargo workspaces version --force "*" --no-git-commit --exact custom ${{ inputs.version }} -y + # cargo workspaces ignores examples/ but cargo release still tries to version it during publishing. + - name: Bump Rust examples version + shell: bash + if: ${{inputs.release-target == 'rust'}} + working-directory: examples + run: | + cargo set-version ${{ inputs.version }} + - name: Bump Wasm bindings crate version shell: bash if: ${{inputs.release-target == 'wasm'}} From 2e1a9c39e1e93c2c98520e15942ee5d30ce0b051 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Ha=C3=9F?= Date: Mon, 20 Jun 2022 14:49:46 +0200 Subject: [PATCH 02/89] Add `RevocationBitmap2022` specification (#879) (#912) --- .../wasm/examples-account/src/revoke_vc.ts | 6 +- .../verifiable_credentials/create.mdx | 2 +- .../verifiable_credentials/revocation.mdx | 62 ++++++ .../verifiable_credentials/revoke.mdx | 48 ----- documentation/docs/specs/overview.md | 10 +- .../docs/specs/revocation_bitmap_2022.md | 201 ++++++++++++++++++ documentation/sidebars.js | 3 +- examples/account/revoke_vc.rs | 11 +- 8 files changed, 281 insertions(+), 62 deletions(-) create mode 100644 documentation/docs/concepts/verifiable_credentials/revocation.mdx delete mode 100644 documentation/docs/concepts/verifiable_credentials/revoke.mdx create mode 100644 documentation/docs/specs/revocation_bitmap_2022.md diff --git a/bindings/wasm/examples-account/src/revoke_vc.ts b/bindings/wasm/examples-account/src/revoke_vc.ts index 5dbf6564f1..59fb034ede 100644 --- a/bindings/wasm/examples-account/src/revoke_vc.ts +++ b/bindings/wasm/examples-account/src/revoke_vc.ts @@ -16,9 +16,9 @@ import { /** This example shows how to revoke a verifiable credential. - The Verifiable Credential is revoked by actually removing a verification method (public key) from the DID Document of the Issuer. - As such, the Verifiable Credential can no longer be validated. - This would invalidate every Verifiable Credential signed with the same public key, therefore the issuer would have to sign every VC with a different key. + It demonstrates two methods for revocation. The first uses a revocation bitmap of type `RevocationBitmap2022`, + while the second method simply removes the verification method (public key) that signed the credential + from the DID Document of the issuer. Note that this example uses the "main" network, if you are writing code against the test network then most function calls will need to include information about the network, since this is not automatically inferred from the diff --git a/documentation/docs/concepts/verifiable_credentials/create.mdx b/documentation/docs/concepts/verifiable_credentials/create.mdx index 75380ca8fe..d4577dec46 100644 --- a/documentation/docs/concepts/verifiable_credentials/create.mdx +++ b/documentation/docs/concepts/verifiable_credentials/create.mdx @@ -23,7 +23,7 @@ In the IOTA Identity Framework you can create a Verifiable Credential with the f - [**ID**](https://www.w3.org/TR/vc-data-model/#identifiers): optional URI identifier for the credential. - [**Issuance Date**](https://www.w3.org/TR/vc-data-model/#issuance-date): optional timestamp for expressing the date and time when a credential becomes valid. - [**Expiration Date**](https://www.w3.org/TR/vc-data-model/#expiration): optional timestamp for expressing the date and time when a credential ceases to be valid. -- [**Status**](https://www.w3.org/TR/vc-data-model/#status): optional information used to determine the current status of a credential, i.e. whether or not it has been [revoked](./revoke.mdx). +- [**Status**](https://www.w3.org/TR/vc-data-model/#status): optional information used to determine the current status of a credential, i.e. whether or not it has been [revoked](./revocation.mdx). - [**Schema**](https://www.w3.org/TR/vc-data-model/#data-schemas): optional list of objects specifying the schema that the data must conform to. - [**Refresh Service**](https://www.w3.org/TR/vc-data-model/#refreshing): optional link to a service where the recipient may refresh the included credentials. - [**Terms of Use**](https://www.w3.org/TR/vc-data-model/#terms-of-use): optional list of policies defining obligations, prohibitions, or permissions of the presentation recipient. diff --git a/documentation/docs/concepts/verifiable_credentials/revocation.mdx b/documentation/docs/concepts/verifiable_credentials/revocation.mdx new file mode 100644 index 0000000000..70bab99914 --- /dev/null +++ b/documentation/docs/concepts/verifiable_credentials/revocation.mdx @@ -0,0 +1,62 @@ +--- +title: Verifiable Credential Revocation +sidebar_label: Revocation +description: Explain how a VC can be revoked +image: /img/Identity_icon.png +keywords: + - verifiable + - credentials + - revoke + - revocation +--- + +import revoke_vc_rs from "!!raw-loader!../../../../examples/account/revoke_vc.rs"; +import CodeSnippet from "../../../src/components/CodeSnippetComponent"; + +## Overview + +The [example](#example) below demonstrates two methods that an issuer can use to revoke a verifiable credential using the IOTA Identity Framework: + +1. By using the [`credentialStatus`](https://www.w3.org/TR/vc-data-model/#status) field in a credential and linking to a revocation bitmap, using the [`RevocationBitmap2022`](../../specs/revocation_bitmap_2022). +2. By removing the verification method that signed the credential. This invalidates all credentials that were signed with that verification method. + +## Revocation Bitmap + +One of the ways for an issuer to control the status of its credentials is by using a revocation list. At the most basic level, revocation information for all verifiable credentials issued by an issuer are expressed as simple binary values. The issuer keeps a list of all verifiable credentials it has issued in a bitmap. Each verifiable credential is associated with a unique index in the list. If the binary value of the index in the bitmap is 1 (one), the verifiable credential is revoked, if it is 0 (zero) it is not revoked. + +For example, with this approach the issuer adds an index to a credential in the `credentialStatus` field, such as `"5"`. This part of the credential might then look like this: + +```json +"credentialStatus": { + "id": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw#revocation", + "type": "RevocationBitmap2022", + "revocationBitmapIndex": "5" +}, +``` + +The verifier uses the `id` field (`did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw#revocation`) to look up the service in the issuer's DID document. This is an example of such a service: + +```json +{ + "id": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw#revocation", + "type": "RevocationBitmap2022", + "serviceEndpoint": "data:application/octet-stream;base64,ZUp5ek1tQmdZR1NBQUFFZ1ptVUFBQWZPQUlF" +} +``` + +During verification the verifier decodes the revocation bitmap embedded in the `data` url. This bitmap written as a bitstring looks like this: `000001`. Here, the 5th bit is set, which means the credential with that index is revoked, while all other credentials aren't revoked. + +## Removing the verification method + +A less efficient alternative is to remove the verification method that signed the credential from the DID Document of the issuer. This means the VC can no longer be validated. However, this would invalidate every VC signed with that verification method, meaning the issuer would have to sign every VC with a different key to retain precise control over which credential is revoked. + +## Example {#example} + +The following code exemplifies how you can revoke a [Verifiable Credential (VC)](overview). + + diff --git a/documentation/docs/concepts/verifiable_credentials/revoke.mdx b/documentation/docs/concepts/verifiable_credentials/revoke.mdx deleted file mode 100644 index 51e01f03f0..0000000000 --- a/documentation/docs/concepts/verifiable_credentials/revoke.mdx +++ /dev/null @@ -1,48 +0,0 @@ ---- -title: Verifiable Credential Revocation -sidebar_label: Revocation -description: Explain how a VC is revoked -image: /img/Identity_icon.png -keywords: -- verifiable -- credentials -- revoke ---- -import revoke_vc_rs from '!!raw-loader!../../../../examples/account/revoke_vc.rs'; -import CodeSnippet from '../../../src/components/CodeSnippetComponent' - -The [example](#example) below demonstrates how an issuer can revoke a verifiable credential using the IOTA Identity Framework by removing its verification method. But another aspect of it, is how an issuer can easily allow verifiers to check if the credential has been revoked. In order to allow this, an issuer can link to a location where a verifier can check the credential status. This can be specified in the [credential status](https://www.w3.org/TR/vc-data-model/#status) property. - -### Revocation List 2020 {#revocation-list-2020} - -One of the ways for an issuer to control the status of its credentials is by using a revocation list. At the most basic level, revocation information for all verifiable credentials issued by an issuer are expressed as simple binary values. The issuer keeps a bitstring list of all verifiable credentials it has issued. Each verifiable credential is associated with a position in the list. If the binary value of the position in the list is 1 (one), the verifiable credential is revoked, if it is 0 (zero) it is not revoked. -When using [revocation list 2020](https://w3c-ccg.github.io/vc-status-rl-2020/#core-concept) the [credential status](https://w3c-ccg.github.io/vc-status-rl-2020/#revocationlist2020status) of the credential must be defined as follows: - -- **ID**: a URL that identifies the status information associated with the verifiable credential. -- **Types**: The type property must be RevocationList2020Status. -- **Revocation List Index**: an arbitrary size integer greater than or equal to 0, expressed as a string. The value identifies the bit position of the revocation status of the verifiable credential. -- **Revocation List Credential**: a URL to a verifiable credential. When the URL is dereferenced, the resulting verifiable credential must have type property that includes the RevocationList2020Credential value. - -When a revocation list is published, the result is a verifiable credential that encapsulates the revocation list. This [revocation list credential](https://w3c-ccg.github.io/vc-status-rl-2020/#revocationlist2020credential) must have the following properties: - -- **ID**: an id property that matches the value specified in revocationListCredential for the corresponding RevocationList2020Status. -- **Types**: must contain RevocationList2020Credential. -- **Credential Subject Type**: the type of the credential subject must be RevocationList2020. -- **Credential Subject Encoded List**: The encodedList property of the credential subject must be the ZLIB-compressed [RFC1950](https://w3c-ccg.github.io/vc-status-rl-2020/#bib-rfc1950), base-64 encoded [RFC4648](https://w3c-ccg.github.io/vc-status-rl-2020/#bib-rfc4648) bitstring values for the associated range of verifiable credential status values. The uncompressed bitstring must be at least 16KB in size. - -:::note - -Revocation List 2020 is not yet supported in the IOTA Identity Framework - -::: - -## Example {#example} - -The following code exemplifies how you can revoke a [Verifiable Credential(VC)](overview) by removing a verification method (public key) from the DID Document of the Issuer. This means the VC can no longer be validated. This would invalidate every VC signed with the same public key, meaning the Issuer would have to sign every VC with a different key. - - \ No newline at end of file diff --git a/documentation/docs/specs/overview.md b/documentation/docs/specs/overview.md index 63b96e59a8..35a9e1fcb9 100644 --- a/documentation/docs/specs/overview.md +++ b/documentation/docs/specs/overview.md @@ -4,11 +4,13 @@ sidebar_label: Overview description: Provide overview of the specifications image: /img/Identity_icon.png keywords: -- specifications + - specifications --- While IOTA Identity implements many existing standards, it also adds some additional features we would like to standardize ourselves. This chapter covers these features and how they work in great detail. These are not light reads and can be skipped. -The current specification are: -- [IOTA DID](./did/overview): The specification for the IOTA DID Method implemented on the tangle -- [IOTA DIDComm](./didcomm/overview): A collection of DIDComm protocols for common SSI interactions \ No newline at end of file +The current specifications are: + +- [IOTA DID](./did/overview): The specification for the IOTA DID Method implemented on the Tangle. +- [IOTA DIDComm](./didcomm/overview): A collection of DIDComm protocols for common SSI interactions. +- [Revocation Bitmap 2022](./revocation_bitmap_2022): The specification for an on-Tangle credential revocation mechanism. diff --git a/documentation/docs/specs/revocation_bitmap_2022.md b/documentation/docs/specs/revocation_bitmap_2022.md new file mode 100644 index 0000000000..2ff5cfbc79 --- /dev/null +++ b/documentation/docs/specs/revocation_bitmap_2022.md @@ -0,0 +1,201 @@ +--- +title: Revocation Bitmap +sidebar_label: Revocation Bitmap +description: The specification for the embedded revocation bitmap. +image: /img/Identity_icon.png +keywords: + - DID + - specs + - specifications + - revocation + - bitmap +--- + +# Revocation Bitmap 2022 + +## Abstract + +This specification describes an on-Tangle mechanism for publishing the revocation status of [Verifiable Credentials](../concepts/verifiable_credentials/overview) embedded in an issuer's DID document. + +## Introduction + +Revocation gives an issuer the capability to invalidate a credential they issued before its natural expiration date. To achieve this, issuers can embed an identifier in the `credentialStatus` field of a credential. Verifiers can then lookup that identifier in a separate list, to check whether the credential is still valid. This document specifies a mechanism of embedding such a list, in form of a bitmap, in the DID document of the issuer, where each bitmap index corresponds to a credential they have issued. This mechanism is space-efficient and enables a verifier to check a credential's status in a privacy-preserving manner and without requiring additional lookups or external resources. + +## Revocation Bitmap Concept + +The revocation status of a verifiable credential is expressed as a binary value. The issuer keeps a bitmap of indices corresponding to verifiable credentials they have issued. If the binary value of the index in the bitmap is 1 (one), the verifiable credential is revoked, if it is 0 (zero) it is not revoked. + +## Data Model + +### Revocation Bitmap Status + +For an issuer to enable verifiers to check the status of a verifiable credential, the [`credentialStatus`](https://www.w3.org/TR/vc-data-model/#status) property MUST be specified with the following properties: + +| Property | Description | +| :--- | :--- | +| `id` | The constraints on the `id` property are listed in the [Verifiable Credentials Data Model specification](https://www.w3.org/TR/vc-data-model/). The `id` MUST be a [DID URL](https://www.w3.org/TR/did-core/#did-url-syntax) that resolves to a [Revocation Bitmap Service](#revocation-bitmap-service) in the DID Document of the issuer. | +| `type` | The `type` property MUST be `"RevocationBitmap2022"`. | +| `revocationBitmapIndex` | The `revocationBitmapIndex` property MUST be an unsigned, 32-bit integer expressed as a string. This is the index of the credential in the issuer's revocation bitmap. Each index SHOULD be unique among all credentials linking to the same [Revocation Bitmap Service](#revocation-bitmap-service). | + +#### Example + +An example of a verifiable credential with a `credentialStatus` of type `RevocationBitmap2022`. + +```json +{ + "@context": "https://www.w3.org/2018/credentials/v1", + "id": "https://example.edu/credentials/3732", + "type": ["VerifiableCredential", "UniversityDegreeCredential"], + "credentialSubject": { + "id": "did:iota:B8DucnzULJ9E8cmaReYoePU2b7UKE9WKxyEVov8tQA7H", + "GPA": "4.0", + "degree": "Bachelor of Science and Arts", + "name": "Alice" + }, + "issuer": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw", + "issuanceDate": "2022-06-13T08:04:36Z", + "credentialStatus": { + "id": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw#revocation", + "type": "RevocationBitmap2022", + "revocationBitmapIndex": "5" + }, + "proof": { + "type": "JcsEd25519Signature2020", + "verificationMethod": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw#key-1", + "signatureValue": "2eHdbDumMrer4pNVkaiYMqsVqVp2adq7bRcgTJZiw17Zeghk2ZT49YHwLwCCg35YKganBhxP6YSbzYoBK1AuCUv" + } +} +``` + +### Revocation Bitmap Service + +To allow verifiers to check the status of a [Revocation Bitmap Status](#revocation-bitmap-status), the DID document of the credential issuer MUST contain a [service](https://www.w3.org/TR/did-core/#services) with the following properties: + +| Property | Description | +| :--- | :--- | +| `id` | The constraints on the `id` property are listed in the [DID Core service specification](https://www.w3.org/TR/did-core/#services). The `id` property MUST be a DID URL uniquely identifying the revocation bitmap. | +| `type` | The `type` property MUST be `"RevocationBitmap2022"`. | +| `serviceEndpoint` | The `serviceEndpoint` MUST be generated according to the [service endpoint generation algorithm](#service-endpoint-generation-algorithm). | + +#### Example + +An example of an issuer's DID document where credential `"5"` in the `#revocation` service is revoked: + +```json +{ + "id": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw", + "verificationMethod": [ + { + "id": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw#key-1", + "controller": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw", + "type": "Ed25519VerificationKey2018", + "publicKeyMultibase": "z3hgM9fNkhwgT5mECbj1HdKoFNZgpffwQYEV8WBVHphXq" + } + ], + "capabilityInvocation": [ + { + "id": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw#sign-0", + "controller": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw", + "type": "Ed25519VerificationKey2018", + "publicKeyMultibase": "z83F6zbD3KqaxvQhqo25LvSXzoDdpZmp3EpPVonSVACwZ" + } + ], + "service": [ + { + "id": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw#revocation", + "type": "RevocationBitmap2022", + "serviceEndpoint": "data:application/octet-stream;base64,ZUp5ek1tQmdZR1NBQUFFZ1ptVUFBQWZPQUlF" + } + ] +} +``` + +## Algorithms + +The following algorithms define how to generate, expand and validate revocation bitmaps. + +### Service Endpoint Generation Algorithm + +The following process MUST be followed when producing a `RevocationBitmap2022` to embed in a service endpoint: + +1. Let **bitmap** be a [_roaring bitmap_](https://roaringbitmap.org/) where each bit is initialized to 0. +2. For each revoked credential with an **index** not exceeding an unsigned, 32-bit integer, set the corresponding bit in **bitmap** at **index** to 1. +3. Generate the **bitmap serialization** according to the [roaring bitmap serialization format](https://github.com/RoaringBitmap/RoaringFormatSpec/) using the **bitmap** as input. +4. Generate a **compressed bitmap** by using the ZLIB compression algorithm [[RFC 1950](https://datatracker.ietf.org/doc/html/rfc1950)] on the **bitmap serialization** and base64-encoding [[RFC4648](https://datatracker.ietf.org/doc/html/rfc4648)] the result. +5. Create the **service endpoint** by embedding the **compressed bitmap** in a data URL [[RFC2397](https://datatracker.ietf.org/doc/html/rfc2397)]. On the data url, the `` MUST be `application/octet-stream` and the `base64` attribute MUST be set. +6. Return the **service endpoint**. + +### Service Endpoint Expansion Algorithm + +The following process MUST be followed when expanding the endpoint from a service of type `RevocationBitmap2022`: + +1. Let **service endpoint** be a data url generated using the [service endpoint generation algorithm](#service-endpoint-generation-algorithm). +2. The `` of the **service endpoint** MUST be `application/octet-stream` and the `base64` attribute MUST be set, return an error otherwise. Let **compressed bitmap** be the `` part of the data url. +3. Generate an **uncompressed bitmap** by base64-decoding [[RFC4648](https://datatracker.ietf.org/doc/html/rfc4648)] the **compressed bitmap** and then decompressing the result using ZLIB [[RFC 1950](https://datatracker.ietf.org/doc/html/rfc1950)]. +4. Generate the **bitmap** by deserializing the **uncompressed bitmap** according to the [roaring bitmap serialization format](https://github.com/RoaringBitmap/RoaringFormatSpec/). +5. Return the **bitmap**. + +### Validation Algorithm + +The following steps MUST be followed when checking whether a verifiable credential is revoked: + +1. Let **credential** be a verifiable credential containing a `credentialStatus` whose `type` is `RevocationBitmap2022`. +2. Let **revocation bitmap URL** be the `id` field of the **credential**'s `credentialStatus`. +3. Resolve the **revocation bitmap URL** to a **revocation bitmap service** in the issuer's DID document, and verify that the service `type` is `RevocationBitmap2022`. Return an error otherwise. +4. Expand the endpoint of the **revocation bitmap service** into a **revocation bitmap** according to the [service endpoint expansion algorithm](#service-endpoint-expansion-algorithm). +5. Let **revocation index** be the integer value of the `revocationBitmapIndex` property contained in the `credentialStatus` of the **credential**. +6. Let **revoked** be the value of the bit at index **revocation index** in the **revocation bitmap**. +7. Return `true` if **revoked** is 1, `false` otherwise. + +## Test Vectors + +This section provides test vectors to validate implementations against. + +### Test Vector 1 + +The following data URL decodes to a bitmap of length 0 where no index is revoked: + +`"data:application/octet-stream;base64,ZUp5ek1tQUFBd0FES0FCcg=="` + +### Test Vector 2 + +The following data URL decodes to a bitmap of length 3 where indices `5`, `398`, and `67000` are revoked: + +`"data:application/octet-stream;base64,ZUp5ek1tQmdZR0lBQVVZZ1pHQ1FBR0laSUdabDZHUGN3UW9BRXVvQjlB"`. + +### Test Vector 3 + +The following data URL decodes to a bitmap of length 16384 where all indices are revoked: + +`"data:application/octet-stream;base64,ZUp6dHhERVJBQ0FNQkxESEFWS1lXZkN2Q3E0MmFESmtyMlNrM0ROckFLQ2RBQUFBQUFBQTMzbGhHZm9q"` + +## Rationale + +This section describes the rationale behind some of the design decisions of this specification. + +### Compression and maximum size + +Considering that messages published to the Tangle cannot exceed [32 KiB](https://github.com/iotaledger/tips/blob/main/tips/TIP-0006/tip-0006.md#message-validation) in size, and that larger messages have increased requirements, the use of compression was assessed. +The precise size of a serialized bitmap varies based on the number and distribution of revoked indices. When indices are revoked uniformly randomly, roughly 100,000 - 200,000 can be achieved in a DID Document with compression, and significantly more if consecutive indices are revoked. + +ZLIB [[RFC 1950](https://datatracker.ietf.org/doc/html/rfc1950)] was chosen for having a free and open source software licence and being one of the most widely used compression schemes, which enhances the accessibility of this specification. Some other assessed algorithms produced only marginally better compression ratios but had far fewer existing implementations across different programming languages. + +### Compressed Bitstring vs. Roaring Bitmap + +Because of its space efficiency, a roaring bitmap is preferred for representing a bitmap in-memory. To avoid the dependency on roaring bitmap, we considered using a compressed bitstring as the serialization format. However, serialization of such a bitstring was 2-3x slower compared to roaring's serialization format, which becomes an issue on resource-constrained devices (e.g. smartphones) or in web browsers. + +### Comparison to `RevocationList2020` and `StatusList2021` + +The [RevocationList2020 specification](https://w3c-ccg.github.io/vc-status-rl-2020/) and [StatusList2021 specification](https://w3c-ccg.github.io/vc-status-list-2021/) both describe a similar revocation mechanism using a verifiable credential that contains a bitmap, similar to the `RevocationBitmap2022` approach. The credential is hosted outside of the DID document and the verifier thus needs to fetch it from an external resource, likely one controlled by the issuer. This has privacy implications as the issuer can track where a fetch request for the credential came from and potentially infer who the credential was verified by and for what purpose. The issuer can also potentially infer which credential was checked. Because `RevocationBitmap2022` is embedded in the issuer's DID document, which can be obtained without the their knowledge, this approach does not suffer from these privacy shortcomings. See also the [privacy considerations](#privacy-considerations). + +A downside of embedding the revocation list in the DID document is that storage in a distributed ledger (DLT) is usually more expensive than other storage hosting solutions. The DLT might also impose message size limitations, capping the total number of revocations that can be done (see also [compression](#compression)). + +Another difference is that `RevocationList2020` specifies a minimum initial size of 131,072 for its bitstring, to mitigate the potential for correlating individuals when few credentials have been issued. `RevocationBitmap2022` uses a roaring bitmap instead of a bitstring, so the maximum size is not fixed (apart from the upper bound of an unsigned 32-bit integer). This means the bitmap cannot be used to correlate small populations without more information not present in the bitmap itself. However, both schemes still reveal publicly how many credentials have been revoked, which could be used to infer other information if more knowledge about how an issuer assigns credential revocation indexes is known. + +`StatusList2021` allows for explicitly stating the purpose of the list, currently either _revocation_ or _suspension_. This specification does not mandate that revoked credentials cannot be unrevoked, which means a `RevocationBitmap2022` can effectively also be used as a suspension list. + +### Privacy Considerations + +Because the revocation bitmap is embedded in the DID document, and thus available without contacting the issuer directly, the issuer cannot correlate how a holder uses their credential. + +An observer finding a service of type `RevocationBitmap2022` in a DID document can infer that this DID belongs to an issuer. However, DIDs of issuers tend to be publicly known, in contrast to DIDs of other entities, so this is unlikely to present an issue. External observers can monitor the frequency of revocations and potentially the total number of issued credentials, depending on how the issuer assigns credential indices (e.g. starting from 0 and incrementing the index for each issued credential). diff --git a/documentation/sidebars.js b/documentation/sidebars.js index bfa8588dae..416fd88e60 100644 --- a/documentation/sidebars.js +++ b/documentation/sidebars.js @@ -52,7 +52,7 @@ module.exports = { 'Verifiable Credentials': [ 'concepts/verifiable_credentials/overview', 'concepts/verifiable_credentials/create', - 'concepts/verifiable_credentials/revoke', + 'concepts/verifiable_credentials/revocation', 'concepts/verifiable_credentials/verifiable_presentations', ], 'Advanced Concepts': [ @@ -101,6 +101,7 @@ module.exports = { collapsed: true, items: [ 'specs/overview', + 'specs/revocation_bitmap_2022', { type: 'category', label: 'IOTA DID', diff --git a/examples/account/revoke_vc.rs b/examples/account/revoke_vc.rs index 14a574cd93..4518a9a98b 100644 --- a/examples/account/revoke_vc.rs +++ b/examples/account/revoke_vc.rs @@ -2,12 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 //! This example shows how to revoke a verifiable credential. +//! It demonstrates two methods for revocation. The first uses a revocation bitmap of type `RevocationBitmap2022`, +//! while the second method simply removes the verification method (public key) that signed the credential +//! from the DID Document of the issuer. //! -//! The Verifiable Credential is revoked by actually removing a verification method (public key) -//! from the DID Document of the Issuer. -//! As such, the Verifiable Credential can no longer be validated. -//! This would invalidate every Verifiable Credential signed with the same public key, therefore the -//! issuer would have to sign every VC with a different key. +//! Note that this example uses the "main" network, if you are writing code against the test network then most function +//! calls will need to include information about the network, since this is not automatically inferred from the +//! arguments in all cases currently. //! //! cargo run --example account_revoke_vc From f75f26e573d0c7bbccd69a990e1bcb79d5bdc270 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Ha=C3=9F?= Date: Tue, 21 Jun 2022 13:06:08 +0200 Subject: [PATCH 03/89] Remove localhost from link (#914) (#915) --- documentation/docs/introduction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/docs/introduction.md b/documentation/docs/introduction.md index 1b557241ad..b8efb342f4 100644 --- a/documentation/docs/introduction.md +++ b/documentation/docs/introduction.md @@ -34,7 +34,7 @@ Describes relevant concepts of SSI and how to utilize them in the library. Explains the DID standard from W3C and how to manipulate DID Documents. -### [Chapter 3.2: Verifiable Credentials (VC)](http://localhost:3000/identity.rs/concepts/verifiable_credentials/overview) +### [Chapter 3.2: Verifiable Credentials (VC)](./concepts/verifiable_credentials/overview) Explains the VC standard from W3C, how to create and revoke VCs, and how to use Verifiable Presentations. From e8502ce192ed6367f99e8db08455e2951cc14c56 Mon Sep 17 00:00:00 2001 From: "Oliver E. Anderson" Date: Wed, 22 Jun 2022 09:20:24 +0200 Subject: [PATCH 04/89] Avoid the include! macro (#917) * Replaced invocation of the include macro to instead deserialize a JSON representation --- identity_core/src/crypto/proof/jcs_ed25519.rs | 44 ++++++++++++------- .../fixtures/jcs_ed25519/test_vector_1.json | 18 ++++++++ .../test_vector_2.json} | 41 +++-------------- 3 files changed, 52 insertions(+), 51 deletions(-) create mode 100644 identity_core/tests/fixtures/jcs_ed25519/test_vector_1.json rename identity_core/tests/fixtures/{jcs_ed25519.rs => jcs_ed25519/test_vector_2.json} (59%) diff --git a/identity_core/src/crypto/proof/jcs_ed25519.rs b/identity_core/src/crypto/proof/jcs_ed25519.rs index a8299d35ca..b41d586c25 100644 --- a/identity_core/src/crypto/proof/jcs_ed25519.rs +++ b/identity_core/src/crypto/proof/jcs_ed25519.rs @@ -87,33 +87,43 @@ mod tests { use crate::crypto::Verifier as _; use crate::json; use crate::utils::BaseEncoding; + use serde::Deserialize; type Signer = JcsEd25519>; type Verifier = JcsEd25519>; - struct TestVector { - public: &'static str, - private: &'static str, - input: &'static str, - output: &'static str, - } - - const TVS: &[TestVector] = &include!("../../../tests/fixtures/jcs_ed25519.rs"); - #[test] fn test_tvs() { - for tv in TVS { - // The test vectors are from [JcsEd25519Signature2020](https://github.com/decentralized-identity/JcsEd25519Signature2020/tree/master/signature-suite-impls/test-vectors), - // and use [Go crypto/ed25519](https://pkg.go.dev/crypto/ed25519#pkg-types)'s convention of representing an Ed25519 private key as: 32-byte seed concatenated with the 32-byte public key (computed from the seed). + // Represents a test vector from [JcsEd25519Signature2020](https://github.com/decentralized-identity/JcsEd25519Signature2020/tree/master/signature-suite-impls/test-vectors), + #[derive(Deserialize)] + struct TestVector { + public: String, + private: String, + input: Object, + output: Object, + } + + const TV_1_BYTES: &[u8] = include_bytes!("../../../tests/fixtures/jcs_ed25519/test_vector_1.json"); + const TV_2_BYTES: &[u8] = include_bytes!("../../../tests/fixtures/jcs_ed25519/test_vector_1.json"); + const TEST_VECTOR_BYTES: [&[u8]; 2] = [TV_1_BYTES, TV_2_BYTES]; + + for tv_bytes in TEST_VECTOR_BYTES { + let TestVector { + public, + private, + input, + output, + } = TestVector::from_json_slice(tv_bytes).unwrap(); + + // The test vectors use [Go crypto/ed25519](https://pkg.go.dev/crypto/ed25519#pkg-types)'s convention of representing an Ed25519 private key as: 32-byte seed concatenated with the 32-byte public key (computed from the seed). // We follow the convention from [RFC 8032](https://datatracker.ietf.org/doc/html/rfc8032#section-3.2) and extract the 32-byte seed as the private key. - let public: PublicKey = BaseEncoding::decode_base58(tv.public).unwrap().into(); - let private: PrivateKey = (BaseEncoding::decode_base58(tv.private).unwrap()[..32]).to_vec().into(); + let public: PublicKey = BaseEncoding::decode_base58(public.as_str()).unwrap().into(); + let private: PrivateKey = (BaseEncoding::decode_base58(private.as_str()).unwrap()[..32]) + .to_vec() + .into(); let badkey: PublicKey = b"IOTA".to_vec().into(); - let input: Object = Object::from_json(tv.input).unwrap(); - let output: Object = Object::from_json(tv.output).unwrap(); - let signature: ProofValue = Signer::sign(&input, &private).unwrap(); assert_eq!(output["proof"]["signatureValue"], signature.as_str()); diff --git a/identity_core/tests/fixtures/jcs_ed25519/test_vector_1.json b/identity_core/tests/fixtures/jcs_ed25519/test_vector_1.json new file mode 100644 index 0000000000..7b7d3a4856 --- /dev/null +++ b/identity_core/tests/fixtures/jcs_ed25519/test_vector_1.json @@ -0,0 +1,18 @@ +{ + "public": "4CcKDtU1JNGi8U4D8Rv9CHzfmF7xzaxEAPFA54eQjRHF", + "private": "z3nisqMdwW7nZdWomCfUyRaezHzKEBfwRbvaMcJAqaMSbmjxuRfS5qz4ff3QAf1A5sZT4ZMcxYoGjN4K1VsDV7b", + "input": + { + "foo": "bar", + "proof": { + "type": "JcsEd25519Signature2020" + } + }, + "output": { + "foo": "bar", + "proof": { + "signatureValue": "4VCNeCSC4Daru6g7oij3QxUL2CS9FZkCYWRMUKyiLuPPK7GWFrM4YtYYQbmgyUXgGuxyKY5Wn1Mh4mmaRkbah4i4", + "type": "JcsEd25519Signature2020" + } + } +} \ No newline at end of file diff --git a/identity_core/tests/fixtures/jcs_ed25519.rs b/identity_core/tests/fixtures/jcs_ed25519/test_vector_2.json similarity index 59% rename from identity_core/tests/fixtures/jcs_ed25519.rs rename to identity_core/tests/fixtures/jcs_ed25519/test_vector_2.json index fe0045fe1b..174894178d 100644 --- a/identity_core/tests/fixtures/jcs_ed25519.rs +++ b/identity_core/tests/fixtures/jcs_ed25519/test_vector_2.json @@ -1,9 +1,7 @@ -[ - TestVector { - public: "4CcKDtU1JNGi8U4D8Rv9CHzfmF7xzaxEAPFA54eQjRHF", - private: "z3nisqMdwW7nZdWomCfUyRaezHzKEBfwRbvaMcJAqaMSbmjxuRfS5qz4ff3QAf1A5sZT4ZMcxYoGjN4K1VsDV7b", - input: r#" - { +{ + "public": "4CcKDtU1JNGi8U4D8Rv9CHzfmF7xzaxEAPFA54eQjRHF", + "private": "z3nisqMdwW7nZdWomCfUyRaezHzKEBfwRbvaMcJAqaMSbmjxuRfS5qz4ff3QAf1A5sZT4ZMcxYoGjN4K1VsDV7b", + "input": { "id": "did:example:abcd", "publicKey": [ { @@ -24,10 +22,8 @@ "proof": { "type": "JcsEd25519Signature2020" } - } - "#, - output: r#" - { + }, + "output": { "id": "did:example:abcd", "publicKey": [ { @@ -50,27 +46,4 @@ "type": "JcsEd25519Signature2020" } } - "#, - }, - TestVector { - public: "4CcKDtU1JNGi8U4D8Rv9CHzfmF7xzaxEAPFA54eQjRHF", - private: "z3nisqMdwW7nZdWomCfUyRaezHzKEBfwRbvaMcJAqaMSbmjxuRfS5qz4ff3QAf1A5sZT4ZMcxYoGjN4K1VsDV7b", - input: r#" - { - "foo": "bar", - "proof": { - "type": "JcsEd25519Signature2020" - } - } - "#, - output: r#" - { - "foo": "bar", - "proof": { - "signatureValue": "4VCNeCSC4Daru6g7oij3QxUL2CS9FZkCYWRMUKyiLuPPK7GWFrM4YtYYQbmgyUXgGuxyKY5Wn1Mh4mmaRkbah4i4", - "type": "JcsEd25519Signature2020" - } - } - "#, - } -] +} \ No newline at end of file From 622f8e6be494f0d6d73ab70a22dedec420eaa9e9 Mon Sep 17 00:00:00 2001 From: Philipp Date: Wed, 22 Jun 2022 17:26:51 +0300 Subject: [PATCH 05/89] Fix cargo check with `--no-default-features` (#916) * Fix feature-gate for `revocation-bitmap` * Fix `encryption` feature gated functions/types * Activate iota.rs `async` feature unconditionally * Fix feature flags in account * Move feature-gate account methods to module * Move revocation functions to module * Remove superfluous cfg-feature * Move memstore encryption into a module * Fix double cfg, improve feature-gating --- identity_account/Cargo.toml | 3 +- identity_account/src/account/account.rs | 198 +++++++++------- .../src/storage/memstore.rs | 214 +++++++++--------- .../src/storage/traits.rs | 3 + identity_iota/Cargo.toml | 5 +- identity_iota/src/lib.rs | 1 + identity_iota_client/Cargo.toml | 7 +- .../src/credential/credential_validator.rs | 22 +- .../src/document/iota_document.rs | 91 ++++---- 9 files changed, 299 insertions(+), 245 deletions(-) diff --git a/identity_account/Cargo.toml b/identity_account/Cargo.toml index ab2e02c986..15cba9bb11 100644 --- a/identity_account/Cargo.toml +++ b/identity_account/Cargo.toml @@ -30,10 +30,9 @@ rusty-fork = { version = "0.3" } tokio = { version = "1.17.0", default-features = false, features = ["macros", "rt", "rt-multi-thread", "sync"] } [features] -default = ["stronghold", "async", "send-sync-storage", "encryption", "revocation-bitmap"] +default = ["stronghold", "send-sync-storage", "encryption", "revocation-bitmap"] mem-client = [] stronghold = ["identity_account_storage/stronghold"] -async = ["identity_iota_client/async"] send-sync-storage = ["identity_account_storage/send-sync-storage"] # Enables encryption and decryption functionality. encryption = ["identity_account_storage/encryption"] diff --git a/identity_account/src/account/account.rs b/identity_account/src/account/account.rs index 5c16357dee..5f4a0f595b 100644 --- a/identity_account/src/account/account.rs +++ b/identity_account/src/account/account.rs @@ -11,18 +11,10 @@ use identity_account_storage::crypto::RemoteEd25519; use identity_account_storage::crypto::RemoteKey; use identity_account_storage::identity::ChainState; use identity_account_storage::storage::Storage; -#[cfg(feature = "encryption")] -use identity_account_storage::types::CekAlgorithm; -#[cfg(feature = "encryption")] -use identity_account_storage::types::EncryptedData; -#[cfg(feature = "encryption")] -use identity_account_storage::types::EncryptionAlgorithm; use identity_account_storage::types::KeyLocation; use identity_core::crypto::KeyType; use identity_core::crypto::ProofOptions; -use identity_core::crypto::PublicKey; use identity_core::crypto::SetSignature; -use identity_did::did::DID; use identity_iota_client::chain::DocumentChain; use identity_iota_client::document::ResolvedIotaDocument; use identity_iota_client::tangle::Client; @@ -316,84 +308,6 @@ where Ok(()) } - /// If the document has a [`RevocationBitmap`][identity_did::revocation::RevocationBitmap] service identified by - /// `fragment`, revoke all credentials with a `revocationBitmapIndex` in `credential_indices`. - pub async fn revoke_credentials(&mut self, fragment: &str, credential_indices: &[u32]) -> Result<()> { - // Find the service to be updated. - let mut service_id: IotaDIDUrl = self.did().to_url(); - service_id.set_fragment(Some(fragment))?; - - self.document.revoke_credentials(&service_id, credential_indices)?; - - self.increment_actions(); - self.publish_internal(false, PublishOptions::default()).await?; - Ok(()) - } - - /// If the document has a [`RevocationBitmap`][identity_did::revocation::RevocationBitmap] service identified by - /// `fragment`, unrevoke all credentials with a `revocationBitmapIndex` in `credential_indices`. - pub async fn unrevoke_credentials(&mut self, fragment: &str, credential_indices: &[u32]) -> Result<()> { - // Find the service to be updated. - let mut service_id: IotaDIDUrl = self.did().to_url(); - service_id.set_fragment(Some(fragment))?; - - self.document.unrevoke_credentials(&service_id, credential_indices)?; - - self.increment_actions(); - self.publish_internal(false, PublishOptions::default()).await?; - Ok(()) - } - - /// Encrypts the given `plaintext` with the specified `encryption_algorithm` and `cek_algorithm`. - /// - /// Returns an [`EncryptedData`] instance. - #[cfg(feature = "encryption")] - pub async fn encrypt_data( - &self, - plaintext: &[u8], - associated_data: &[u8], - encryption_algorithm: &EncryptionAlgorithm, - cek_algorithm: &CekAlgorithm, - public_key: PublicKey, - ) -> Result { - self - .storage() - .data_encrypt( - self.did(), - plaintext.to_vec(), - associated_data.to_vec(), - encryption_algorithm, - cek_algorithm, - public_key, - ) - .await - .map_err(Into::into) - } - - /// Decrypts the given `data` with the key identified by `fragment` using the given `encryption_algorithm` and - /// `cek_algorithm`. - /// - /// Returns the decrypted text. - #[cfg(feature = "encryption")] - pub async fn decrypt_data( - &self, - data: EncryptedData, - encryption_algorithm: &EncryptionAlgorithm, - cek_algorithm: &CekAlgorithm, - fragment: &str, - ) -> Result> { - let method: &IotaVerificationMethod = self - .document() - .resolve_method(fragment, None) - .ok_or(Error::DIDError(identity_did::Error::MethodNotFound))?; - let private_key: KeyLocation = KeyLocation::from_verification_method(method)?; - self - .storage() - .data_decrypt(self.did(), data, encryption_algorithm, cek_algorithm, &private_key) - .await - .map_err(Into::into) - } - // =========================================================================== // Misc. Private // =========================================================================== @@ -636,3 +550,115 @@ where Ok(()) } } + +#[cfg(feature = "revocation-bitmap")] +mod account_revocation { + use super::Account; + use crate::account::PublishOptions; + use crate::Result; + use identity_did::did::DID; + use identity_iota_client::tangle::Client; + use identity_iota_client::tangle::SharedPtr; + use identity_iota_core::did::IotaDIDUrl; + + impl Account + where + C: SharedPtr, + { + /// If the document has a [`RevocationBitmap`][identity_did::revocation::RevocationBitmap] service identified by + /// `fragment`, revoke all credentials with a `revocationBitmapIndex` in `credential_indices`. + pub async fn revoke_credentials(&mut self, fragment: &str, credential_indices: &[u32]) -> Result<()> { + // Find the service to be updated. + let mut service_id: IotaDIDUrl = self.did().to_url(); + service_id.set_fragment(Some(fragment))?; + + self.document.revoke_credentials(&service_id, credential_indices)?; + + self.increment_actions(); + self.publish_internal(false, PublishOptions::default()).await?; + Ok(()) + } + + /// If the document has a [`RevocationBitmap`][identity_did::revocation::RevocationBitmap] service identified by + /// `fragment`, unrevoke all credentials with a `revocationBitmapIndex` in `credential_indices`. + pub async fn unrevoke_credentials(&mut self, fragment: &str, credential_indices: &[u32]) -> Result<()> { + // Find the service to be updated. + let mut service_id: IotaDIDUrl = self.did().to_url(); + service_id.set_fragment(Some(fragment))?; + + self.document.unrevoke_credentials(&service_id, credential_indices)?; + + self.increment_actions(); + self.publish_internal(false, PublishOptions::default()).await?; + Ok(()) + } + } +} + +#[cfg(feature = "encryption")] +mod account_encryption { + use super::Account; + use crate::Error; + use crate::Result; + use identity_account_storage::types::CekAlgorithm; + use identity_account_storage::types::EncryptedData; + use identity_account_storage::types::EncryptionAlgorithm; + use identity_account_storage::types::KeyLocation; + use identity_core::crypto::PublicKey; + use identity_iota_client::tangle::Client; + use identity_iota_client::tangle::SharedPtr; + use identity_iota_core::document::IotaVerificationMethod; + + impl Account + where + C: SharedPtr, + { + /// Encrypts the given `plaintext` with the specified `encryption_algorithm` and `cek_algorithm`. + /// + /// Returns an [`EncryptedData`] instance. + pub async fn encrypt_data( + &self, + plaintext: &[u8], + associated_data: &[u8], + encryption_algorithm: &EncryptionAlgorithm, + cek_algorithm: &CekAlgorithm, + public_key: PublicKey, + ) -> Result { + self + .storage() + .data_encrypt( + self.did(), + plaintext.to_vec(), + associated_data.to_vec(), + encryption_algorithm, + cek_algorithm, + public_key, + ) + .await + .map_err(Into::into) + } + + /// Decrypts the given `data` with the key identified by `fragment` using the given `encryption_algorithm` and + /// `cek_algorithm`. + /// + /// Returns the decrypted text. + pub async fn decrypt_data( + &self, + data: EncryptedData, + encryption_algorithm: &EncryptionAlgorithm, + cek_algorithm: &CekAlgorithm, + fragment: &str, + ) -> Result> { + let method: &IotaVerificationMethod = self + .document() + .resolve_method(fragment, None) + .ok_or(Error::DIDError(identity_did::Error::MethodNotFound))?; + let private_key: KeyLocation = KeyLocation::from_verification_method(method)?; + self + .storage() + .data_decrypt(self.did(), data, encryption_algorithm, cek_algorithm, &private_key) + .await + .map_err(Into::into) + } + } +} diff --git a/identity_account_storage/src/storage/memstore.rs b/identity_account_storage/src/storage/memstore.rs index 07ab5a0952..b404830e36 100644 --- a/identity_account_storage/src/storage/memstore.rs +++ b/identity_account_storage/src/storage/memstore.rs @@ -1,15 +1,16 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use core::convert::TryFrom; use core::fmt::Debug; use core::fmt::Formatter; use async_trait::async_trait; +#[cfg(feature = "encryption")] use crypto::ciphers::aes::Aes256Gcm; +#[cfg(feature = "encryption")] +use crypto::ciphers::aes_kw::Aes256Kw; +#[cfg(feature = "encryption")] use crypto::ciphers::traits::Aead; -use crypto::hashes::sha::Sha256; -use crypto::hashes::Digest; use hashbrown::HashMap; use identity_core::crypto::Ed25519; use identity_core::crypto::KeyPair; @@ -17,6 +18,7 @@ use identity_core::crypto::KeyType; use identity_core::crypto::PrivateKey; use identity_core::crypto::PublicKey; use identity_core::crypto::Sign; +#[cfg(feature = "encryption")] use identity_core::crypto::X25519; use identity_iota_core::did::IotaDID; use identity_iota_core::document::IotaDocument; @@ -30,8 +32,6 @@ use crate::error::Result; use crate::identity::ChainState; use crate::storage::Storage; #[cfg(feature = "encryption")] -use crate::types::AgreementInfo; -#[cfg(feature = "encryption")] use crate::types::CekAlgorithm; #[cfg(feature = "encryption")] use crate::types::EncryptedData; @@ -40,8 +40,6 @@ use crate::types::EncryptionAlgorithm; use crate::types::KeyLocation; use crate::types::Signature; use crate::utils::Shared; -#[cfg(feature = "encryption")] -use crypto::ciphers::aes_kw::Aes256Kw; // The map from DIDs to chain states. type ChainStates = HashMap; @@ -282,9 +280,9 @@ impl Storage for MemStore { // Obtain the shared secret by combining the ephemeral key and the static public key let shared_secret: [u8; 32] = X25519::key_exchange(keypair.private(), &public_key)?; let derived_secret: Vec = - concat_kdf(cek_algorithm.name(), Aes256Gcm::KEY_LENGTH, &shared_secret, agreement) + memstore_encryption::concat_kdf(cek_algorithm.name(), Aes256Gcm::KEY_LENGTH, &shared_secret, agreement) .map_err(Error::EncryptionFailure)?; - let encrypted_data = try_encrypt( + let encrypted_data = memstore_encryption::try_encrypt( &derived_secret, encryption_algorithm, &plaintext, @@ -297,10 +295,11 @@ impl Storage for MemStore { CekAlgorithm::ECDH_ES_A256KW(agreement) => { let keypair: KeyPair = KeyPair::new(KeyType::X25519)?; let shared_secret: [u8; 32] = X25519::key_exchange(keypair.private(), &public_key)?; - let derived_secret: Vec = concat_kdf(cek_algorithm.name(), Aes256Kw::KEY_LENGTH, &shared_secret, agreement) - .map_err(Error::EncryptionFailure)?; + let derived_secret: Vec = + memstore_encryption::concat_kdf(cek_algorithm.name(), Aes256Kw::KEY_LENGTH, &shared_secret, agreement) + .map_err(Error::EncryptionFailure)?; - let cek: Vec = generate_content_encryption_key(*encryption_algorithm)?; + let cek: Vec = memstore_encryption::generate_content_encryption_key(*encryption_algorithm)?; let mut encrypted_cek: Vec = vec![0; cek.len() + Aes256Kw::BLOCK]; let aes_kw: Aes256Kw<'_> = Aes256Kw::new(derived_secret.as_ref()); @@ -308,7 +307,7 @@ impl Storage for MemStore { .wrap_key(cek.as_ref(), &mut encrypted_cek) .map_err(Error::EncryptionFailure)?; - let encrypted_data = try_encrypt( + let encrypted_data = memstore_encryption::try_encrypt( &cek, encryption_algorithm, &plaintext, @@ -348,14 +347,14 @@ impl Storage for MemStore { CekAlgorithm::ECDH_ES(agreement) => { let shared_secret: [u8; 32] = X25519::key_exchange(key_pair.private(), &public_key)?; let derived_secret: Vec = - concat_kdf(cek_algorithm.name(), Aes256Gcm::KEY_LENGTH, &shared_secret, agreement) + memstore_encryption::concat_kdf(cek_algorithm.name(), Aes256Gcm::KEY_LENGTH, &shared_secret, agreement) .map_err(Error::DecryptionFailure)?; - try_decrypt(&derived_secret, encryption_algorithm, &data) + memstore_encryption::try_decrypt(&derived_secret, encryption_algorithm, &data) } CekAlgorithm::ECDH_ES_A256KW(agreement) => { let shared_secret: [u8; 32] = X25519::key_exchange(key_pair.private(), &public_key)?; let derived_secret: Vec = - concat_kdf(cek_algorithm.name(), Aes256Kw::KEY_LENGTH, &shared_secret, agreement) + memstore_encryption::concat_kdf(cek_algorithm.name(), Aes256Kw::KEY_LENGTH, &shared_secret, agreement) .map_err(Error::DecryptionFailure)?; let cek_len: usize = @@ -375,7 +374,7 @@ impl Storage for MemStore { .unwrap_key(data.encrypted_cek.as_ref(), &mut cek) .map_err(Error::DecryptionFailure)?; - try_decrypt(&cek, encryption_algorithm, &data) + memstore_encryption::try_decrypt(&cek, encryption_algorithm, &data) } } } @@ -413,100 +412,120 @@ impl Storage for MemStore { } } -fn try_encrypt( - key: &[u8], - algorithm: &EncryptionAlgorithm, - data: &[u8], - associated_data: Vec, - encrypted_cek: Vec, - ephemeral_public_key: Vec, -) -> Result { - match algorithm { - EncryptionAlgorithm::AES256GCM => { - let nonce: &[u8] = &Aes256Gcm::random_nonce().map_err(Error::EncryptionFailure)?; - let padding: usize = Aes256Gcm::padsize(data).map(|size| size.get()).unwrap_or_default(); - let mut ciphertext: Vec = vec![0; data.len() + padding]; - let mut tag: Vec = [0; Aes256Gcm::TAG_LENGTH].to_vec(); - Aes256Gcm::try_encrypt(key, nonce, associated_data.as_ref(), data, &mut ciphertext, &mut tag) - .map_err(Error::EncryptionFailure)?; - Ok(EncryptedData::new( - nonce.to_vec(), - associated_data, - tag, - ciphertext, - encrypted_cek, - ephemeral_public_key, - )) +#[cfg(feature = "encryption")] +mod memstore_encryption { + use crate::types::AgreementInfo; + use crate::types::EncryptedData; + use crate::types::EncryptionAlgorithm; + use crate::Error; + use crate::Result; + use crypto::ciphers::aes::Aes256Gcm; + use crypto::ciphers::traits::Aead; + use crypto::hashes::sha::Sha256; + use crypto::hashes::Digest; + + pub(crate) fn try_encrypt( + key: &[u8], + algorithm: &EncryptionAlgorithm, + data: &[u8], + associated_data: Vec, + encrypted_cek: Vec, + ephemeral_public_key: Vec, + ) -> Result { + match algorithm { + EncryptionAlgorithm::AES256GCM => { + let nonce: &[u8] = &Aes256Gcm::random_nonce().map_err(Error::EncryptionFailure)?; + let padding: usize = Aes256Gcm::padsize(data).map(|size| size.get()).unwrap_or_default(); + let mut ciphertext: Vec = vec![0; data.len() + padding]; + let mut tag: Vec = [0; Aes256Gcm::TAG_LENGTH].to_vec(); + Aes256Gcm::try_encrypt(key, nonce, associated_data.as_ref(), data, &mut ciphertext, &mut tag) + .map_err(Error::EncryptionFailure)?; + Ok(EncryptedData::new( + nonce.to_vec(), + associated_data, + tag, + ciphertext, + encrypted_cek, + ephemeral_public_key, + )) + } } } -} -fn try_decrypt(key: &[u8], algorithm: &EncryptionAlgorithm, data: &EncryptedData) -> Result> { - match algorithm { - EncryptionAlgorithm::AES256GCM => { - let mut plaintext = vec![0; data.ciphertext.len()]; - let len: usize = Aes256Gcm::try_decrypt( - key, - &data.nonce, - &data.associated_data, - &mut plaintext, - &data.ciphertext, - &data.tag, - ) - .map_err(Error::DecryptionFailure)?; - plaintext.truncate(len); - Ok(plaintext) + pub(crate) fn try_decrypt(key: &[u8], algorithm: &EncryptionAlgorithm, data: &EncryptedData) -> Result> { + match algorithm { + EncryptionAlgorithm::AES256GCM => { + let mut plaintext = vec![0; data.ciphertext.len()]; + let len: usize = Aes256Gcm::try_decrypt( + key, + &data.nonce, + &data.associated_data, + &mut plaintext, + &data.ciphertext, + &data.tag, + ) + .map_err(Error::DecryptionFailure)?; + plaintext.truncate(len); + Ok(plaintext) + } } } -} -/// The Concat KDF (using SHA-256) as defined in Section 5.8.1 of NIST.800-56A -fn concat_kdf( - alg: &'static str, - len: usize, - shared_secret: &[u8], - agreement: &AgreementInfo, -) -> crypto::error::Result> { - let mut digest: Sha256 = Sha256::new(); - let mut output: Vec = Vec::new(); + /// The Concat KDF (using SHA-256) as defined in Section 5.8.1 of NIST.800-56A + pub(crate) fn concat_kdf( + alg: &'static str, + len: usize, + shared_secret: &[u8], + agreement: &AgreementInfo, + ) -> crypto::error::Result> { + let mut digest: Sha256 = Sha256::new(); + let mut output: Vec = Vec::new(); - let target: usize = (len + (Sha256::output_size() - 1)) / Sha256::output_size(); - let rounds: u32 = u32::try_from(target).map_err(|_| crypto::error::Error::InvalidArgumentError { - alg, - expected: "iterations can't exceed 2^32 - 1", - })?; + let target: usize = (len + (Sha256::output_size() - 1)) / Sha256::output_size(); + let rounds: u32 = u32::try_from(target).map_err(|_| crypto::error::Error::InvalidArgumentError { + alg, + expected: "iterations can't exceed 2^32 - 1", + })?; - for count in 0..rounds { - // Iteration Count - digest.update(&(count as u32 + 1).to_be_bytes()); + for count in 0..rounds { + // Iteration Count + digest.update(&(count as u32 + 1).to_be_bytes()); - // Derived Secret - digest.update(shared_secret); + // Derived Secret + digest.update(shared_secret); - // AlgorithmId - digest.update(&(alg.len() as u32).to_be_bytes()); - digest.update(alg.as_bytes()); + // AlgorithmId + digest.update(&(alg.len() as u32).to_be_bytes()); + digest.update(alg.as_bytes()); - // PartyUInfo - digest.update(&(agreement.apu.len() as u32).to_be_bytes()); - digest.update(&agreement.apu); + // PartyUInfo + digest.update(&(agreement.apu.len() as u32).to_be_bytes()); + digest.update(&agreement.apu); - // PartyVInfo - digest.update(&(agreement.apv.len() as u32).to_be_bytes()); - digest.update(&agreement.apv); + // PartyVInfo + digest.update(&(agreement.apv.len() as u32).to_be_bytes()); + digest.update(&agreement.apv); - // SuppPubInfo - digest.update(&agreement.pub_info); + // SuppPubInfo + digest.update(&agreement.pub_info); - // SuppPrivInfo - digest.update(&agreement.priv_info); + // SuppPrivInfo + digest.update(&agreement.priv_info); - output.extend_from_slice(&digest.finalize_reset()); - } + output.extend_from_slice(&digest.finalize_reset()); + } - output.truncate(len); + output.truncate(len); + + Ok(output) + } - Ok(output) + /// Generate a random content encryption key of suitable length for `encryption_algorithm`. + pub(crate) fn generate_content_encryption_key(encryption_algorithm: EncryptionAlgorithm) -> Result> { + let mut bytes: Vec = vec![0; encryption_algorithm.key_length()]; + crypto::utils::rand::fill(bytes.as_mut()).map_err(Error::EncryptionFailure)?; + Ok(bytes) + } } impl Debug for MemStore { @@ -529,13 +548,6 @@ impl Default for MemStore { } } -/// Generate a random content encryption key of suitable length for `encryption_algorithm`. -fn generate_content_encryption_key(encryption_algorithm: EncryptionAlgorithm) -> Result> { - let mut bytes: Vec = vec![0; encryption_algorithm.key_length()]; - crypto::utils::rand::fill(bytes.as_mut()).map_err(Error::EncryptionFailure)?; - Ok(bytes) -} - #[cfg(test)] mod tests { use crate::storage::Storage; diff --git a/identity_account_storage/src/storage/traits.rs b/identity_account_storage/src/storage/traits.rs index 51f9d5f722..7816ce1e3a 100644 --- a/identity_account_storage/src/storage/traits.rs +++ b/identity_account_storage/src/storage/traits.rs @@ -14,8 +14,11 @@ use identity_iota_core::tangle::NetworkName; use crate::error::Result; use crate::identity::ChainState; +#[cfg(feature = "encryption")] use crate::types::CekAlgorithm; +#[cfg(feature = "encryption")] use crate::types::EncryptedData; +#[cfg(feature = "encryption")] use crate::types::EncryptionAlgorithm; use crate::types::KeyLocation; use crate::types::Signature; diff --git a/identity_iota/Cargo.toml b/identity_iota/Cargo.toml index 358689f238..4c7e852a06 100644 --- a/identity_iota/Cargo.toml +++ b/identity_iota/Cargo.toml @@ -31,10 +31,7 @@ name = "benchmark" harness = false [features] -default = ["async", "account", "stronghold", "send-sync-storage", "unstable-encryption", "revocation-bitmap"] - -# Enables async runtime support (Tokio). -async = ["identity_iota_client/async"] +default = ["account", "stronghold", "send-sync-storage", "unstable-encryption", "revocation-bitmap"] # Enables support for secure storage of DID Documents account = ["identity_account", "identity_account_storage"] diff --git a/identity_iota/src/lib.rs b/identity_iota/src/lib.rs index 3284fd0721..44ace7ad22 100644 --- a/identity_iota/src/lib.rs +++ b/identity_iota/src/lib.rs @@ -56,6 +56,7 @@ pub mod did { pub use identity_did::document::*; pub use identity_did::error::*; + #[cfg(feature = "revocation-bitmap")] pub use identity_did::revocation::*; pub use identity_did::service::*; pub use identity_did::utils::*; diff --git a/identity_iota_client/Cargo.toml b/identity_iota_client/Cargo.toml index e4df9e9a58..de4826834b 100644 --- a/identity_iota_client/Cargo.toml +++ b/identity_iota_client/Cargo.toml @@ -32,7 +32,7 @@ thiserror = { version = "1.0", default-features = false } [dependencies.iota-client] version = "1.2.0" -features = ["tls"] +features = ["async", "tls"] default-features = false [target.'cfg(all(target_arch = "wasm32", not(target_os = "wasi")))'.dependencies.iota-client] @@ -50,10 +50,7 @@ proptest = { version = "1.0.0", default-features = false, features = ["std"] } tokio = { version = "1.17.0", default-features = false, features = ["macros"] } [features] -default = ["async", "revocation-bitmap"] - -# Enables async runtime support (Tokio) -async = ["iota-client/async"] +default = ["revocation-bitmap"] # Enables revocation with `RevocationBitmap2022`. revocation-bitmap = ["identity_iota_core/revocation-bitmap"] diff --git a/identity_iota_client/src/credential/credential_validator.rs b/identity_iota_client/src/credential/credential_validator.rs index 830256e698..e6bda891db 100644 --- a/identity_iota_client/src/credential/credential_validator.rs +++ b/identity_iota_client/src/credential/credential_validator.rs @@ -7,10 +7,13 @@ use identity_core::common::OneOrMany; use identity_core::common::Timestamp; use identity_core::common::Url; use identity_credential::credential::Credential; +#[cfg(feature = "revocation-bitmap")] use identity_credential::credential::RevocationBitmapStatus; +#[cfg(feature = "revocation-bitmap")] use identity_did::revocation::RevocationBitmap; use identity_did::verifiable::VerifierOptions; use identity_iota_core::did::IotaDID; +#[cfg(feature = "revocation-bitmap")] use identity_iota_core::did::IotaDIDUrl; use identity_iota_core::document::IotaDocument; @@ -19,6 +22,7 @@ use crate::Result; use super::errors::CompoundCredentialValidationError; use super::errors::SignerContext; use super::errors::ValidationError; +#[cfg(feature = "revocation-bitmap")] use super::validation_options::StatusCheck; use super::CredentialValidationOptions; use super::FailFast; @@ -179,6 +183,7 @@ impl CredentialValidator { /// Checks whether the credential status has been revoked. /// /// Only supports `BitmapRevocation2022`. + #[cfg(feature = "revocation-bitmap")] pub fn check_status>( credential: &Credential, trusted_issuers: &[D], @@ -220,6 +225,7 @@ impl CredentialValidator { /// Check the given `status` against the matching [`RevocationBitmap`] service in the /// issuer's DID Document. + #[cfg(feature = "revocation-bitmap")] fn check_revocation_bitmap_status>( issuer: D, status: RevocationBitmapStatus, @@ -276,15 +282,19 @@ impl CredentialValidator { .unwrap_or(Ok(())) }); - let revocation_validation = std::iter::once_with(|| Self::check_status(credential, issuers, options.status)); - - let validation_units_error_iter = issuance_date_validation + let validation_units_iter = issuance_date_validation .chain(expiry_date_validation) .chain(structure_validation) .chain(subject_holder_validation) - .chain(signature_validation) - .chain(revocation_validation) - .filter_map(|result| result.err()); + .chain(signature_validation); + + #[cfg(feature = "revocation-bitmap")] + let validation_units_iter = { + let revocation_validation = std::iter::once_with(|| Self::check_status(credential, issuers, options.status)); + validation_units_iter.chain(revocation_validation) + }; + + let validation_units_error_iter = validation_units_iter.filter_map(|result| result.err()); let validation_errors: Vec = match fail_fast { FailFast::FirstError => validation_units_error_iter.take(1).collect(), FailFast::AllErrors => validation_units_error_iter.collect(), diff --git a/identity_iota_core/src/document/iota_document.rs b/identity_iota_core/src/document/iota_document.rs index 898efa4089..3bff1f6722 100644 --- a/identity_iota_core/src/document/iota_document.rs +++ b/identity_iota_core/src/document/iota_document.rs @@ -22,8 +22,6 @@ use identity_core::crypto::PublicKey; use identity_core::crypto::SetSignature; use identity_core::crypto::Signer; use identity_did::document::CoreDocument; -#[cfg(feature = "revocation-bitmap")] -use identity_did::revocation::RevocationBitmap; use identity_did::service::Service; use identity_did::utils::DIDUrlQuery; use identity_did::verifiable::DocumentSigner; @@ -612,52 +610,62 @@ impl IotaDocument { }) .collect() } +} - /// If the document has a [`RevocationBitmap`] service identified by `fragment`, - /// revoke all credentials with a `revocationBitmapIndex` in `credential_indices`. - #[cfg(feature = "revocation-bitmap")] - pub fn revoke_credentials(&mut self, service_id: &IotaDIDUrl, credential_indices: &[u32]) -> Result<()> { - self.update_revocation_bitmap(service_id, |revocation_bitmap| { - // Revoke all given credential indices. - for credential in credential_indices { - revocation_bitmap.revoke(*credential); - } - }) - } +#[cfg(feature = "revocation-bitmap")] +mod iota_document_revocation { + use super::IotaDocument; + use crate::did::IotaDID; + use crate::did::IotaDIDUrl; + use crate::Error; + use crate::Result; + use identity_did::revocation::RevocationBitmap; + use identity_did::service::Service; + + impl IotaDocument { + /// If the document has a [`RevocationBitmap`] service identified by `fragment`, + /// revoke all credentials with a `revocationBitmapIndex` in `credential_indices`. + pub fn revoke_credentials(&mut self, service_id: &IotaDIDUrl, credential_indices: &[u32]) -> Result<()> { + self.update_revocation_bitmap(service_id, |revocation_bitmap| { + // Revoke all given credential indices. + for credential in credential_indices { + revocation_bitmap.revoke(*credential); + } + }) + } - /// If the document has a [`RevocationBitmap`] service identified by `fragment`, - /// unrevoke all credentials with a `revocationBitmapIndex` in `credential_indices`. - #[cfg(feature = "revocation-bitmap")] - pub fn unrevoke_credentials(&mut self, service_id: &IotaDIDUrl, credential_indices: &[u32]) -> Result<()> { - self.update_revocation_bitmap(service_id, |revocation_bitmap| { - // Unrevoke all given credential indices. - for credential in credential_indices { - revocation_bitmap.unrevoke(*credential); - } - }) - } + /// If the document has a [`RevocationBitmap`] service identified by `fragment`, + /// unrevoke all credentials with a `revocationBitmapIndex` in `credential_indices`. + pub fn unrevoke_credentials(&mut self, service_id: &IotaDIDUrl, credential_indices: &[u32]) -> Result<()> { + self.update_revocation_bitmap(service_id, |revocation_bitmap| { + // Unrevoke all given credential indices. + for credential in credential_indices { + revocation_bitmap.unrevoke(*credential); + } + }) + } - #[cfg(feature = "revocation-bitmap")] - fn update_revocation_bitmap(&mut self, service_id: &IotaDIDUrl, f: F) -> Result<()> - where - F: FnOnce(&mut RevocationBitmap), - { - let service: &mut Service = self - .core_document_mut() - .service_mut() - .iter_mut_unchecked() - .find(|service| service.id() == service_id) - .ok_or(Error::RevocationError(identity_did::Error::InvalidService( - "invalid id - service not found", - )))?; + fn update_revocation_bitmap(&mut self, service_id: &IotaDIDUrl, f: F) -> Result<()> + where + F: FnOnce(&mut RevocationBitmap), + { + let service: &mut Service = self + .core_document_mut() + .service_mut() + .iter_mut_unchecked() + .find(|service| service.id() == service_id) + .ok_or(Error::RevocationError(identity_did::Error::InvalidService( + "invalid id - service not found", + )))?; - let mut revocation_bitmap: RevocationBitmap = (&*service).try_into().map_err(Error::RevocationError)?; + let mut revocation_bitmap: RevocationBitmap = (&*service).try_into().map_err(Error::RevocationError)?; - f(&mut revocation_bitmap); + f(&mut revocation_bitmap); - std::mem::swap(service.service_endpoint_mut(), &mut revocation_bitmap.to_endpoint()?); + std::mem::swap(service.service_endpoint_mut(), &mut revocation_bitmap.to_endpoint()?); - Ok(()) + Ok(()) + } } } @@ -725,6 +733,7 @@ mod tests { use identity_core::crypto::KeyType; use identity_core::utils::BaseEncoding; use identity_did::did::DID; + use identity_did::revocation::RevocationBitmap; use identity_did::verifiable::VerifiableProperties; use identity_did::verification::MethodData; From 55af4069961683601a3ed13b2fdb751e1e1b619e Mon Sep 17 00:00:00 2001 From: Philipp Date: Thu, 23 Jun 2022 14:29:49 +0300 Subject: [PATCH 06/89] Implement the Identity Agent (#322) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * actor: Add builder for `Communicator` * identity: Re-export actor types * actor: Specify `tokio` dep explicitly via git * actor: Specify `tokio` via crates.io * actor: Rename communicator module to actor * actor: Rename communicator structs to actor * actor: Rename `register_command` to `set_handler` * actor: Spawn handler loop internally * actor: Impl dispatch pattern for handler loop * actor: Trait to determine `send_request` ret type * actor: Get request_name based on type parameter * actor: Remove `Identity` prefixes in type names * actor: Finer-grained handler impl for a nicer API * actor: Migrate to new stronghold comms version * actor: Fix account feature conditional guards * actor: Use type map to allow reusing receiver * actor: Update stronghold comms to new version * actor: Impl async handler functions * actor: Separate storage handler & types * actor: Rename StorageHandler & impl resolution * actor: Reexport Keypair * actor: Expand Cargo package info * actor: Only spawn listener if there are addresses * actor: Upgrade stronghold comms lib version * actor: Add dummy comm handler * actor: Expose executor builder API * actor: Impl ffi-compatible handler registration * actor: Use closures instead of function pointers Signed-off-by: PhilippGackstatter * actor: Let request_name take &self for bindings * actor: Use `Cow` in trait for more flexibility * actor: Use `log` instead of println * actor: Implement `send_named_request` for bindings * actor: Use `ClientMap` for working did resolution * actor: Upgrade to latest stronghold p2p * actor: Impl remote error and deserialization logic * actor: Restructure tests, add SendError test * actor: Use new `StorageError` * actor: Handle all errors in handle invoker task * actor: Define separate request & response types * actor: Expand `StorageError` & impl `Category` * Add licences to all files * Apply cargo and toml formatting * Address clippy lints * Define a type name for the more complex types * Update to latest stronghold p2p version * Let `add_method` take an `async fn` directly * Use consistent names for the generics * Implement `Endpoint` and catch-all handler * Make `Actor` cloneable, inject it into handlers * Inject `PeerId` into every handler * Impl `RequestContext` & dummy didcomm protocol * Add test for other presentation direction * Rename handler registration methods * Refactor handler invocation in prep for hooks * Implement basic `call_hook` method * Update account usages to latest dev * Remove memory leak / circular reference * Impl part of a hook test * Fully impl hook invocation in send_request * Apply new rustfmt granularity * Restructure actor crate * Restructure didcomm parts * Add error handling in `RequestHandler::invoke` * Fix response serialization * Impl hooks for `await_message` * Add await_message hook error test * Migrate to latest dev * Reduce code duplication for handler invocation * Replace `stronghold-p2p` with only `libp2p` * Impl working serialization; add invocation test * Return `RemoteSendError` in `Actor::send_request` * Partially implement didcomm threads * Impl thread routing * Reimplement implicit hooks * Add `StopListening` command * Refactor handler invocation fns * Address clippy lints * Shrink actor state to make cloning cheaper * Improve type safety of `HandlerBuilder` * Store `peer_id` in `ActorState` * Remove one superfluous tokio task and channel pair * Map errors and document `ActorBuilder` * Polish errors * Update copyright to 2022 * Replace `DidCommRequest` with `RequestMessage` * Move message types to p2p module * Match more efficiently on swarm events * Remove `SwarmCommand::GetPeerId` * Return inbound failure when sending response * Rename shutdown method * Remove send_request functions for now * Reorganize crate * Remove `DidCommActor` * Factor out "default" `RequestHandler` impls * Spell out individual exports * Rename `AsyncFn` -> `Handler` * Rename `DidCommHook` -> `Hook` * Move `ActorRequest` to its own module * Reogranize dependencies * Better test name * Implement `RequestMode`, partially * Implement (a)sync `RequestMode` using strategies * Make `InvocationStrategy` methods static * Add `Actor::send_request` * Do not send DCPM in sync mode * Make `ActorRequest` generic over `SyncMode` * Redo the serialization errors (partial) * Finish serialization error refactoring * Add timeout error * Make downcasting & cloning objects fallible * Introduce `HandlerObject` type for readability * Restrict `Endpoint`s to ascii alphabetic and `_` * Fix `Actor::start_listening` * Drain open channels during shutdown * Add `ActorConfig` and timeout * Reorganize imports * Be more specific for non-existent threads * Fix logger in tests and listening * Add actor feature in identity crate * Impl working test remote account * Test thread not found error for async messages * Remove `RequestHandler::object_type_id` (unused) * Inline `InvocationStrategy` methods * Abort request handling on error in async strategy * Impl `FromStr` for `Endpoint` * Only allow handler modification during build phase * Add remote account benchmark * Document public types (partial) * Remove unused errors; remove `Category` * Document the rest * Make p2p module exports explicit * Make remote_account exports explicit * Remove exports from remote_account * Remove commented code in didcomm presentation * Rm unnecessary bounds on `ActorRequest` generics * Test various error scenarios * Remove start_listening test b/c non-deterministic * Add handler finishes after shutdown test * Rename `ActorRequest::request_name` -> `endpoint` * Reduce p2p module visibility * Make `Actor::peer_id` just `&self` * Impl `add_addresses` for improved test reliability * Increase timeout to increase test reliability * Specialize `add_handler` for synchronicity * Fix documentation for `add_handler` functions * Use minor version; use default-features = false * Return `Error::Shutdown` instead of panicking * Document more on the `Actor` type * Move bounds onto `InvocationStrategy` trait * Test subset of serialization errors * Don't require absolute latest tokio version * Bump `libp2p` to `0.43` * Feature gate `ActorBuilder::build` * Add Wasm integration features * Remove superfluous `pub(crate)` in `mod p2p` * Use fn pointers instead of generic closures * Update comment in shutdown test * Address import issues caused by merge * Fix post-merge issues * Let `ActorRequest::endpoint` return `&'static str` * Remove explicit hook endpoint from `add_hook` * Remove TODO on actor protocol version * Factor out `DidComm` specifics into `DidCommActor` * Split `ActorBuilder` into two * Fix docs, remove superfluous functions * Change how items are exported * Refactor into `RawActor` * Refactor actor state repr (partial) * Split `RequestHandler` in two * Return `Endpoint` from `ActorRequest::endpoint` * Remove `RawActor` * Split `ActorRequest` in two * Fix `ActorRequest` docs * Doc/resolve (#823) * Upgrade to new `Stronghold` interface (#787) * Rename stronghold module * Postfix old stronghold with `_old` * Migrate to new stronghold interface * Impl did_create properly with client syncing * Add context to `StrongholdError`s * Add `Stronghold` wrapper test * Add `test_key_delete` * Add storage_test_suite setup & did_create test * Re-export test suite feature * Expose test suite in Wasm * Extend `did_create` test, fix index persistence * Test `key_generate` * Move `key_delete` to test suite * Remove test suite from this branch * Add initial test suite and expose to Wasm * rm `Error` postfix from `StrongholdError` variants * Remove duplicate `mod tests` in Wasm * Handle client sync error; document syncing * Use updated stronghold * Use dedicated `load_snapshot` function * Purge client in `did_purge` * Revert cfg_attr shenanigans * Make `Stronghold::client` not async * Remove asyncness from fns where not necessary * Make `mutate_client` not async either * Move test_util mod where it belongs * Remove `source` errors from `Display` impl * Remove `RecordHint` everywhere * Use base crate `MemoryError`; remove engine dep * Revert temporary send/sync change * Document `Stronghold` wrapper * Use same export style as other crates * Create parent directories if they don't exist * Remove outdated TODO * Fix index writing in purge; update stronghold rev * Remove old stronghold wrapper * Reactivate multi identity example * Add `dropsave` getter/setter * Fully qualify `std::any::type_name` * Remove tests which are already in test suite * Reactivate `Send`-assertion test * Return `Stronghold` instance from test `storages` * Test incorrect password returns error * Use `OsRng` instead of `thread_rng` * Bump stronghold revision * Remove unused `getrandom` depenency * Remove unused `actix` dependency * Remove tokio `rt-multi-thread` feature * Prefer `sample_string` over `sample_iter` * Enable `didPurge` test for NAPI stronghold * Simplify `did_create` by using `mutate_client` * Rename doc/state client paths to store keys * Add procedure_error fn to reduce err map code * Remove unnecessary clone * Disable multiple identities example temporarily * Disable musl build * Remove musl target from stronghold-nodejs * use local workflow file * Revert "use local workflow file" This reverts commit 2f12afd088ebe80476f858dba13efede12463709. Co-authored-by: Eike Haß * Fix Stronghold bindings build for musl (#845) * Use Alpine container to build Stronghold for musl * Remove musl setup step * Change musl container to rust:1.60-alpine * Specify shell in CI * Remove shell configuration * Remove node check-latest * Install NodeJs manually for musl * Remove obsolete comment * Point action back at dev branch * Remove unused Dockerfile, switch to rust:1-alpine * Revert `IdentitySetup` modifications * Refactor `Endpoint` * Let `Endpoint` deserialization validate * Document outstanding methods and types * Document and test `OneOrMany(Into)Iter(ator)` * Remove unstable error types from public API * Remove duplicate test assertion * Test newer nightly in CI * Refactor `Actor` to trait approach * Document types * Move poc modules behind test flag * Remove hooks * Rename modules * Remove `primitives` feature flag * Rename `actor` flag to `actor-unstable` * Derive Debug for `System` * Derive `Debug` for `DidCommSystem` * More renaming actor to system * Add missing `async` feature on `identity-iota` * Remove `cfg-if` dep; use `tokio` unconditionally * Bump `libp2p` to `0.45` * Remove hooks from `Endpoint`s, simplify tests * Log warning instead of panicking * Improve log statements * Document and rename `actor_not_found` method * Remove unnecessary tests, document tests more * Add a `yield_now` to allow bg tasks to finish * Remove `listen_on` due to unreliability * Reorg imports to std/crates/internal hierarchy * Return error in base system on async requests * Rename actor to system * Remove unnecessary features and dependencies * Improve `Endpoint` doc * Remove `HookInvocationError` * Rename `add_address` -> `add_peer_address` * Document more, make more things private * Forbid unsafe code, add warning lints * Rename actor to agent * Rename more types to agent, fix docs accordingly * Add DIDComm benchmark * Add required features to run tests * Improve DIDComm test docs * Rename `PeerId` -> `AgentId` * Rename `ActorResult` -> `AgentResult` * Rename mod `actor` to `agent` * Rename `System` to `Agent` * Rename `Actor` to `Handler` * Revert unintended changes * Use agent instead of actor in `identity_iota` * Revert another unintended change * Rename `ACT` generic type to `HND` * Add keywords, remove wasm dependencies * Bump dashmap to `5.3` * Replace more occurences of peer with agent * Use `DidCommRequest` terminology consistently * Annotate types in `AgentBuilder` * Change agent exports * Use only required features * Remove unused ITERATIONS param * Fix docs and implicit agent feature * Use `AgentId` instead of peer id terminology * Improve `DidCommAgent` doc description * Add README as crate description and in docs * Ignore rust doctests Co-authored-by: Eike Haß Co-authored-by: Oliver E. Anderson Co-authored-by: cycraig --- Cargo.toml | 1 + identity_agent/Cargo.toml | 39 ++ identity_agent/README.md | 192 ++++++++++ identity_agent/benches/agent.rs | 146 ++++++++ identity_agent/benches/didcomm.rs | 137 +++++++ identity_agent/src/agent/agent.rs | 261 +++++++++++++ identity_agent/src/agent/agent_builder.rs | 206 ++++++++++ identity_agent/src/agent/agent_state.rs | 14 + identity_agent/src/agent/config.rs | 18 + identity_agent/src/agent/endpoint.rs | 142 +++++++ identity_agent/src/agent/errors.rs | 117 ++++++ identity_agent/src/agent/handler.rs | 106 ++++++ identity_agent/src/agent/mod.rs | 23 ++ identity_agent/src/agent/request.rs | 35 ++ identity_agent/src/agent/request_context.rs | 32 ++ identity_agent/src/didcomm/agent.rs | 295 +++++++++++++++ identity_agent/src/didcomm/agent_builder.rs | 161 ++++++++ identity_agent/src/didcomm/dcpm.rs | 63 ++++ identity_agent/src/didcomm/handler.rs | 143 +++++++ identity_agent/src/didcomm/mod.rs | 16 + identity_agent/src/didcomm/request.rs | 26 ++ identity_agent/src/didcomm/thread_id.rs | 23 ++ identity_agent/src/lib.rs | 21 ++ identity_agent/src/p2p/behaviour.rs | 78 ++++ identity_agent/src/p2p/event_loop.rs | 239 ++++++++++++ identity_agent/src/p2p/message.rs | 44 +++ identity_agent/src/p2p/mod.rs | 12 + identity_agent/src/p2p/net_commander.rs | 154 ++++++++ identity_agent/src/tests/didcomm.rs | 281 ++++++++++++++ identity_agent/src/tests/handler.rs | 354 ++++++++++++++++++ identity_agent/src/tests/mod.rs | 85 +++++ identity_agent/src/tests/presentation.rs | 161 ++++++++ .../src/tests/remote_account/error.rs | 18 + .../src/tests/remote_account/handler.rs | 65 ++++ .../src/tests/remote_account/mod.rs | 11 + .../src/tests/remote_account/requests.rs | 54 +++ .../src/tests/remote_account/tests.rs | 49 +++ identity_core/src/common/one_or_many.rs | 89 +++++ identity_iota/Cargo.toml | 5 + identity_iota/src/lib.rs | 10 + 40 files changed, 3926 insertions(+) create mode 100644 identity_agent/Cargo.toml create mode 100644 identity_agent/README.md create mode 100644 identity_agent/benches/agent.rs create mode 100644 identity_agent/benches/didcomm.rs create mode 100644 identity_agent/src/agent/agent.rs create mode 100644 identity_agent/src/agent/agent_builder.rs create mode 100644 identity_agent/src/agent/agent_state.rs create mode 100644 identity_agent/src/agent/config.rs create mode 100644 identity_agent/src/agent/endpoint.rs create mode 100644 identity_agent/src/agent/errors.rs create mode 100644 identity_agent/src/agent/handler.rs create mode 100644 identity_agent/src/agent/mod.rs create mode 100644 identity_agent/src/agent/request.rs create mode 100644 identity_agent/src/agent/request_context.rs create mode 100644 identity_agent/src/didcomm/agent.rs create mode 100644 identity_agent/src/didcomm/agent_builder.rs create mode 100644 identity_agent/src/didcomm/dcpm.rs create mode 100644 identity_agent/src/didcomm/handler.rs create mode 100644 identity_agent/src/didcomm/mod.rs create mode 100644 identity_agent/src/didcomm/request.rs create mode 100644 identity_agent/src/didcomm/thread_id.rs create mode 100644 identity_agent/src/lib.rs create mode 100644 identity_agent/src/p2p/behaviour.rs create mode 100644 identity_agent/src/p2p/event_loop.rs create mode 100644 identity_agent/src/p2p/message.rs create mode 100644 identity_agent/src/p2p/mod.rs create mode 100644 identity_agent/src/p2p/net_commander.rs create mode 100644 identity_agent/src/tests/didcomm.rs create mode 100644 identity_agent/src/tests/handler.rs create mode 100644 identity_agent/src/tests/mod.rs create mode 100644 identity_agent/src/tests/presentation.rs create mode 100644 identity_agent/src/tests/remote_account/error.rs create mode 100644 identity_agent/src/tests/remote_account/handler.rs create mode 100644 identity_agent/src/tests/remote_account/mod.rs create mode 100644 identity_agent/src/tests/remote_account/requests.rs create mode 100644 identity_agent/src/tests/remote_account/tests.rs diff --git a/Cargo.toml b/Cargo.toml index e35afd0a33..e564cfa7e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ members = [ # "identity_comm", "identity_account", "identity_account_storage", + "identity_agent", "identity_core", "identity_credential", "identity_did", diff --git a/identity_agent/Cargo.toml b/identity_agent/Cargo.toml new file mode 100644 index 0000000000..5729cca4f9 --- /dev/null +++ b/identity_agent/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "identity_agent" +version = "0.6.0" +authors = ["IOTA Stiftung"] +edition = "2021" +homepage = "https://www.iota.org" +keywords = ["iota", "tangle", "identity", "p2p", "agent"] +license = "Apache-2.0" +readme = "./README.md" +repository = "https://github.com/iotaledger/identity.rs" +description = "A peer-to-peer communication framework for building digital agents on IOTA Identity" + +[dependencies] +async-trait = { version = "0.1", default-features = false } +dashmap = { version = "5.3", default-features = false } +futures = { version = "0.3", default-features = false } +identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } +identity_iota_client = { version = "=0.6.0", path = "../identity_iota_client", default-features = false } +identity_iota_core = { version = "=0.6.0", path = "../identity_iota_core", default-features = false } +libp2p = { version = "0.45", default-features = false, features = ["tcp-tokio", "dns-tokio", "websocket", "request-response", "noise", "yamux"] } +log = { version = "0.4", default-features = false } +serde = { version = "1.0", default-features = false, features = ["derive"] } +serde_json = { version = "1.0", default-features = false } +thiserror = { version = "1.0", default-features = false } +tokio = { version = "1.15", default-features = false, features = ["rt", "time"] } +uuid = { version = "0.8", default-features = false, features = ["v4", "serde"] } + +[dev-dependencies] +criterion = { version = "0.3", default-features = false, features = ["stable"] } +identity_account = { version = "=0.6.0", path = "../identity_account", default-features = false, features = ["send-sync-storage"] } +pretty_env_logger = { version = "0.4", default-features = false } + +[[bench]] +name = "agent" +harness = false + +[[bench]] +name = "didcomm" +harness = false diff --git a/identity_agent/README.md b/identity_agent/README.md new file mode 100644 index 0000000000..38d99606bf --- /dev/null +++ b/identity_agent/README.md @@ -0,0 +1,192 @@ +# IOTA Identity Agent + +The identity agent is a peer-to-peer communication framework for building SSI agents on IOTA Identity. It is intended to host implementations of the [DIDComm protocols](https://wiki.iota.org/identity.rs/specs/didcomm/overview) with future updates. Together with these protocols, this will, for example, allow for did-authenticated communication between two identities to exchange verifiable credentials or presentations. + +For a high-level and less technical introduction, see the [blog post](https://blog.iota.org/the-iota-identity-actor-explained/) on the agent (formerly known as identity actor). + +The most important dependency of the agent is libp2p. [What is libp2p?](https://docs.libp2p.io/introduction/what-is-libp2p/) + +> The one-liner pitch is that libp2p is a modular system of _protocols_, _specifications_ and _libraries_ that enable the development of peer-to-peer network applications. + +We use libp2p because it can easily secure transports using the noise protocol, is agnostic of transports (so agents could conceivably communicate over TCP, websockets or Bluetooth), and because of how flexible it is we can make it suit the agent nicely. + +## Building an agent + +```rust,ignore +let id_keys: IdentityKeypair = IdentityKeypair::generate_ed25519(); +let addr: Multiaddr = "/ip4/0.0.0.0/tcp/0".parse()?; + +let mut agent: Agent = AgentBuilder::new() + .keypair(id_keys) + .build() + .await?; + +agent.start_listening(addr).await?; +``` + +To build a minimal working agent, we generate a new `IdentityKeypair` from which the `AgentId` of the agent is derived. The `AgentId` is an alias for a `libp2p::PeerId`, which allows for cryptographically verifiable identification of a peer. This decouples the identity concept from the underlying network address, which is important if the agent roams across networks. If we want the agent to have the same `AgentId` across program executions, we need to store this keypair. Next we create the address for the agent to listen on. A `Multiaddr` is the address format in libp2p to encode addresses of various transports. Finally, we build the agent with a default transport, that supports DNS resolution and can use TCP or websockets. + +## Processing incoming requests + +To make the agent do something useful, we need handlers. A handler is some state with associated behavior that processes incoming requests. It will be invoked if the agent is able to deserialize the incoming request to the type the handler expects. The `Handler` is a trait that looks like this: + +```rust,ignore +#[async_trait::async_trait] +pub trait Handler: Debug + 'static { + async fn handle(&self, request: RequestContext) -> REQ::Response; +} +``` + +- It takes `&self` so it can modify its state through appropriate mechanisms, such as locks. A handler will thus typically implement a shallow copy mechanism (e.g. using `Arc`) to share state. +- It takes the request it wants to handle, which needs to implement the `HandlerRequest` trait and needs to return the defined response type. +- This trait can be implemented multiple times so the same handler can process different request types. + +Here is an example of a handler being attached on an `AgentBuilder`. We implement `RemoteAccounts`, an exemplary type that manages `Account`s remotely. + +```rust,ignore +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum RemoteAccountsError { + IdentityNotFound, +} + +/// The struct that will be sent over the network. +/// When received by an agent, the contained DID is looked up. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct RemoteAccountsGet(pub IotaDID); + +impl HandlerRequest for RemoteAccountsGet { + /// The result of the lookup procedure, either the corresponding IotaDocument, or an error. + type Response = Result; + + /// `Endpoint`s are identifiers for requests which lets the remote agent determine the appropriate handler to invoke. + fn endpoint() -> Endpoint { + "remote_accounts/get".try_into().unwrap() + } +} + +/// Our thread-safe type that holds accounts that can be looked up by their DID. +#[derive(Debug, Clone, Default)] +pub struct RemoteAccounts { + accounts: Arc>, +} + +#[async_trait::async_trait] +impl Handler for RemoteAccounts { + /// To handle the request, we take the HandlerRequest type wrapped in a RequestContext, which provides some + /// useful information about the caller like their `AgentId`. + async fn handle(&self, request: RequestContext) -> Result { + self + .accounts + .get(&request.input.0) + .map(|account| account.document().to_owned()) + .ok_or(RemoteAccountsError::IdentityNotFound) + } +} + +/// To build the agent with our custom functionality, we first build the agent itself +/// and attach the handler. +async fn build_agent() { + let mut builder = AgentBuilder::new(); + builder.attach(RemoteAccounts::default()); +} +``` + +An agent that receives a request will check whether a handler is attached that can handle the request's `Endpoint` and if so, will invoke it. In our case, the agent will call the `handle` function of the handler when a `RemoteAccountsGet` request is received. If we wanted, we could attach more handlers to the same agent, and even implement `Handler` for `RemoteAccounts` multiple times, in order to handle different request types. + +## Sending requests + +To invoke a handler on a remote agent, we send a type that implements `HandlerRequest`, such as `RemoteAccountsGet`. + +```rust,ignore +let mut agent: Agent = builder.build().await?; + +agent.add_agent_address(remote_agent_id, addr).await?; + +let result: Result = agent + .send_request(remote_agent_id, RemoteAccountsGet("did:iota:...".parse()?)) + .await?; +``` + +After building the agent and adding the address of the remote agent, we can send a request. The agent takes care of serializing the request, and attempts to deserialize the response into `::Response`. + +## Agent modes + +We've just seen an example of a synchronous request, one where we invoke a handler on a remote agent and wait for it to finish execution and return a result. Next to the `Agent` type we also have a `DidCommAgent` type. The latter additionally supports an asynchronous mode, where we send a request without waiting for the result of the handler invocation. Instead, we can explicitly await a request: + +```rust,ignore +async fn didcomm_protocol(agent_id: AgentId, didcomm_agent: &DidCommAgent) -> AgentResult<()> { + let thread_id: ThreadId = ThreadId::new(); + + didcomm_agent + .send_didcomm_request(agent_id, &thread_id, PresentationOffer::default()) + .await?; + + let request: DidCommPlaintextMessage = didcomm_agent.await_didcomm_request(&thread_id).await?; + + Ok(()) +} +``` + +This request mode is implemented to support the implementation of [DIDComm](https://identity.foundation/didcomm-messaging/spec/) protocols, which is why a separate `DidCommAgent` is defined that extends the `Agent`s functionality and handles the specifics of DIDComm. Note that the base `Agent` doesn't support the asynchronous mode, but the `DidCommAgent` supports the sychronous mode. + +Here, the protocol expects us to first send a `PresentationOffer` request to the remote agent. This method call returns successfully if the request can be deserialized properly and if an appropriate handler exists on the remote agent, but the call might return before the handler on the remote has finished. According to the protocol we implement, we should expect the remote to send us a `PresentationRequest` so we explicitly call `await_didcomm_request` to await the incoming request on the same `ThreadId` that we sent our previous request on. This allows for imperative protocol implementations within a single handler. This is nice to have, because the alternative would be that each request invokes a separate handler in an agent, which would force protocol implementors to hold the state in some shared state, rather than implicitly in the function (such as the `thread_id` here). This setup is intended for DIDComm protocols, as it directly implements DIDComm concepts such as threads. + +## Examples + +There are currently no examples for the agent in the `examples` directory. This is mostly due to the instability of the agent. Still, there are two "examples" for each mode of operation as part of the `tests` module, the remote account as a synchronous example, and the IOTA DIDComm presentation protocol as an asynchronous example (this doesn't implement the actual protocol, it just asserts that requests can be sent back and forth as expected). The DIDComm example in particular is very simple and minimal and mostly exists as a proof of concept for the async mode, but it also serves as an example for how a DIDComm protocol could potentially be implemented. + +### DIDComm example setup + +The async mode didcomm examples are worth explaining a little more. The implementation difficulty for these protocols comes mostly because of how flexible they are. In the presentation protocol for example, both the holder and verifier can initiate the exchange. On the agent level this means either calling the protocol explicitly to initiate it, or attaching a handler to let the agent handle the protocol in the background when a remote agent initiates. Thus, there is one function that implements the actual protocol for each of the roles (i.e. _holder_ and _verifier_ in the `presentation` protocol). As an example, this is what the signature of the holder role would look like: + +```rust,ignore +pub(crate) async fn presentation_holder_handler( + mut agent: DidCommAgent, + agent_id: AgentId, + request: Option>, +) -> AgentResult<()> { ... } +``` + +The holder can call this function to initiate the protocol imperatively by passing `None` as `request`. On the other hand, if the verifier initiates, the holder defines a handler that will inject the received `request`: + +```rust,ignore +#[async_trait::async_trait] +impl DidCommHandler> for DidCommState { + async fn handle(&self, agent: DidCommAgent, request: RequestContext>) { + let result = presentation_holder_handler(agent, request.agent_id, Some(request.input)).await; + + if let Err(err) = result { + log::error!("presentation holder handler errored: {err:?}"); + } + } +} +``` + +and attaches it: + +```rust,ignore +didcomm_builder.attach::, _>(DidCommState::new()); +``` + +`DidCommState` holds the state for one or more DIDComm protocols. When a `PresentationRequest` is received, it calls the protocol function (`presentation_holder_handler`) to run through the protocol. This allows us to nicely reuse the `presentation_holder_handler` function as the core protocol implementation and only requires defining a thin handler method. The verifier can follow the same pattern for their side of the protocol. + +## Implementation Details + +This section goes into some details of agent internals. + +### Agent internals + +The overall architecture can be seen as four layers. A libp2p layer, a commander layer to interact with the libp2p layer, the raw agent layer (which uses the commander) and the `DidCommAgent` on top. This architecture is strongly inspired by [stronghold-p2p](https://github.com/iotaledger/stronghold.rs/tree/dev/p2p). + +- The p2p layer consists of a `libp2p::RequestResponse` protocol, which enforces on a type level that each request has a response. This naturally maps to the sync mode of the identity agent where each request has some response, as well as to the async mode where each request will be acknowledged. +- This layer has an `EventLoop` that concurrently polls the libp2p `Swarm` to handle its events as well as commands that are sent to it from the `NetCommander`. +- The commander layer, or `NetCommander` communicates with the event loop via channels and is thus the interface for the `EventLoop`. +- When the agent is built, it spawns an `EventLoop` in the background and interacts with it using the `NetCommander`. +- On incoming requests, the `EventLoop` spawns a new task and injects a clone of the agent into it (see `EventLoop::run` and its argument). + +### DidCommAgent internals + +- In async mode, the `DidCommAgent` returns an acknowledgment if 1) a handler for the endpoint or a thread exists and 2) if the request can be deserialized into the expected type for the handler or thread (e.g. a DIDComm plaintext message) +- Timeouts can occur in two ways and both are configured via `AgentBuilder::timeout`. + - A request sender can receive an `InboundFailure::Timeout` if the peer did not respond within the configured timeout. This happens on the event loop level and is handled by the `RequestResponse` protocol. + - `DidCommAgent::await_didcomm_request` can time out. This is the same timeout value as for the underlying `RequestResponse` protocol. In such a case, the event loop will receive a timeout error, but since no entry in the thread hash map is waiting for a response, it is silently dropped. Thus, `await_didcomm_request` implements its own timeout, and automatically uses the same duration as the underlying protocol to ensure consistent behaviour. For this reason, the `await_didcomm_request` timeout is a per-agent configuration value, and not a parameter on the function, although that would also be possible if desired. diff --git a/identity_agent/benches/agent.rs b/identity_agent/benches/agent.rs new file mode 100644 index 0000000000..d2626b2d1f --- /dev/null +++ b/identity_agent/benches/agent.rs @@ -0,0 +1,146 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use criterion::criterion_group; +use criterion::criterion_main; +use criterion::Criterion; +use identity_agent::agent::Agent; +use identity_agent::agent::AgentBuilder; +use identity_agent::agent::AgentId; +use identity_agent::Multiaddr; + +use remote_account::IdentityCreate; +use remote_account::RemoteAccount; + +async fn setup() -> (Agent, AgentId, Agent) { + let addr: Multiaddr = "/ip4/0.0.0.0/tcp/0".parse().unwrap(); + let mut builder = AgentBuilder::new(); + + let remote_account = RemoteAccount::new().unwrap(); + builder.attach::(remote_account); + + let mut receiver: Agent = builder.build().await.unwrap(); + + let addr = receiver.start_listening(addr).await.unwrap(); + let receiver_agent_id = receiver.agent_id(); + + let mut sender: Agent = AgentBuilder::new().build().await.unwrap(); + + sender.add_agent_address(receiver_agent_id, addr).await.unwrap(); + + (receiver, receiver_agent_id, sender) +} + +fn bench_remote_account(c: &mut Criterion) { + let runtime = tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap(); + + let (receiver, receiver_agent_id, sender) = runtime.block_on(setup()); + + let mut group = c.benchmark_group("remote_account"); + + group.bench_function("IdentityCreate", |bencher| { + bencher.to_async(&runtime).iter(|| { + let mut sender_clone: Agent = sender.clone(); + + async move { + sender_clone + .send_request(receiver_agent_id, IdentityCreate::default()) + .await + .unwrap() + .unwrap(); + } + }); + }); + + group.finish(); + + runtime.block_on(async move { + sender.shutdown().await.unwrap(); + receiver.shutdown().await.unwrap(); + }); +} + +criterion_group!(benches, bench_remote_account); + +criterion_main!(benches); + +mod remote_account { + use dashmap::DashMap; + use identity_account::account::Account; + use identity_account::account::AccountBuilder; + use identity_account::types::IdentitySetup; + use identity_agent::agent::Endpoint; + use identity_agent::agent::Handler; + use identity_agent::agent::HandlerRequest; + use identity_agent::agent::RequestContext; + use identity_iota_core::did::IotaDID; + use identity_iota_core::document::IotaDocument; + use serde::Deserialize; + use serde::Serialize; + use std::sync::Arc; + use tokio::sync::Mutex; + + #[derive(Debug, Clone)] + pub struct RemoteAccount { + builder: Arc>, + accounts: Arc>, + } + + impl RemoteAccount { + pub fn new() -> identity_account::Result { + let builder: AccountBuilder = Account::builder().autopublish(false); + + Ok(Self { + builder: Arc::new(Mutex::new(builder)), + accounts: Arc::new(DashMap::new()), + }) + } + } + + /// Can be sent to a `RemoteAccount` to instruct it to create an identity. + #[derive(Debug, Default, Clone, Serialize, Deserialize)] + pub struct IdentityCreate; + + impl From for IdentitySetup { + fn from(_: IdentityCreate) -> Self { + IdentitySetup::default() + } + } + + impl HandlerRequest for IdentityCreate { + type Response = Result; + + fn endpoint() -> Endpoint { + "remote_account/create".try_into().unwrap() + } + } + + #[async_trait::async_trait] + impl Handler for RemoteAccount { + async fn handle(&self, request: RequestContext) -> Result { + let account: Account = self.builder.lock().await.create_identity(request.input.into()).await?; + let doc = account.document().to_owned(); + self.accounts.insert(account.did().to_owned(), account); + Ok(doc) + } + } + + /// The error type for the [`RemoteAccount`]. + #[derive(Debug, thiserror::Error, serde::Serialize, serde::Deserialize)] + #[non_exhaustive] + pub enum RemoteAccountError { + #[error("identity not found")] + IdentityNotFound, + #[error("{0}")] + AccountError(String), + } + + impl From for RemoteAccountError { + fn from(err: identity_account::Error) -> Self { + Self::AccountError(err.to_string()) + } + } +} diff --git a/identity_agent/benches/didcomm.rs b/identity_agent/benches/didcomm.rs new file mode 100644 index 0000000000..00eb33dcf1 --- /dev/null +++ b/identity_agent/benches/didcomm.rs @@ -0,0 +1,137 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use criterion::criterion_group; +use criterion::criterion_main; +use criterion::Criterion; + +use identity_agent::agent::AgentId; +use identity_agent::didcomm::DidCommAgent; +use identity_agent::didcomm::DidCommAgentBuilder; +use identity_agent::didcomm::DidCommAgentIdentity; +use identity_agent::didcomm::DidCommPlaintextMessage; +use identity_agent::didcomm::ThreadId; +use identity_agent::Multiaddr; + +use identity_core::crypto::KeyPair; +use identity_core::crypto::KeyType; +use identity_iota_core::document::IotaDocument; +use test_handler::PresentationOffer; +use test_handler::PresentationRequest; +use test_handler::TestHandler; + +async fn setup() -> (DidCommAgent, AgentId, DidCommAgent) { + let addr: Multiaddr = "/ip4/0.0.0.0/tcp/0".parse().unwrap(); + let mut builder = DidCommAgentBuilder::new().identity(DidCommAgentIdentity { + document: IotaDocument::new(&KeyPair::new(KeyType::Ed25519).unwrap()).unwrap(), + }); + + builder.attach_didcomm(TestHandler); + + let mut receiver: DidCommAgent = builder.build().await.unwrap(); + + let addr = receiver.start_listening(addr).await.unwrap(); + let receiver_agent_id = receiver.agent_id(); + + let mut sender: DidCommAgent = DidCommAgentBuilder::new() + .identity(DidCommAgentIdentity { + document: IotaDocument::new(&KeyPair::new(KeyType::Ed25519).unwrap()).unwrap(), + }) + .build() + .await + .unwrap(); + + sender.add_agent_address(receiver_agent_id, addr).await.unwrap(); + + (receiver, receiver_agent_id, sender) +} + +fn bench_didcomm_requests(c: &mut Criterion) { + let runtime = tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap(); + + let (receiver, receiver_agent_id, sender) = runtime.block_on(setup()); + + let mut group = c.benchmark_group("didcomm_requests"); + + group.bench_function("send and await", |bencher| { + bencher.to_async(&runtime).iter(|| { + let mut sender_clone: DidCommAgent = sender.clone(); + + let thread_id: ThreadId = ThreadId::new(); + + async move { + sender_clone + .send_didcomm_request(receiver_agent_id, &thread_id, PresentationRequest::default()) + .await + .unwrap(); + + let _: DidCommPlaintextMessage = + sender_clone.await_didcomm_request(&thread_id).await.unwrap(); + } + }); + }); + + group.finish(); + + runtime.block_on(async move { + sender.shutdown().await.unwrap(); + receiver.shutdown().await.unwrap(); + }); +} + +criterion_group!(benches, bench_didcomm_requests); + +criterion_main!(benches); + +mod test_handler { + use identity_agent::agent::Endpoint; + use identity_agent::agent::RequestContext; + use identity_agent::didcomm::DidCommAgent; + use identity_agent::didcomm::DidCommHandler; + use identity_agent::didcomm::DidCommPlaintextMessage; + use identity_agent::didcomm::DidCommRequest; + use serde::Deserialize; + use serde::Serialize; + + #[derive(Debug, Clone)] + pub struct TestHandler; + + #[derive(Clone, Debug, Deserialize, Serialize, Default)] + pub(crate) struct PresentationRequest(u8); + + impl DidCommRequest for PresentationRequest { + fn endpoint() -> Endpoint { + "didcomm/presentation_request".try_into().unwrap() + } + } + + #[derive(Clone, Debug, Deserialize, Serialize, Default)] + pub(crate) struct PresentationOffer(u16); + + impl DidCommRequest for PresentationOffer { + fn endpoint() -> Endpoint { + "didcomm/presentation_offer".try_into().unwrap() + } + } + + #[async_trait::async_trait] + impl DidCommHandler> for TestHandler { + async fn handle( + &self, + mut agent: DidCommAgent, + request: RequestContext>, + ) { + agent + .send_didcomm_request( + request.agent_id, + request.input.thread_id(), + PresentationOffer(request.input.body().0 as u16), + ) + .await + .unwrap(); + } + } +} diff --git a/identity_agent/src/agent/agent.rs b/identity_agent/src/agent/agent.rs new file mode 100644 index 0000000000..480a1deb14 --- /dev/null +++ b/identity_agent/src/agent/agent.rs @@ -0,0 +1,261 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::collections::HashMap; +use std::sync::Arc; + +use identity_core::common::OneOrMany; +use libp2p::request_response::InboundFailure; +use libp2p::request_response::RequestId; +use libp2p::request_response::ResponseChannel; +use libp2p::Multiaddr; + +use crate::agent::errors::ErrorLocation; +use crate::agent::AbstractHandler; +use crate::agent::AgentState; +use crate::agent::Endpoint; +use crate::agent::Error; +use crate::agent::HandlerRequest; +use crate::agent::RemoteSendError; +use crate::agent::RequestContext; +use crate::agent::RequestMode; +use crate::agent::Result as AgentResult; +use crate::p2p::InboundRequest; +use crate::p2p::NetCommander; +use crate::p2p::RequestMessage; +use crate::p2p::ResponseMessage; + +/// A map from an endpoint to the handler that handles its requests. +pub(crate) type HandlerMap = HashMap>; + +/// The cryptographic identifier of an agent on the network. +pub type AgentId = libp2p::PeerId; + +/// An agent can be used to send requests to other, remote agents, and fowards incoming requests +/// to attached handlers. +/// +/// An agent is a frontend for an event loop running in the background, which invokes +/// user-attached handlers. Agents can be cloned without cloning the event loop, and doing so +/// is a cheap operation. +/// Handlers are attached at agent build time, using the [`AgentBuilder`](crate::agent::AgentBuilder). +/// +/// After shutting down the event loop of an agent using [`Agent::shutdown`], other clones of the +/// agent will receive [`Error::Shutdown`] when attempting to interact with the event loop. +#[derive(Debug)] +pub struct Agent { + commander: NetCommander, + state: Arc, +} + +// Implement Clone for the sake of documenting that it is a cheap operation in this case. +impl Clone for Agent { + /// Produce a shallow copy of the agent, which uses the same event loop as the + /// agent that it was cloned from. + fn clone(&self) -> Self { + Self { + commander: self.commander.clone(), + state: self.state.clone(), + } + } +} + +impl Agent { + pub(crate) fn new(commander: NetCommander, state: Arc) -> Agent { + Self { commander, state } + } + + pub(crate) fn state(&self) -> &AgentState { + self.state.as_ref() + } + + /// Returns the [`AgentId`] that other peers can securely identify this agent with. + pub fn agent_id(&self) -> AgentId { + self.state().agent_id + } + + pub(crate) fn commander_mut(&mut self) -> &mut NetCommander { + &mut self.commander + } + + /// Start listening on the given `address`. Returns the first address that the agent started listening on, which may + /// be different from `address` itself, for example when passing addresses like `/ip4/0.0.0.0/tcp/0`. Even when + /// passing a single address, multiple addresses may end up being listened on. To obtain all those addresses, use + /// [`Agent::addresses`]. Note that even when the same address is passed, the returned address is not deterministic, + /// and should thus not be relied upon. + pub async fn start_listening(&mut self, address: Multiaddr) -> AgentResult { + self.commander_mut().start_listening(address).await + } + + /// Return all addresses that are currently being listened on. + pub async fn addresses(&mut self) -> AgentResult> { + self.commander_mut().get_addresses().await + } + + /// Shut this agent down. This will break the event loop in the background immediately, + /// returning an error for all current handlers that interact with their copy of the + /// agent or those waiting on messages. The agent will thus stop listening on all addresses. + /// + /// Calling this and other methods, which interact with the event loop, on an agent that was shutdown + /// will return [`Error::Shutdown`]. + pub async fn shutdown(mut self) -> AgentResult<()> { + // Consuming self drops the internal commander. If this is the last copy of the commander, + // the event loop will break as a result. However, if copies exist, such as in running handlers, + // this function will return while the event loop keeps running. Ideally we could then join on the background task + // to wait for all handlers to finish gracefully. This was not implemented that way, because of a previous + // dependency on wasm_bindgen_futures::spawn_local which does not return a JoinHandle. It would be an option to + // change it, now that we're using tokio exclusively. + // The current implementation uses a non-graceful exit, which breaks the event loop immediately + // and returns an error through all open channels that require a result. + self.commander_mut().shutdown().await + } + + /// Associate the given `agent_id` with an `address`. This `address`, or another one that was added, + /// will be use to send requests to `agent_id`. + pub async fn add_agent_address(&mut self, agent_id: AgentId, address: Multiaddr) -> AgentResult<()> { + self + .commander_mut() + .add_addresses(agent_id, OneOrMany::One(address)) + .await + } + + /// Associate the given `agent_id` with multiple `addresses`. One of the `addresses`, or another one that was added, + /// will be use to send requests to `agent_id`. + pub async fn add_agent_addresses(&mut self, agent_id: AgentId, addresses: Vec) -> AgentResult<()> { + self + .commander_mut() + .add_addresses(agent_id, OneOrMany::Many(addresses)) + .await + } + + /// Sends a synchronous request to an agent, identified through `agent_id`, and returns its response. + /// + /// An address needs to be available for the given `agent_id`, which can be added + /// with [`Agent::add_agent_address`] or [`Agent::add_agent_addresses`]. + pub async fn send_request( + &mut self, + agent_id: AgentId, + request: REQ, + ) -> AgentResult { + let endpoint: Endpoint = REQ::endpoint(); + let request_mode: RequestMode = REQ::request_mode(); + + let request_vec = serde_json::to_vec(&request).map_err(|err| Error::SerializationFailure { + location: ErrorLocation::Local, + context: "send request".to_owned(), + error_message: err.to_string(), + })?; + + log::debug!("sending request on endpoint `{endpoint}`"); + + let request: RequestMessage = RequestMessage::new(endpoint, request_mode, request_vec); + + let response: ResponseMessage = self.commander_mut().send_request(agent_id, request).await?; + + let response: Vec = + serde_json::from_slice::, RemoteSendError>>(&response.0).map_err(|err| { + Error::DeserializationFailure { + location: ErrorLocation::Local, + context: "send request (result)".to_owned(), + error_message: err.to_string(), + } + })??; + + serde_json::from_slice::(&response).map_err(|err| Error::DeserializationFailure { + location: ErrorLocation::Local, + context: "send request".to_owned(), + error_message: err.to_string(), + }) + } + + /// Let this agent handle the given `request`, by invoking the appropriate handler, if attached. + /// This consumes the agent because it passes itself to the handler. + /// The agent will thus typically be cloned before calling this method. + pub(crate) fn handle_request(mut self, request: InboundRequest) { + if request.request_mode == RequestMode::Synchronous { + self.handle_sync_request(request) + } else { + let _ = tokio::spawn(async move { + if let Err(error) = send_response( + self.commander_mut(), + Result::<(), RemoteSendError>::Err(RemoteSendError::UnexpectedRequest( + "asynchronous requests are not supported".to_owned(), + )), + request.response_channel, + request.request_id, + ) + .await + { + log::error!( + "unable to respond to synchronous request on endpoint `{}` due to: {error}", + request.endpoint + ); + } + }); + } + } + + #[inline(always)] + pub(crate) fn handle_sync_request(mut self, request: InboundRequest) { + let _ = tokio::spawn(async move { + match self.state.handlers.get(&request.endpoint) { + Some(handler) => { + let context: RequestContext> = + RequestContext::new(request.input, request.peer_id, request.endpoint.clone()); + let result: Result, RemoteSendError> = handler.handle(context).await; + + if let Err(error) = send_response( + self.commander_mut(), + result, + request.response_channel, + request.request_id, + ) + .await + { + log::error!( + "unable to respond to synchronous request on endpoint `{}` due to: {error}", + request.endpoint + ); + } + } + None => { + endpoint_not_found(&mut self, request).await; + } + } + }); + } +} + +pub(crate) async fn send_response( + commander: &mut NetCommander, + response: Result, + channel: ResponseChannel, + request_id: RequestId, +) -> AgentResult> { + let response: Vec = serde_json::to_vec(&response).map_err(|err| crate::agent::Error::SerializationFailure { + location: ErrorLocation::Local, + context: "send response".to_owned(), + error_message: err.to_string(), + })?; + commander.send_response(response, channel, request_id).await +} + +#[inline(always)] +async fn endpoint_not_found(handler: &mut Agent, request: InboundRequest) { + let response: Result, RemoteSendError> = + Err(RemoteSendError::UnexpectedRequest(request.endpoint.to_string())); + + let send_result = send_response( + handler.commander_mut(), + response, + request.response_channel, + request.request_id, + ) + .await; + + if let Err(err) = send_result { + log::error!( + "could not return error for request on endpoint `{}` due to: {err:?}", + request.endpoint + ); + } +} diff --git a/identity_agent/src/agent/agent_builder.rs b/identity_agent/src/agent/agent_builder.rs new file mode 100644 index 0000000000..fa45a0f658 --- /dev/null +++ b/identity_agent/src/agent/agent_builder.rs @@ -0,0 +1,206 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::collections::HashMap; +use std::iter; +use std::sync::Arc; +use std::time::Duration; + +use futures::channel::mpsc; +use futures::AsyncRead; +use futures::AsyncWrite; +use futures::FutureExt; +use libp2p::core::transport::upgrade; +use libp2p::core::Executor; +use libp2p::core::Transport; +use libp2p::dns::TokioDnsConfig; +use libp2p::identity::Keypair; +use libp2p::noise::AuthenticKeypair; +use libp2p::noise::Keypair as NoiseKeypair; +use libp2p::noise::NoiseConfig; +use libp2p::noise::X25519Spec; +use libp2p::request_response::ProtocolSupport; +use libp2p::request_response::RequestResponse; +use libp2p::request_response::RequestResponseConfig; +use libp2p::swarm::SwarmBuilder; +use libp2p::tcp::TokioTcpConfig; +use libp2p::websocket::WsConfig; +use libp2p::yamux::YamuxConfig; +use libp2p::Swarm; + +use crate::agent::AbstractHandler; +use crate::agent::Agent; +use crate::agent::AgentConfig; +use crate::agent::AgentId; +use crate::agent::AgentState; +use crate::agent::Error; +use crate::agent::Handler; +use crate::agent::HandlerMap; +use crate::agent::HandlerRequest; +use crate::agent::HandlerWrapper; +use crate::agent::Result as AgentResult; +use crate::p2p::AgentProtocol; +use crate::p2p::AgentRequestResponseCodec; +use crate::p2p::EventLoop; +use crate::p2p::InboundRequest; +use crate::p2p::NetCommander; + +/// A builder for [`Agent`]s to customize its configuration and attach handlers. +pub struct AgentBuilder { + pub(crate) keypair: Option, + pub(crate) config: AgentConfig, + pub(crate) handlers: HandlerMap, +} + +impl AgentBuilder { + /// Create a new builder with the default configuration. + pub fn new() -> AgentBuilder { + Self { + keypair: None, + config: AgentConfig::default(), + handlers: HashMap::new(), + } + } + + /// Set the keypair from which the `AgentId` of the agent is derived. + /// + /// If unset, a new keypair is generated. + #[must_use] + pub fn keypair(mut self, keypair: Keypair) -> Self { + self.keypair = Some(keypair); + self + } + + /// Sets the timeout for the underlying libp2p [`RequestResponse`] protocol. + #[must_use] + pub fn timeout(mut self, timeout: Duration) -> Self { + self.config.timeout = timeout; + self + } + + /// Attaches a [`Handler`] to this agent. + /// + /// This means that when the agent receives a request of type `REQ`, it will invoke this handler. + /// + /// Calling this method with a `REQ` type whose endpoint is already attached to a handler + /// will overwrite the previous attachment. + pub fn attach(&mut self, handler: HND) + where + HND: Handler + Send + Sync, + REQ: HandlerRequest + Send + Sync, + REQ::Response: Send, + { + self.handlers.insert( + REQ::endpoint(), + Box::new(HandlerWrapper::new(handler)) as Box, + ); + } + + /// Build the handler with a default transport which supports DNS, TCP and WebSocket capabilities. + pub async fn build(self) -> AgentResult { + let transport: _ = { + let dns_tcp_transport: TokioDnsConfig<_> = TokioDnsConfig::system(TokioTcpConfig::new().nodelay(true)) + .map_err(|err| Error::TransportError("building transport", libp2p::TransportError::Other(err)))?; + let ws_transport: WsConfig<_> = WsConfig::new( + TokioDnsConfig::system(TokioTcpConfig::new().nodelay(true)) + .map_err(|err| Error::TransportError("building transport", libp2p::TransportError::Other(err)))?, + ); + dns_tcp_transport.or_transport(ws_transport) + }; + + self.build_with_transport(transport).await + } + + /// Build the agent with a custom transport. + pub async fn build_with_transport(self, transport: TRA) -> AgentResult + where + TRA: Transport + Sized + Send + Sync + 'static, + TRA::Output: AsyncRead + AsyncWrite + Unpin + Send + 'static, + TRA::Dial: Send + 'static, + TRA::Listener: Send + 'static, + TRA::ListenerUpgrade: Send + 'static, + TRA::Error: Send + Sync, + { + let executor = Box::new(|fut| { + tokio::spawn(fut); + }); + + let (event_loop, handler_state, net_commander): (EventLoop, AgentState, NetCommander) = + self.build_constituents(transport, executor.clone()).await?; + + let agent: Agent = Agent::new(net_commander, Arc::new(handler_state)); + let agent_clone: Agent = agent.clone(); + + let event_handler = move |event: InboundRequest| { + agent_clone.clone().handle_request(event); + }; + + executor.exec(event_loop.run(event_handler).boxed()); + + Ok(agent) + } + + /// Build the agent constituents with a custom transport and custom executor. + pub(crate) async fn build_constituents( + self, + transport: TRA, + executor: Box, + ) -> AgentResult<(EventLoop, AgentState, NetCommander)> + where + TRA: Transport + Sized + Send + Sync + 'static, + TRA::Output: AsyncRead + AsyncWrite + Unpin + Send + 'static, + TRA::Dial: Send + 'static, + TRA::Listener: Send + 'static, + TRA::ListenerUpgrade: Send + 'static, + TRA::Error: Send + Sync, + { + let (noise_keypair, agent_id): (AuthenticKeypair<_>, AgentId) = { + let keypair: Keypair = self.keypair.unwrap_or_else(Keypair::generate_ed25519); + let noise_keypair = NoiseKeypair::::new() + .into_authentic(&keypair) + .expect("ed25519 keypair should be convertible into x25519"); + let agent_id = keypair.public().to_peer_id(); + (noise_keypair, agent_id) + }; + + let swarm: Swarm> = { + let mut config: RequestResponseConfig = RequestResponseConfig::default(); + config.set_request_timeout(self.config.timeout); + + let behaviour = RequestResponse::new( + AgentRequestResponseCodec(), + iter::once((AgentProtocol(), ProtocolSupport::Full)), + config, + ); + + let transport: _ = transport + .upgrade(upgrade::Version::V1) + .authenticate(NoiseConfig::xx(noise_keypair).into_authenticated()) + .multiplex(YamuxConfig::default()) + .boxed(); + + SwarmBuilder::new(transport, behaviour, agent_id) + .executor(executor) + .build() + }; + + let (cmd_sender, cmd_receiver): _ = mpsc::channel(10); + + let event_loop: EventLoop = EventLoop::new(swarm, cmd_receiver); + let net_commander: NetCommander = NetCommander::new(cmd_sender); + + let agent_state: AgentState = AgentState { + agent_id, + config: self.config, + handlers: self.handlers, + }; + + Ok((event_loop, agent_state, net_commander)) + } +} + +impl Default for AgentBuilder { + fn default() -> Self { + Self::new() + } +} diff --git a/identity_agent/src/agent/agent_state.rs b/identity_agent/src/agent/agent_state.rs new file mode 100644 index 0000000000..c88927f973 --- /dev/null +++ b/identity_agent/src/agent/agent_state.rs @@ -0,0 +1,14 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use crate::agent::AgentConfig; +use crate::agent::AgentId; +use crate::agent::HandlerMap; + +/// The internal state of an `Agent`. +#[derive(Debug)] +pub(crate) struct AgentState { + pub(crate) agent_id: AgentId, + pub(crate) config: AgentConfig, + pub(crate) handlers: HandlerMap, +} diff --git a/identity_agent/src/agent/config.rs b/identity_agent/src/agent/config.rs new file mode 100644 index 0000000000..a5d9db04fd --- /dev/null +++ b/identity_agent/src/agent/config.rs @@ -0,0 +1,18 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::time::Duration; + +/// Configuration options for a [`Agent`](crate::handler::Agent). +#[derive(Debug, Clone)] +pub(crate) struct AgentConfig { + pub(crate) timeout: Duration, +} + +impl Default for AgentConfig { + fn default() -> Self { + Self { + timeout: Duration::from_secs(30), + } + } +} diff --git a/identity_agent/src/agent/endpoint.rs b/identity_agent/src/agent/endpoint.rs new file mode 100644 index 0000000000..c5f40bffc6 --- /dev/null +++ b/identity_agent/src/agent/endpoint.rs @@ -0,0 +1,142 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::borrow::Cow; +use std::fmt::Display; +use std::str::Split; + +use serde::Deserialize; +use serde::Serialize; + +use crate::agent::Error; +use crate::agent::Result as AgentResult; + +/// A path-like identifier for a handler request. As an example, `identity/resolve` +/// could be the endpoint of the "resolve" request within the "identity" namespace. +/// +/// The namespace and request are separated by a slash and only allow alphabetic ascii characters and `_`. +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(try_from = "String", into = "String")] +pub struct Endpoint { + name: Cow<'static, str>, +} + +impl Endpoint { + /// Checks whether the given `string` is a valid [`Endpoint`]. + fn validate(string: &str) -> AgentResult<()> { + let mut split: Split<'_, char> = string.split('/'); + + // Once the Never (`!`) type lands in stable Rust, we can try to map the `None` variant to ! instead. + let namespace: &str = split.next().expect("split always returns at least one element"); + let request: &str = split.next().ok_or(Error::InvalidEndpoint)?; + + let is_valid_segment = + |segment: &str| !segment.is_empty() && segment.chars().all(|c| c.is_ascii_alphabetic() || c == '_'); + + if !is_valid_segment(namespace) || !is_valid_segment(request) { + return Err(Error::InvalidEndpoint); + } + + if split.next().is_some() { + return Err(Error::InvalidEndpoint); + } + + Ok(()) + } +} + +impl TryFrom<&'static str> for Endpoint { + type Error = Error; + + /// Creates a new endpoint from a string. Returns an [`Error::InvalidEndpoint`] + /// if disallowed characters are encountered. + fn try_from(endpoint: &'static str) -> Result { + Self::validate(endpoint)?; + + Ok(Self { + name: Cow::Borrowed(endpoint), + }) + } +} + +impl TryFrom for Endpoint { + type Error = Error; + + /// Creates a new endpoint from a string. Returns an [`Error::InvalidEndpoint`] + /// if disallowed characters are encountered. + fn try_from(endpoint: String) -> Result { + Self::validate(&endpoint)?; + + Ok(Self { + name: Cow::Owned(endpoint), + }) + } +} + +impl Display for Endpoint { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(self.name.as_ref()) + } +} + +impl AsRef for Endpoint { + fn as_ref(&self) -> &str { + self.name.as_ref() + } +} + +impl From for String { + fn from(endpoint: Endpoint) -> Self { + endpoint.name.into_owned() + } +} + +#[cfg(test)] +mod tests { + use identity_core::convert::FromJson; + + use crate::agent::Endpoint; + use crate::agent::Error; + + #[test] + fn test_endpoint_invalid() { + for invalid_endpoint in [ + "", + "/", + "//", + "a/", + "/b", + "a/b/", + "a/b/c", + "a/b/c/d", + "1a/b", + "a/b2", + "námespace/endpoint", + "namespace/hyphenated-word", + ] { + assert!(matches!( + Endpoint::try_from(invalid_endpoint).unwrap_err(), + Error::InvalidEndpoint + ),); + } + } + + #[test] + fn test_endpoint_valid() { + for valid_endpoint in ["a/b", "longer/word", "longer_endpoint/underscored_word"] { + assert!( + Endpoint::try_from(valid_endpoint).is_ok(), + "expected `{valid_endpoint}` to be a valid endpoint" + ); + } + } + + #[test] + fn test_endpoint_deserialization_validates() { + let err = Endpoint::from_json(r#"{ "name": "a/b/invalid" }"#).unwrap_err(); + assert!(matches!( + err, + identity_core::Error::DecodeJSON(serde_json::Error { .. }) + )); + } +} diff --git a/identity_agent/src/agent/errors.rs b/identity_agent/src/agent/errors.rs new file mode 100644 index 0000000000..f99b0ce353 --- /dev/null +++ b/identity_agent/src/agent/errors.rs @@ -0,0 +1,117 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use libp2p::request_response::OutboundFailure; + +use crate::didcomm::ThreadId; + +/// The `Result` type for the agent. +pub type Result = std::result::Result; + +/// Errors that can occur during agent execution. +#[non_exhaustive] +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[non_exhaustive] + #[error("transport error during {0}")] + TransportError(&'static str, #[source] libp2p::TransportError), + #[error("invalid endpoint")] + InvalidEndpoint, + #[non_exhaustive] + #[error("failure during sending an outbound request and receiving the response")] + OutboundFailure(#[source] OutboundFailure), + #[error("unexpected request `{0}`")] + UnexpectedRequest(String), + #[error("handler invocation error: {0}")] + HandlerInvocationError(String), + #[non_exhaustive] + #[error("{location} serialization failed during {context} due to: {error_message}")] + SerializationFailure { + location: ErrorLocation, + context: String, + error_message: String, + }, + #[error("{location} deserialization failed during {context} due to: {error_message}")] + DeserializationFailure { + location: ErrorLocation, + context: String, + error_message: String, + }, + #[error("thread with id `{0}` not found")] + ThreadNotFound(ThreadId), + #[error("awaiting message timed out on thread `{0}`")] + AwaitTimeout(ThreadId), + #[error("handler was shutdown")] + Shutdown, + #[error("handler identity missing")] + IdentityMissing, +} + +/// Errors that can occur on the remote agent. +#[non_exhaustive] +#[derive(Debug, thiserror::Error, serde::Serialize, serde::Deserialize)] +pub enum RemoteSendError { + #[error("unexpected request: {0}")] + UnexpectedRequest(String), + #[error("handler invocation error: {0}")] + HandlerInvocationError(String), + #[error("{location} serialization failed during {context} due to: {error_message}")] + SerializationFailure { + location: ErrorLocation, + context: String, + error_message: String, + }, + #[error("{location} deserialization failed during {context} due to: {error_message}")] + DeserializationFailure { + location: ErrorLocation, + context: String, + error_message: String, + }, +} + +impl From for Error { + fn from(err: RemoteSendError) -> Self { + match err { + RemoteSendError::UnexpectedRequest(req) => Error::UnexpectedRequest(req), + RemoteSendError::HandlerInvocationError(err) => Error::HandlerInvocationError(err), + RemoteSendError::DeserializationFailure { + location, + context, + error_message, + } => Error::DeserializationFailure { + location, + context, + error_message, + }, + RemoteSendError::SerializationFailure { + location, + context, + error_message, + } => Error::SerializationFailure { + location, + context, + error_message, + }, + } + } +} + +/// The location of an error, either locally or remotely. +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub enum ErrorLocation { + /// The error occured locally. + Local, + /// The error occured remotely. + Remote, +} + +impl std::fmt::Display for ErrorLocation { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let display = match self { + ErrorLocation::Local => "local", + ErrorLocation::Remote => "remote", + }; + + f.write_str(display) + } +} diff --git a/identity_agent/src/agent/handler.rs b/identity_agent/src/agent/handler.rs new file mode 100644 index 0000000000..bdcc5cddfd --- /dev/null +++ b/identity_agent/src/agent/handler.rs @@ -0,0 +1,106 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::fmt::Debug; +use std::future::Future; +use std::marker::PhantomData; +use std::pin::Pin; + +use crate::agent::ErrorLocation; +use crate::agent::HandlerRequest; +use crate::agent::RemoteSendError; +use crate::agent::RequestContext; + +/// A boxed future that is `Send`. +pub(crate) type BoxFuture<'me, T> = Pin + Send + 'me>>; + +/// Handlers are objects that encapsulate state and behavior. +/// +/// Handlers handle one or more requests by implementing this trait one or more times +/// for different `HandlerRequest` types. +/// +/// The requests for a handler are handled synchronously, meaning that the calling agent waits for +/// the handler to return its result before continuing. +#[async_trait::async_trait] +pub trait Handler: Debug + 'static { + /// Called when the agent receives a request of type `REQ`. + /// The result will be returned to the calling agent. + async fn handle(&self, request: RequestContext) -> REQ::Response; +} + +/// A trait that wraps a synchronous handler implementation and erases its type. +/// This allows holding handlers with different concrete types in the same collection. +pub(crate) trait AbstractHandler: Debug + Send + Sync + 'static { + fn handle(&self, request: RequestContext>) -> BoxFuture<'_, Result, RemoteSendError>>; +} + +/// A wrapper around synchronous handler implementations that is used for +/// type erasure together with [`AbstractHandler`]. +#[derive(Debug)] +pub(crate) struct HandlerWrapper +where + REQ: HandlerRequest + Send + Sync, + HND: Handler + Send + Sync, +{ + handler: HND, + _phantom_req: PhantomData, +} + +impl HandlerWrapper +where + REQ: HandlerRequest + Send + Sync, + HND: Handler + Send + Sync, +{ + pub(crate) fn new(handler: HND) -> Self { + Self { + handler, + _phantom_req: PhantomData, + } + } +} + +impl AbstractHandler for HandlerWrapper +where + REQ: HandlerRequest + Send + Sync, + REQ::Response: Send, + HND: Handler + Send + Sync, +{ + fn handle(&self, request: RequestContext>) -> BoxFuture<'_, Result, RemoteSendError>> { + let future = async move { + let req: REQ = + serde_json::from_slice(&request.input).map_err(|error| RemoteSendError::DeserializationFailure { + location: ErrorLocation::Remote, + context: format!( + "deserializing the received bytes into the handler's expected type `{}`", + std::any::type_name::() + ), + error_message: error.to_string(), + })?; + + let req: RequestContext = request.convert(req); + let result: REQ::Response = self.handler.handle(req).await; + serialize_response::(&result) + }; + + Box::pin(future) + } +} + +#[inline(always)] +fn serialize_response(input: &REQ::Response) -> Result, RemoteSendError> { + log::debug!( + "attempt response serialization into {:?}", + std::any::type_name::() + ); + + let response: Vec = serde_json::to_vec(&input).map_err(|error| RemoteSendError::SerializationFailure { + location: ErrorLocation::Remote, + context: format!( + "serializing the handler's response into `{}`", + std::any::type_name::() + ), + error_message: error.to_string(), + })?; + + Ok(response) +} diff --git a/identity_agent/src/agent/mod.rs b/identity_agent/src/agent/mod.rs new file mode 100644 index 0000000000..45e84559da --- /dev/null +++ b/identity_agent/src/agent/mod.rs @@ -0,0 +1,23 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +#[allow(clippy::module_inception)] +mod agent; +mod agent_builder; +mod agent_state; +mod config; +mod endpoint; +mod errors; +mod handler; +mod request; +mod request_context; + +pub use agent::*; +pub use agent_builder::*; +pub(crate) use agent_state::*; +pub(crate) use config::*; +pub use endpoint::*; +pub use errors::*; +pub use handler::*; +pub use request::*; +pub use request_context::*; diff --git a/identity_agent/src/agent/request.rs b/identity_agent/src/agent/request.rs new file mode 100644 index 0000000000..034d846bb6 --- /dev/null +++ b/identity_agent/src/agent/request.rs @@ -0,0 +1,35 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::fmt::Debug; + +use serde::de::DeserializeOwned; +use serde::Deserialize; +use serde::Serialize; + +use crate::agent::Endpoint; + +/// Expresses the synchronicity of a request at runtime, i.e. whether a request +/// is handled synchronously or asynchronously. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +pub enum RequestMode { + Synchronous, + Asynchronous, +} + +/// A request sent to a handler with a response of type `Response`. +/// +/// This request is sent synchronously, which means waiting for +/// the result of that invocation on the remote agent. +pub trait HandlerRequest: Debug + Serialize + DeserializeOwned + Send + 'static { + /// The response type for this request. + type Response: Debug + Serialize + DeserializeOwned + 'static; + + /// The unique identifier for this request. See [`Endpoint`] for more details. + fn endpoint() -> Endpoint; + + /// Whether this request is synchronous or asynchronous. + fn request_mode() -> RequestMode { + RequestMode::Synchronous + } +} diff --git a/identity_agent/src/agent/request_context.rs b/identity_agent/src/agent/request_context.rs new file mode 100644 index 0000000000..62c6148dd4 --- /dev/null +++ b/identity_agent/src/agent/request_context.rs @@ -0,0 +1,32 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use crate::agent::AgentId; +use crate::agent::Endpoint; + +/// A request paired with some context such as the sender's peer id. +#[non_exhaustive] +#[derive(Debug, Clone)] +pub struct RequestContext { + /// The request type. + pub input: T, + /// The [`AgentId`] of the sender. + pub agent_id: AgentId, + /// The [`Endpoint`] of this request. + pub endpoint: Endpoint, +} + +impl RequestContext { + pub(crate) fn new(input: T, agent_id: AgentId, endpoint: Endpoint) -> Self { + Self { + input, + agent_id, + endpoint, + } + } + + /// Convert this context's inner type to another one. + pub(crate) fn convert(self, input: I) -> RequestContext { + RequestContext::new(input, self.agent_id, self.endpoint) + } +} diff --git a/identity_agent/src/didcomm/agent.rs b/identity_agent/src/didcomm/agent.rs new file mode 100644 index 0000000000..8c840c746b --- /dev/null +++ b/identity_agent/src/didcomm/agent.rs @@ -0,0 +1,295 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::collections::HashMap; +use std::sync::Arc; + +use dashmap::DashMap; +use futures::channel::oneshot; +use identity_iota_core::document::IotaDocument; +use libp2p::Multiaddr; +use serde::de::DeserializeOwned; + +use crate::agent::Agent; +use crate::agent::AgentId; +use crate::agent::Endpoint; +use crate::agent::Error; +use crate::agent::ErrorLocation; +use crate::agent::HandlerRequest; +use crate::agent::RemoteSendError; +use crate::agent::RequestMode; +use crate::agent::Result as AgentResult; +use crate::didcomm::dcpm::DidCommPlaintextMessage; +use crate::didcomm::AbstractDidCommHandler; +use crate::didcomm::DidCommRequest; +use crate::didcomm::ThreadId; +use crate::p2p::InboundRequest; +use crate::p2p::NetCommander; +use crate::p2p::RequestMessage; +use crate::p2p::ThreadRequest; + +/// The identity of a [`DidCommAgent`]. +/// +/// Note: Currently an incomplete implementation. +#[derive(Debug, Clone)] +pub struct DidCommAgentIdentity { + // TODO: This type is meant to be used in a future update. + #[allow(dead_code)] + pub document: IotaDocument, +} + +/// The internal state of a [`DidCommAgent`]. +#[derive(Debug)] +pub struct DidCommAgentState { + pub(crate) handlers: DidCommHandlerMap, + pub(crate) threads_receiver: DashMap>, + pub(crate) threads_sender: DashMap>, + // TODO: See above. + #[allow(dead_code)] + pub(crate) identity: DidCommAgentIdentity, +} + +impl DidCommAgentState { + pub(crate) fn new(handlers: DidCommHandlerMap, identity: DidCommAgentIdentity) -> Self { + Self { + handlers, + threads_receiver: DashMap::new(), + threads_sender: DashMap::new(), + identity, + } + } +} + +/// A [`DidCommAgent`] is an extension of an [`Agent`] with support for sending and awaiting [`DidCommRequest`]s. +/// +/// An agent can be used to send requests to other, remote agents, and fowards incoming requests +/// to attached handlers. It is a frontend for an event loop running in the background, which invokes +/// user-attached handlers. Agents can be cloned without cloning the event loop, and doing so +/// is a cheap operation. +/// +/// Handlers are attached at agent build time, using the [`DidCommAgentBuilder`](crate::didcomm::DidCommAgentBuilder). +/// +/// While an [`Agent`] only supports attachements of synchronous [`Handler`](crate::agent::Handler)s, +/// a [`DidCommAgent`] additionally supports asynchronous [`DidCommHandler`](crate::didcomm::DidCommHandler)s. +/// +/// After shutting down the event loop of an agent using [`DidCommAgent::shutdown`], other clones of the +/// agent will receive [`Error::Shutdown`] when attempting to interact with the event loop. +#[derive(Debug, Clone)] +pub struct DidCommAgent { + pub(crate) agent: Agent, + pub(crate) state: Arc, +} + +impl DidCommAgent { + pub(crate) fn commander_mut(&mut self) -> &mut NetCommander { + self.agent.commander_mut() + } + + /// Let this agent handle the given `request`, by invoking the appropriate handler, if attached. + /// This consumes the agent because it passes itself to the handler. + /// The agent will thus typically be cloned before calling this method. + pub(crate) fn handle_request(self, request: InboundRequest) { + match request.request_mode { + RequestMode::Asynchronous => self.handle_async_request(request), + RequestMode::Synchronous => self.agent.handle_sync_request(request), + } + } + + /// See [`Agent::start_listening`]. + pub async fn start_listening(&mut self, address: Multiaddr) -> AgentResult { + self.agent.start_listening(address).await + } + + /// See [`Agent::agent_id`]. + pub fn agent_id(&self) -> AgentId { + self.agent.agent_id() + } + + /// See [`Agent::addresses`]. + pub async fn addresses(&mut self) -> AgentResult> { + self.agent.addresses().await + } + + /// See [`Agent::add_agent_address`]. + pub async fn add_agent_address(&mut self, agent_id: AgentId, address: Multiaddr) -> AgentResult<()> { + self.agent.add_agent_address(agent_id, address).await + } + + /// See [`Agent::add_agent_addresses`]. + pub async fn add_agent_addresses(&mut self, agent_id: AgentId, addresses: Vec) -> AgentResult<()> { + self.agent.add_agent_addresses(agent_id, addresses).await + } + + /// See [`Agent::shutdown`]. + pub async fn shutdown(self) -> AgentResult<()> { + self.agent.shutdown().await + } + + /// See [`Agent::send_request`]. + pub async fn send_request( + &mut self, + agent_id: AgentId, + request: REQ, + ) -> AgentResult { + self.agent.send_request(agent_id, request).await + } + + /// Sends an asynchronous DIDComm request to an agent. + /// + /// To receive a possible response, call [`DidCommAgent::await_didcomm_request`] with the same `thread_id`. + pub async fn send_didcomm_request( + &mut self, + agent_id: AgentId, + thread_id: &ThreadId, + message: REQ, + ) -> AgentResult<()> { + let endpoint: Endpoint = REQ::endpoint(); + let request_mode: RequestMode = REQ::request_mode(); + + let dcpm = DidCommPlaintextMessage::new(thread_id.to_owned(), endpoint.to_string(), message); + + self.create_thread_channels(thread_id); + + let dcpm_vec = serde_json::to_vec(&dcpm).map_err(|err| Error::SerializationFailure { + location: ErrorLocation::Local, + context: "send message".to_owned(), + error_message: err.to_string(), + })?; + + log::debug!("sending DIDComm request on endpoint `{endpoint}`"); + + let message: RequestMessage = RequestMessage::new(endpoint, request_mode, dcpm_vec); + + let response = self.commander_mut().send_request(agent_id, message).await?; + + serde_json::from_slice::>(&response.0).map_err(|err| { + Error::DeserializationFailure { + location: ErrorLocation::Local, + context: "send message".to_owned(), + error_message: err.to_string(), + } + })??; + + Ok(()) + } + + /// Wait for a message on a given `thread_id`. This can only be called successfully if + /// [`DidCommAgent::send_didcomm_request`] was called on the same `thread_id` previously. + /// Calling `send_didcomm_request` multiple times still only allows to await one message on the thread. + /// + /// This will return a timeout error if no message is received within the duration passed + /// to [`DidCommAgentBuilder::timeout`](crate::didcomm::DidCommAgentBuilder::timeout). + pub async fn await_didcomm_request( + &mut self, + thread_id: &ThreadId, + ) -> AgentResult> { + if let Some(receiver) = self.state.threads_receiver.remove(thread_id) { + // Receiving + Deserialization + let inbound_request = tokio::time::timeout(self.agent.state().config.timeout, receiver.1) + .await + .map_err(|_| Error::AwaitTimeout(receiver.0.clone()))? + .map_err(|_| Error::ThreadNotFound(receiver.0))?; + + let message: DidCommPlaintextMessage = + serde_json::from_slice(inbound_request.input.as_ref()).map_err(|err| Error::DeserializationFailure { + location: ErrorLocation::Local, + context: "await message".to_owned(), + error_message: err.to_string(), + })?; + + log::debug!("awaited message {}", inbound_request.endpoint); + + Ok(message) + } else { + log::warn!("attempted to wait for a message on thread {thread_id:?}, which does not exist"); + Err(Error::ThreadNotFound(thread_id.to_owned())) + } + } + + /// Creates the channels used to await a message on a thread. + fn create_thread_channels(&mut self, thread_id: &ThreadId) { + let (sender, receiver) = oneshot::channel(); + + // The logic is that for every received message on a thread, + // there must be a preceding `send_didcomm_request` on that same thread. + // Note that on the receiving handler, the very first message of a protocol + // is not awaited through `await_didcomm_request`, so it does not need to follow these rules. + self.state.threads_sender.insert(thread_id.to_owned(), sender); + self.state.threads_receiver.insert(thread_id.to_owned(), receiver); + } + + #[inline(always)] + pub(crate) fn handle_async_request(mut self, request: InboundRequest) { + let _ = tokio::spawn(async move { + match self.state.handlers.get(&request.endpoint) { + Some(handler) => { + let handler: &dyn AbstractDidCommHandler = handler.as_ref(); + + handler.handle(self.clone(), request).await; + } + None => { + handler_not_found(&mut self, request).await; + } + } + }); + } +} + +/// Invoked when no handler was found that can handle the received request. +/// Attempts to find a thread waiting for the received message, +/// otherwise returns an error to the calling agent. +async fn handler_not_found(handler: &mut DidCommAgent, request: InboundRequest) { + let result: Result<(), RemoteSendError> = + match serde_json::from_slice::>(&request.input) { + Err(error) => Err(RemoteSendError::DeserializationFailure { + location: ErrorLocation::Remote, + context: "DIDComm plaintext message deserialization".to_owned(), + error_message: error.to_string(), + }), + Ok(plaintext_msg) => { + let thread_id = plaintext_msg.thread_id(); + + match handler.state.threads_sender.remove(thread_id) { + Some(sender) => { + let thread_request = ThreadRequest { + endpoint: request.endpoint, + input: request.input, + }; + + if sender.1.send(thread_request).is_err() { + log::warn!("unable to send request with thread id `{thread_id}`"); + } + + Ok(()) + } + None => { + log::info!( + "no handler or thread found for the received message `{}`", + request.endpoint + ); + // The assumption is that DID authentication is done before this point, so this is not + // considered an information leak, e.g. to enumerate thread ids. + Err(RemoteSendError::UnexpectedRequest(format!( + "thread id `{}` not found", + thread_id + ))) + } + } + } + }; + + let send_result = crate::agent::send_response( + handler.commander_mut(), + result, + request.response_channel, + request.request_id, + ) + .await; + + if let Err(err) = send_result { + log::error!("could not acknowledge request due to: {err:?}"); + } +} + +/// A map from an endpoint to the handler that handles its requests. +pub(crate) type DidCommHandlerMap = HashMap>; diff --git a/identity_agent/src/didcomm/agent_builder.rs b/identity_agent/src/didcomm/agent_builder.rs new file mode 100644 index 0000000000..510fd6db71 --- /dev/null +++ b/identity_agent/src/didcomm/agent_builder.rs @@ -0,0 +1,161 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::collections::HashMap; +use std::sync::Arc; +use std::time::Duration; + +use futures::AsyncRead; +use futures::AsyncWrite; +use futures::FutureExt; +use libp2p::core::Executor; +use libp2p::dns::TokioDnsConfig; +use libp2p::identity::Keypair; +use libp2p::tcp::TokioTcpConfig; +use libp2p::websocket::WsConfig; +use libp2p::Transport; + +use crate::agent::Agent; +use crate::agent::AgentBuilder; +use crate::agent::AgentState; +use crate::agent::Error; +use crate::agent::Handler; +use crate::agent::HandlerRequest; +use crate::agent::Result as AgentResult; +use crate::didcomm::AbstractDidCommHandler; +use crate::didcomm::DidCommAgent; +use crate::didcomm::DidCommAgentIdentity; +use crate::didcomm::DidCommAgentState; +use crate::didcomm::DidCommHandler; +use crate::didcomm::DidCommHandlerMap; +use crate::didcomm::DidCommHandlerWrapper; +use crate::didcomm::DidCommRequest; +use crate::p2p::EventLoop; +use crate::p2p::InboundRequest; +use crate::p2p::NetCommander; + +/// A builder for [`DidCommAgent`]s to customize its configuration and attach handlers. +pub struct DidCommAgentBuilder { + inner: AgentBuilder, + identity: Option, + didcomm_handlers: DidCommHandlerMap, +} + +impl DidCommAgentBuilder { + /// Create a new builder with the default configuration. + pub fn new() -> DidCommAgentBuilder { + Self { + inner: AgentBuilder::new(), + identity: None, + didcomm_handlers: HashMap::new(), + } + } + + /// See [`AgentBuilder::keypair`]. + #[must_use] + pub fn keypair(mut self, keypair: Keypair) -> Self { + self.inner.keypair = Some(keypair); + self + } + + /// Sets the timeout for [`DidCommAgent::await_didcomm_request`] and the underlying libp2p + /// [`RequestResponse`](libp2p::request_response::RequestResponse) protocol. + #[must_use] + pub fn timeout(mut self, timeout: Duration) -> Self { + self.inner.config.timeout = timeout; + self + } + + /// Set the [`DidCommAgentIdentity`] that will be used for DIDComm related tasks, such as en- and decryption. + #[must_use] + pub fn identity(mut self, identity: DidCommAgentIdentity) -> Self { + self.identity = Some(identity); + self + } + + /// Attaches a [`DidCommHandler`] to this agent. + /// + /// This means that when the agent receives a request of type `REQ`, it will invoke this handler. + /// + /// Calling this method with a `REQ` type whose endpoint is already attached to a handler + /// will overwrite the previous attachment. + pub fn attach_didcomm(&mut self, handler: HND) + where + HND: DidCommHandler + Send + Sync, + REQ: DidCommRequest + Send + Sync, + { + self.didcomm_handlers.insert( + REQ::endpoint(), + Box::new(DidCommHandlerWrapper::new(handler)) as Box, + ); + } + + /// See [`AgentBuilder::attach`]. + pub fn attach(&mut self, handler: HND) + where + HND: Handler + Send + Sync, + REQ: HandlerRequest + Send + Sync, + REQ::Response: Send, + { + self.inner.attach(handler); + } + + /// See [`AgentBuilder::build`]. + pub async fn build(self) -> AgentResult { + let transport: _ = { + let dns_tcp_transport: TokioDnsConfig<_> = TokioDnsConfig::system(TokioTcpConfig::new().nodelay(true)) + .map_err(|err| Error::TransportError("building transport", libp2p::TransportError::Other(err)))?; + let ws_transport: WsConfig<_> = WsConfig::new( + TokioDnsConfig::system(TokioTcpConfig::new().nodelay(true)) + .map_err(|err| Error::TransportError("building transport", libp2p::TransportError::Other(err)))?, + ); + dns_tcp_transport.or_transport(ws_transport) + }; + + self.build_with_transport(transport).await + } + + /// See [`AgentBuilder::build_with_transport`]. + pub async fn build_with_transport(self, transport: TRA) -> AgentResult + where + TRA: Transport + Sized + Send + Sync + 'static, + TRA::Output: AsyncRead + AsyncWrite + Unpin + Send + 'static, + TRA::Dial: Send + 'static, + TRA::Listener: Send + 'static, + TRA::ListenerUpgrade: Send + 'static, + TRA::Error: Send + Sync, + { + let executor = Box::new(|fut| { + tokio::spawn(fut); + }); + + let (event_loop, handler_state, net_commander): (EventLoop, AgentState, NetCommander) = + self.inner.build_constituents(transport, executor.clone()).await?; + + let state: DidCommAgentState = + DidCommAgentState::new(self.didcomm_handlers, self.identity.ok_or(Error::IdentityMissing)?); + + let agent: Agent = Agent::new(net_commander, Arc::new(handler_state)); + + let didcomm_agent: DidCommAgent = DidCommAgent { + agent, + state: Arc::new(state), + }; + + let didcomm_agent_clone: DidCommAgent = didcomm_agent.clone(); + + let event_handler = move |event: InboundRequest| { + didcomm_agent_clone.clone().handle_request(event); + }; + + executor.exec(event_loop.run(event_handler).boxed()); + + Ok(didcomm_agent) + } +} + +impl Default for DidCommAgentBuilder { + fn default() -> Self { + Self::new() + } +} diff --git a/identity_agent/src/didcomm/dcpm.rs b/identity_agent/src/didcomm/dcpm.rs new file mode 100644 index 0000000000..dd46e3aee2 --- /dev/null +++ b/identity_agent/src/didcomm/dcpm.rs @@ -0,0 +1,63 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use crate::agent::Endpoint; +use crate::didcomm::DidCommRequest; +use crate::didcomm::ThreadId; + +/// A DIDComm Plaintext Message. Implementation is currently rudimentary. +/// +/// See also: . +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub struct DidCommPlaintextMessage { + pub(crate) typ: String, + pub(crate) id: ThreadId, + pub(crate) thid: Option, + pub(crate) pthid: Option, + #[serde(rename = "type")] + pub(crate) type_: String, + pub(crate) from: String, + pub(crate) to: String, + pub(crate) created_time: u32, + pub(crate) expires_time: u32, + pub(crate) body: T, +} + +impl DidCommPlaintextMessage { + pub(crate) fn new(id: ThreadId, type_: String, body: T) -> Self { + DidCommPlaintextMessage { + id, + type_, + body, + typ: String::new(), + thid: None, + pthid: None, + from: String::new(), + to: String::new(), + created_time: 0, + expires_time: 0, + } + } + + /// Returns the `ThreadId` of the message. + pub fn thread_id(&self) -> &ThreadId { + match self.thid.as_ref() { + Some(thid) => thid, + None => &self.id, + } + } + + /// Returns the body of the message. + pub fn body(&self) -> &T { + &self.body + } +} + +impl DidCommRequest for DidCommPlaintextMessage +where + T: DidCommRequest, +{ + fn endpoint() -> Endpoint { + T::endpoint() + } +} diff --git a/identity_agent/src/didcomm/handler.rs b/identity_agent/src/didcomm/handler.rs new file mode 100644 index 0000000000..8e962e8a77 --- /dev/null +++ b/identity_agent/src/didcomm/handler.rs @@ -0,0 +1,143 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::fmt::Debug; +use std::marker::PhantomData; + +use libp2p::request_response::RequestId; +use libp2p::request_response::ResponseChannel; +use serde::Serialize; + +use crate::agent::BoxFuture; +use crate::agent::Endpoint; +use crate::agent::ErrorLocation; +use crate::agent::RemoteSendError; +use crate::agent::RequestContext; +use crate::didcomm::DidCommAgent; +use crate::didcomm::DidCommRequest; +use crate::p2p::InboundRequest; +use crate::p2p::NetCommander; +use crate::p2p::ResponseMessage; + +/// Handlers are objects that encapsulate state and behavior. +/// +/// A DidCommHandler handles one or more requests by implementing this trait one or more times +/// for different `DidCommRequest` types. +/// +/// The requests for a DidCommHandler are handled asynchronously, meaning that the calling agent does +/// not wait for the handler to complete its invocation. If that is desired, the [`Handler`](crate::agent::Handler) +/// trait should be implemented instead. +#[async_trait::async_trait] +pub trait DidCommHandler: Debug + 'static { + /// Called when the agent receives a request of type `REQ`. + async fn handle(&self, handler: DidCommAgent, request: RequestContext); +} + +/// A trait that wraps a DidCommHandler implementation and erases its type. +/// This allows holding handlers with different concrete types in the same collection. +pub(crate) trait AbstractDidCommHandler: Debug + Send + Sync + 'static { + fn handle(&self, handler: DidCommAgent, request: InboundRequest) -> BoxFuture<'_, ()>; +} + +/// A wrapper around asynchronous handler implementations that is used for +/// type erasure together with [`AbstractAsyncHandler`]. +#[derive(Debug)] +pub(crate) struct DidCommHandlerWrapper +where + REQ: DidCommRequest + Send + Sync, + HND: DidCommHandler + Send + Sync, +{ + handler: HND, + _phantom_req: PhantomData, +} + +impl DidCommHandlerWrapper +where + REQ: DidCommRequest + Send + Sync, + HND: DidCommHandler + Send + Sync, +{ + pub(crate) fn new(handler: HND) -> Self { + Self { + handler, + _phantom_req: PhantomData, + } + } +} + +impl AbstractDidCommHandler for DidCommHandlerWrapper +where + REQ: DidCommRequest + Send + Sync, + HND: DidCommHandler + Send + Sync, +{ + fn handle(&self, mut agent: DidCommAgent, request: InboundRequest) -> BoxFuture<'_, ()> { + let future: _ = async move { + let req: REQ = match serde_json::from_slice::<'_, REQ>(&request.input).map_err(|error| { + RemoteSendError::DeserializationFailure { + location: ErrorLocation::Remote, + context: format!( + "deserializing the received bytes into the handler's expected type `{}`", + std::any::type_name::() + ), + error_message: error.to_string(), + } + }) { + Ok(req) => { + // Acknowledge request was received and understood. + send_didcomm_response( + agent.commander_mut(), + Ok(()), + &request.endpoint, + request.response_channel, + request.request_id, + ) + .await; + + req + } + Err(err) => { + send_didcomm_response( + agent.commander_mut(), + Result::<(), RemoteSendError>::Err(err), + &request.endpoint, + request.response_channel, + request.request_id, + ) + .await; + + // Abort because there is no request to handle and/or the calling agent is unresponsive. + return; + } + }; + + let context: RequestContext = RequestContext::new(req, request.peer_id, request.endpoint); + + self.handler.handle(agent, context).await; + }; + + Box::pin(future) + } +} + +async fn send_didcomm_response( + commander: &mut NetCommander, + response: Result, + endpoint: &Endpoint, + channel: ResponseChannel, + request_id: RequestId, +) { + match crate::agent::send_response(commander, response, channel, request_id).await { + Ok(Err(err)) => { + log::error!( + "could not send error for request on endpoint `{}` due to: {err:?}", + endpoint + ); + } + Err(err) => { + log::error!( + "could not send error for request on endpoint `{}` due to: {err:?}", + endpoint + ); + } + Ok(_) => (), + } +} diff --git a/identity_agent/src/didcomm/mod.rs b/identity_agent/src/didcomm/mod.rs new file mode 100644 index 0000000000..af00a32f10 --- /dev/null +++ b/identity_agent/src/didcomm/mod.rs @@ -0,0 +1,16 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +mod agent; +mod agent_builder; +mod dcpm; +mod handler; +mod request; +mod thread_id; + +pub use agent::*; +pub use agent_builder::*; +pub use dcpm::*; +pub use handler::*; +pub use request::*; +pub use thread_id::*; diff --git a/identity_agent/src/didcomm/request.rs b/identity_agent/src/didcomm/request.rs new file mode 100644 index 0000000000..5524334dd3 --- /dev/null +++ b/identity_agent/src/didcomm/request.rs @@ -0,0 +1,26 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::fmt::Debug; + +use serde::de::DeserializeOwned; +use serde::Serialize; + +use crate::agent::Endpoint; +use crate::agent::RequestMode; + +/// A message that can be sent to a remote handler without an explicit response. +/// +/// This request is sent asynchronously, which means sending the request without waiting for +/// the result of that invocation on the remote agent. +/// However, an acknowledgment is returned to signal that an +/// appropriate handler exists that can handle the request, or an error, if the opposite is true. +pub trait DidCommRequest: Debug + Serialize + DeserializeOwned + Send + 'static { + /// The unique identifier for this request. See [`Endpoint`] for more details. + fn endpoint() -> Endpoint; + + /// Whether this request is synchronous or asynchronous. + fn request_mode() -> RequestMode { + RequestMode::Asynchronous + } +} diff --git a/identity_agent/src/didcomm/thread_id.rs b/identity_agent/src/didcomm/thread_id.rs new file mode 100644 index 0000000000..9f5abc2df8 --- /dev/null +++ b/identity_agent/src/didcomm/thread_id.rs @@ -0,0 +1,23 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::fmt::Display; + +use uuid::Uuid; + +/// An identifier for a DIDComm messaging thread. +#[derive(Debug, Clone, Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct ThreadId(Uuid); + +impl ThreadId { + #[allow(clippy::new_without_default)] + pub fn new() -> Self { + Self(Uuid::new_v4()) + } +} + +impl Display for ThreadId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} diff --git a/identity_agent/src/lib.rs b/identity_agent/src/lib.rs new file mode 100644 index 0000000000..7636710a4e --- /dev/null +++ b/identity_agent/src/lib.rs @@ -0,0 +1,21 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +#![doc = include_str!("../README.md")] +#![forbid(unsafe_code)] +#![warn( + rust_2018_idioms, + unreachable_pub, + rustdoc::broken_intra_doc_links, + rustdoc::private_intra_doc_links, + rustdoc::private_doc_tests +)] + +pub mod agent; +pub mod didcomm; +mod p2p; +#[cfg(test)] +mod tests; + +pub use libp2p::identity::Keypair as IdentityKeypair; +pub use libp2p::Multiaddr; diff --git a/identity_agent/src/p2p/behaviour.rs b/identity_agent/src/p2p/behaviour.rs new file mode 100644 index 0000000000..583481e9a0 --- /dev/null +++ b/identity_agent/src/p2p/behaviour.rs @@ -0,0 +1,78 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::io::{self}; + +use futures::AsyncRead; +use futures::AsyncWrite; +use futures::AsyncWriteExt; +use libp2p::core::upgrade; +use libp2p::core::ProtocolName; +use libp2p::request_response::RequestResponseCodec; + +use crate::p2p::RequestMessage; +use crate::p2p::ResponseMessage; + +/// The protocol of the agent. +#[derive(Debug, Clone)] +pub(crate) struct AgentProtocol(); + +/// Defines the request and response types for the libp2p RequestResponse layer. +#[derive(Clone)] +pub(crate) struct AgentRequestResponseCodec(); + +impl ProtocolName for AgentProtocol { + fn protocol_name(&self) -> &[u8] { + "/agent/0.1.0".as_bytes() + } +} + +#[async_trait::async_trait] +impl RequestResponseCodec for AgentRequestResponseCodec { + type Protocol = AgentProtocol; + type Request = RequestMessage; + type Response = ResponseMessage; + + async fn read_request(&mut self, _protocol: &Self::Protocol, io: &mut T) -> io::Result + where + T: AsyncRead + Unpin + Send, + { + let vec = upgrade::read_length_prefixed(io, 1_000_000).await?; + + let request: RequestMessage = RequestMessage::from_bytes(vec.as_ref())?; + + Ok(request) + } + + async fn read_response(&mut self, _protocol: &Self::Protocol, io: &mut T) -> io::Result + where + T: AsyncRead + Unpin + Send, + { + let vec = upgrade::read_length_prefixed(io, 1_000_000).await?; + + Ok(ResponseMessage(vec)) + } + + async fn write_request(&mut self, _protocol: &Self::Protocol, io: &mut T, request: Self::Request) -> io::Result<()> + where + T: AsyncWrite + Unpin + Send, + { + let bytes: Vec = request.to_bytes()?; + + upgrade::write_length_prefixed(io, bytes).await?; + io.close().await + } + + async fn write_response( + &mut self, + _protocol: &Self::Protocol, + io: &mut T, + ResponseMessage(data): Self::Response, + ) -> io::Result<()> + where + T: AsyncWrite + Unpin + Send, + { + upgrade::write_length_prefixed(io, data).await?; + io.close().await + } +} diff --git a/identity_agent/src/p2p/event_loop.rs b/identity_agent/src/p2p/event_loop.rs new file mode 100644 index 0000000000..af5f49042f --- /dev/null +++ b/identity_agent/src/p2p/event_loop.rs @@ -0,0 +1,239 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::collections::HashMap; +use std::ops::ControlFlow; + +use futures::channel::mpsc; +use futures::channel::oneshot; +use futures::FutureExt; +use futures::StreamExt; +use libp2p::core::connection::ListenerId; +use libp2p::request_response::InboundFailure; +use libp2p::request_response::OutboundFailure; +use libp2p::request_response::RequestId; +use libp2p::request_response::RequestResponse; +use libp2p::request_response::RequestResponseEvent; +use libp2p::request_response::RequestResponseMessage; +use libp2p::request_response::ResponseChannel; +use libp2p::swarm::SwarmEvent; +use libp2p::Multiaddr; +use libp2p::PeerId; +use libp2p::Swarm; +use libp2p::TransportError; + +use crate::agent::Endpoint; +use crate::agent::RequestMode; +use crate::p2p::AgentRequestResponseCodec; +use crate::p2p::RequestMessage; +use crate::p2p::ResponseMessage; +use crate::p2p::SwarmCommand; + +/// The background loop that handles libp2p swarm events and `NetCommander` commands simultaneously. +pub(crate) struct EventLoop { + swarm: Swarm>, + command_channel: mpsc::Receiver, + await_response: HashMap>>, + await_response_sent: HashMap>>, + await_listen: HashMap>>>, +} + +impl EventLoop { + /// Create a new `EventLoop` from the given `swarm` and the receiving end of a channel. The sender + /// part needs to be passed to a `NetCommander`, which allows it to send request to this loop. + pub(crate) fn new( + swarm: Swarm>, + command_channel: mpsc::Receiver, + ) -> Self { + EventLoop { + swarm, + command_channel, + await_response: HashMap::new(), + await_response_sent: HashMap::new(), + await_listen: HashMap::new(), + } + } + + /// Block on this event loop until it terminates, simultaneously handling incoming events from other agents + /// as well as request from the corresponding `NetCommander`s. + pub(crate) async fn run(mut self, event_handler: F) + where + F: Fn(InboundRequest), + { + loop { + futures::select_biased! { + event = self.swarm.select_next_some() => self.handle_swarm_event(event, &event_handler).await, + command = self.command_channel.next().fuse() => { + if let Some(c) = command { + if let ControlFlow::Break(_) = self.handle_command(c) { + break; + } + } else { + break; + } + }, + } + } + } + + async fn handle_swarm_event( + &mut self, + event: SwarmEvent, THandleErr>, + event_handler: &F, + ) where + F: Fn(InboundRequest), + { + match event { + SwarmEvent::Behaviour(RequestResponseEvent::Message { + message: RequestResponseMessage::Request { + channel, + request, + request_id, + }, + peer, + }) => { + event_handler(InboundRequest { + peer_id: peer, + endpoint: request.endpoint, + request_mode: request.request_mode, + input: request.data, + response_channel: channel, + request_id, + }); + } + SwarmEvent::Behaviour(RequestResponseEvent::Message { + message: RequestResponseMessage::Response { request_id, response }, + .. + }) => { + if let Some(response_channel) = self.await_response.remove(&request_id) { + let _ = response_channel.send(Ok(response)); + } + } + SwarmEvent::Behaviour(RequestResponseEvent::OutboundFailure { request_id, error, .. }) => { + if let Some(response_channel) = self.await_response.remove(&request_id) { + let _ = response_channel.send(Err(error)); + } + } + SwarmEvent::Behaviour(RequestResponseEvent::InboundFailure { error, request_id, .. }) => { + if let Some(response_channel) = self.await_response_sent.remove(&request_id) { + let _ = response_channel.send(Err(error)); + } + } + SwarmEvent::Behaviour(RequestResponseEvent::ResponseSent { request_id, .. }) => { + if let Some(response_channel) = self.await_response_sent.remove(&request_id) { + let _ = response_channel.send(Ok(())); + } + } + SwarmEvent::NewListenAddr { listener_id, address } => { + if let Some(response_channel) = self.await_listen.remove(&listener_id) { + let _ = response_channel.send(Ok(address)); + } + } + _ => (), + } + } + + fn handle_command(&mut self, command: SwarmCommand) -> ControlFlow<()> { + match command { + SwarmCommand::SendRequest { + peer_id: peer, + request, + response_channel, + } => { + let request_id = self.swarm.behaviour_mut().send_request(&peer, request); + self.await_response.insert(request_id, response_channel); + } + SwarmCommand::SendResponse { + response, + response_channel, + cmd_response_channel, + request_id, + } => { + if self + .swarm + .behaviour_mut() + .send_response(response_channel, ResponseMessage(response)) + .is_err() + { + if let Err(err) = cmd_response_channel.send(Err(InboundFailure::ConnectionClosed)) { + log::warn!("unable to send message `{err:?}` because receiver was dropped"); + } + } else { + self.await_response_sent.insert(request_id, cmd_response_channel); + } + } + SwarmCommand::StartListening { + address, + response_channel, + } => match self.swarm.listen_on(address) { + Ok(listener_id) => { + self.await_listen.insert(listener_id, response_channel); + } + Err(err) => { + if let Err(err) = response_channel.send(Err(err)) { + log::warn!("unable to send message `{err:?}` because receiver was dropped"); + } + } + }, + SwarmCommand::AddAddresses { + peer_id: peer, + addresses, + } => { + for addr in addresses { + self.swarm.behaviour_mut().add_address(&peer, addr); + } + } + SwarmCommand::GetAddresses { response_channel } => { + if let Err(err) = response_channel.send(self.swarm.listeners().map(ToOwned::to_owned).collect()) { + log::warn!("unable to send message `{err:?}` because receiver was dropped"); + } + } + SwarmCommand::Shutdown { response_channel } => { + // On shutdown, send error messages through all open channels + // to allow those tasks to terminate gracefully. + for (listener, channel) in std::mem::take(&mut self.await_listen).into_iter() { + let _ = self.swarm.remove_listener(listener); + let err = TransportError::Other(std::io::Error::new( + std::io::ErrorKind::Interrupted, + "handler was shut down", + )); + + let _ = channel.send(Err(err)); + } + + for (_, channel) in std::mem::take(&mut self.await_response) { + let _ = channel.send(Err(OutboundFailure::ConnectionClosed)); + } + + for (_, channel) in std::mem::take(&mut self.await_response_sent) { + let _ = channel.send(Err(InboundFailure::ConnectionClosed)); + } + + if let Err(err) = response_channel.send(()) { + log::warn!("unable to send message `{err:?}` because receiver was dropped"); + } + + return ControlFlow::Break(()); + } + } + ControlFlow::Continue(()) + } +} + +/// An inbound request as received by the p2p layer. +#[derive(Debug)] +pub(crate) struct InboundRequest { + pub(crate) peer_id: PeerId, + pub(crate) endpoint: Endpoint, + pub(crate) request_mode: RequestMode, + pub(crate) input: Vec, + pub(crate) response_channel: ResponseChannel, + pub(crate) request_id: RequestId, +} + +/// A request in a DIDComm thread. +#[derive(Debug)] +pub(crate) struct ThreadRequest { + pub(crate) endpoint: Endpoint, + pub(crate) input: Vec, +} diff --git a/identity_agent/src/p2p/message.rs b/identity_agent/src/p2p/message.rs new file mode 100644 index 0000000000..2f4f776ca2 --- /dev/null +++ b/identity_agent/src/p2p/message.rs @@ -0,0 +1,44 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::fmt::Debug; + +use serde::Deserialize; +use serde::Serialize; + +use crate::agent::Endpoint; +use crate::agent::RequestMode; + +/// A request message containing some opaque data together with the endpoint it is inteded for and its request mode. +#[derive(Debug, Serialize, Deserialize)] +pub(crate) struct RequestMessage { + pub(crate) endpoint: Endpoint, + pub(crate) request_mode: RequestMode, + pub(crate) data: Vec, +} + +impl RequestMessage { + /// Creates a new request message from its parts. + pub(crate) fn new(endpoint: Endpoint, request_mode: RequestMode, data: Vec) -> Self { + Self { + endpoint, + request_mode, + data, + } + } + + /// Deserializes some JSON bytes into a request message. + pub(crate) fn from_bytes(bytes: &[u8]) -> std::io::Result { + serde_json::from_slice::<'_, Self>(bytes) + .map_err(|err| std::io::Error::new(std::io::ErrorKind::InvalidData, err.to_string())) + } + + /// Serializes the request message into JSON bytes. + pub(crate) fn to_bytes(&self) -> std::io::Result> { + serde_json::to_vec(self).map_err(|err| std::io::Error::new(std::io::ErrorKind::InvalidData, err.to_string())) + } +} + +/// A response message containing some opaque data. +#[derive(Debug)] +pub(crate) struct ResponseMessage(pub(crate) Vec); diff --git a/identity_agent/src/p2p/mod.rs b/identity_agent/src/p2p/mod.rs new file mode 100644 index 0000000000..3dc1e65abd --- /dev/null +++ b/identity_agent/src/p2p/mod.rs @@ -0,0 +1,12 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +mod behaviour; +mod event_loop; +mod message; +mod net_commander; + +pub(crate) use behaviour::*; +pub(crate) use event_loop::*; +pub(crate) use message::*; +pub(crate) use net_commander::*; diff --git a/identity_agent/src/p2p/net_commander.rs b/identity_agent/src/p2p/net_commander.rs new file mode 100644 index 0000000000..b516fb4962 --- /dev/null +++ b/identity_agent/src/p2p/net_commander.rs @@ -0,0 +1,154 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use futures::channel::mpsc; +use futures::channel::oneshot; +use futures::future::poll_fn; +use identity_core::common::OneOrMany; +use libp2p::request_response::InboundFailure; +use libp2p::request_response::OutboundFailure; +use libp2p::request_response::RequestId; +use libp2p::request_response::ResponseChannel; +use libp2p::Multiaddr; +use libp2p::PeerId; +use libp2p::TransportError; + +use crate::agent::Error; +use crate::agent::Result as AgentResult; +use crate::p2p::RequestMessage; +use crate::p2p::ResponseMessage; + +/// A thread-safe way to interact with an `EventLoop` running in the background. +#[derive(Debug, Clone)] +pub(crate) struct NetCommander { + command_sender: mpsc::Sender, +} + +impl NetCommander { + /// Create a new [`NetCommander`] from the sender half of a channel. + /// The receiver half needs to be passed to the `EventLoop`. + pub(crate) fn new(command_sender: mpsc::Sender) -> Self { + NetCommander { command_sender } + } + + /// Send the `request` to `peer_id` and returns the response. + pub(crate) async fn send_request( + &mut self, + peer_id: PeerId, + request: RequestMessage, + ) -> AgentResult { + let (sender, receiver) = oneshot::channel(); + let command = SwarmCommand::SendRequest { + peer_id, + request, + response_channel: sender, + }; + self.send_command(command).await?; + receiver + .await + .map_err(|_| Error::Shutdown)? + .map_err(Error::OutboundFailure) + } + + /// Send `data` as a response for the `request_id` using the provided `channel`. + /// The inner result signals whether sending the response was successful. + pub(crate) async fn send_response( + &mut self, + data: Vec, + channel: ResponseChannel, + request_id: RequestId, + ) -> AgentResult> { + let (sender, receiver) = oneshot::channel(); + let command = SwarmCommand::SendResponse { + response: data, + cmd_response_channel: sender, + response_channel: channel, + request_id, + }; + self.send_command(command).await?; + receiver.await.map_err(|_| Error::Shutdown) + } + + /// Start listening on the given address. + pub(crate) async fn start_listening(&mut self, address: Multiaddr) -> AgentResult { + let (sender, receiver) = oneshot::channel(); + let command = SwarmCommand::StartListening { + address, + response_channel: sender, + }; + self.send_command(command).await?; + receiver + .await + .map_err(|_| Error::Shutdown)? + .map_err(|transport_err| Error::TransportError("start listening", transport_err)) + } + + /// Add additional `addresses` to listen on. + pub(crate) async fn add_addresses(&mut self, peer_id: PeerId, addresses: OneOrMany) -> AgentResult<()> { + self + .send_command(SwarmCommand::AddAddresses { peer_id, addresses }) + .await + } + + /// Returns all addresses the event loop is listening on. + pub(crate) async fn get_addresses(&mut self) -> AgentResult> { + let (sender, receiver) = oneshot::channel(); + self + .send_command(SwarmCommand::GetAddresses { + response_channel: sender, + }) + .await?; + receiver.await.map_err(|_| Error::Shutdown) + } + + /// Shut down the event loop. This will return `Error::Shutdown` from all outstanding requests. + pub(crate) async fn shutdown(&mut self) -> AgentResult<()> { + let (sender, receiver) = oneshot::channel(); + self + .send_command(SwarmCommand::Shutdown { + response_channel: sender, + }) + .await?; + receiver.await.map_err(|_| Error::Shutdown) + } + + /// Send a command to the event loop. + async fn send_command(&mut self, command: SwarmCommand) -> AgentResult<()> { + poll_fn(|cx| self.command_sender.poll_ready(cx)) + .await + .map_err(|_| Error::Shutdown)?; + self.command_sender.start_send(command).map_err(|_| Error::Shutdown) + } +} + +/// A command to send to the `EventLoop` with (typically) a channel to return a response through. +/// +/// See the [`NetCommander`] methods for documentation. +#[derive(Debug)] +pub(crate) enum SwarmCommand { + SendRequest { + peer_id: PeerId, + request: RequestMessage, + response_channel: oneshot::Sender>, + }, + SendResponse { + response: Vec, + cmd_response_channel: oneshot::Sender>, + response_channel: ResponseChannel, + request_id: RequestId, + }, + StartListening { + address: Multiaddr, + response_channel: oneshot::Sender>>, + }, + AddAddresses { + peer_id: PeerId, + addresses: OneOrMany, + }, + GetAddresses { + response_channel: oneshot::Sender>, + }, + Shutdown { + response_channel: oneshot::Sender<()>, + }, +} diff --git a/identity_agent/src/tests/didcomm.rs b/identity_agent/src/tests/didcomm.rs new file mode 100644 index 0000000000..9561ef83e4 --- /dev/null +++ b/identity_agent/src/tests/didcomm.rs @@ -0,0 +1,281 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::sync::atomic::AtomicBool; +use std::sync::atomic::Ordering; +use std::sync::Arc; +use std::time::Duration; + +use crate::agent::AgentId; +use crate::agent::Endpoint; +use crate::agent::Error; +use crate::agent::Handler; +use crate::agent::HandlerRequest; +use crate::agent::RequestContext; +use crate::agent::Result as AgentResult; +use crate::didcomm::DidCommAgent; +use crate::didcomm::DidCommHandler; +use crate::didcomm::DidCommPlaintextMessage; +use crate::didcomm::DidCommRequest; +use crate::didcomm::ThreadId; +use crate::tests::default_listening_didcomm_agent; +use crate::tests::default_sending_didcomm_agent; +use crate::tests::presentation::presentation_holder_handler; +use crate::tests::presentation::presentation_verifier_handler; +use crate::tests::presentation::DidCommState; +use crate::tests::presentation::PresentationOffer; +use crate::tests::presentation::PresentationRequest; +use crate::tests::remote_account::IdentityList; +use crate::tests::try_init_logger; + +/// Ensure the DidCommAgent supports handlers working with `HandlerRequest`s (rather than `DidCommRequest`s). +#[tokio::test] +async fn test_didcomm_agent_supports_handler_requests() -> AgentResult<()> { + try_init_logger(); + + #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] + struct SyncDummy(u16); + + impl HandlerRequest for SyncDummy { + type Response = u16; + + fn endpoint() -> Endpoint { + "test/request".try_into().unwrap() + } + } + + #[derive(Debug)] + struct TestHandler; + + #[async_trait::async_trait] + impl Handler for TestHandler { + async fn handle(&self, request: RequestContext) -> u16 { + request.input.0 + } + } + + let (listening_handler, addrs, agent_id) = default_listening_didcomm_agent(|mut builder| { + builder.attach(TestHandler); + builder + }) + .await; + + let mut sending_agent = default_sending_didcomm_agent(|builder| builder).await; + sending_agent.add_agent_addresses(agent_id, addrs).await.unwrap(); + + let result = sending_agent.send_request(agent_id, SyncDummy(42)).await; + + assert_eq!(result.unwrap(), 42); + + listening_handler.shutdown().await.unwrap(); + sending_agent.shutdown().await.unwrap(); + + Ok(()) +} + +#[tokio::test] +async fn test_unknown_thread_returns_error() -> AgentResult<()> { + try_init_logger(); + + let (listening_handler, addrs, agent_id) = default_listening_didcomm_agent(|builder| builder).await; + + let mut sending_agent = default_sending_didcomm_agent(|builder| builder).await; + sending_agent.add_agent_addresses(agent_id, addrs).await.unwrap(); + + #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] + struct DidCommTestRequest(u16); + + impl DidCommRequest for DidCommTestRequest { + fn endpoint() -> Endpoint { + "unknown/thread".try_into().unwrap() + } + } + + // Send a message that no handling handler on the remote agent exists for + // which causes the remote agent to look for a potential thread that is waiting for this message, + // but no such thread exists either, so an error is returned. + let result = sending_agent + .send_didcomm_request(agent_id, &ThreadId::new(), DidCommTestRequest(42)) + .await; + + assert!(matches!(result.unwrap_err(), Error::UnexpectedRequest(_))); + + listening_handler.shutdown().await.unwrap(); + sending_agent.shutdown().await.unwrap(); + + Ok(()) +} + +#[tokio::test] +async fn test_didcomm_presentation_holder_initiates() -> AgentResult<()> { + try_init_logger(); + let handler: DidCommState = DidCommState::new(); + + let mut holder_agent: DidCommAgent = default_sending_didcomm_agent(|builder| builder).await; + + // Attach the DidCommState handler to the listening agent, so it can handle PresentationOffer requests. + let (verifier_agent, addrs, agent_id) = default_listening_didcomm_agent(|mut builder| { + builder.attach_didcomm::, _>(handler.clone()); + builder + }) + .await; + + holder_agent.add_agent_addresses(agent_id, addrs).await.unwrap(); + + // Holder initiates the presentation protocol. + presentation_holder_handler(holder_agent.clone(), agent_id, None) + .await + .unwrap(); + + // Allow background tasks to finish. + // The test also succeeds without this, but might cause the background tasks to panic or log an error. + tokio::task::yield_now().await; + + verifier_agent.shutdown().await.unwrap(); + holder_agent.shutdown().await.unwrap(); + + Ok(()) +} + +#[tokio::test] +async fn test_didcomm_presentation_verifier_initiates() -> AgentResult<()> { + try_init_logger(); + + let handler = DidCommState::new(); + + // Attach the DidCommState handler to the listening agent, so it can handle PresentationRequest requests. + let (holder_agent, addrs, agent_id) = default_listening_didcomm_agent(|mut builder| { + builder.attach_didcomm::, _>(handler.clone()); + builder + }) + .await; + let mut verifier_agent = default_sending_didcomm_agent(|builder| builder).await; + + verifier_agent.add_agent_addresses(agent_id, addrs).await.unwrap(); + + // Verifier initiates the presentation protocol. + presentation_verifier_handler(verifier_agent.clone(), agent_id, None) + .await + .unwrap(); + + holder_agent.shutdown().await.unwrap(); + verifier_agent.shutdown().await.unwrap(); + + Ok(()) +} + +#[tokio::test] +async fn test_sending_to_an_unconnected_agent_returns_error() -> AgentResult<()> { + try_init_logger(); + + let mut sending_agent = default_sending_didcomm_agent(|builder| builder).await; + + // Send a request without adding an address first. + let result = sending_agent.send_request(AgentId::random(), IdentityList).await; + + assert!(matches!(result.unwrap_err(), Error::OutboundFailure(_))); + + let result = sending_agent + .send_didcomm_request(AgentId::random(), &ThreadId::new(), PresentationOffer::default()) + .await; + + assert!(matches!(result.unwrap_err(), Error::OutboundFailure(_))); + + sending_agent.shutdown().await.unwrap(); + + Ok(()) +} + +#[tokio::test] +async fn test_await_didcomm_request_returns_timeout_error() -> AgentResult<()> { + try_init_logger(); + + #[derive(Debug, Clone)] + struct MyHandler; + + #[async_trait::async_trait] + impl DidCommHandler> for MyHandler { + async fn handle(&self, _: DidCommAgent, _: RequestContext>) {} + } + + let (listening_handler, addrs, agent_id) = default_listening_didcomm_agent(|mut builder| { + builder.attach_didcomm(MyHandler); + builder + }) + .await; + + let mut sending_agent: DidCommAgent = + default_sending_didcomm_agent(|builder| builder.timeout(Duration::from_millis(50))).await; + + sending_agent.add_agent_addresses(agent_id, addrs).await.unwrap(); + + let thread_id = ThreadId::new(); + sending_agent + .send_didcomm_request(agent_id, &thread_id, PresentationOffer::default()) + .await + .unwrap(); + + // We attempt to await a message, but the remote agent never sends one, so we expect a timeout. + let result = sending_agent.await_didcomm_request::<()>(&thread_id).await; + + assert!(matches!(result.unwrap_err(), Error::AwaitTimeout(_))); + + listening_handler.shutdown().await.unwrap(); + sending_agent.shutdown().await.unwrap(); + + Ok(()) +} + +#[tokio::test] +async fn test_handler_finishes_execution_after_shutdown() -> AgentResult<()> { + try_init_logger(); + + #[derive(Debug, Clone)] + struct TestHandler { + was_called: Arc, + } + + impl TestHandler { + fn new() -> Self { + Self { + was_called: Arc::new(AtomicBool::new(false)), + } + } + } + + #[async_trait::async_trait] + impl DidCommHandler> for TestHandler { + async fn handle(&self, _: DidCommAgent, _: RequestContext>) { + tokio::time::sleep(Duration::from_millis(25)).await; + self.was_called.store(true, Ordering::SeqCst); + } + } + + let test_handler = TestHandler::new(); + + let (listening_agent, addrs, agent_id) = default_listening_didcomm_agent(|mut builder| { + builder.attach_didcomm(test_handler.clone()); + builder + }) + .await; + + let mut sending_agent: DidCommAgent = default_sending_didcomm_agent(|builder| builder).await; + sending_agent.add_agent_addresses(agent_id, addrs).await.unwrap(); + + sending_agent + .send_didcomm_request(agent_id, &ThreadId::new(), PresentationOffer::default()) + .await + .unwrap(); + + // Shut down the agent that executes the handler, and wait for some time to allow the handler to finish. + // Even though we shut the agent down, we expect the task that the handler is running in to finish. + listening_agent.shutdown().await.unwrap(); + + tokio::time::sleep(Duration::from_millis(50)).await; + + sending_agent.shutdown().await.unwrap(); + + assert!(test_handler.was_called.load(Ordering::SeqCst)); + + Ok(()) +} diff --git a/identity_agent/src/tests/handler.rs b/identity_agent/src/tests/handler.rs new file mode 100644 index 0000000000..709d753bac --- /dev/null +++ b/identity_agent/src/tests/handler.rs @@ -0,0 +1,354 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::sync::atomic::AtomicBool; +use std::sync::atomic::AtomicU32; +use std::sync::atomic::Ordering; +use std::sync::Arc; +use std::task::Poll; + +use futures::pin_mut; +use identity_iota_core::did::IotaDID; +use libp2p::request_response::OutboundFailure; +use libp2p::Multiaddr; + +use crate::agent::Agent; +use crate::agent::AgentBuilder; +use crate::agent::Endpoint; +use crate::agent::Error; +use crate::agent::ErrorLocation; +use crate::agent::Handler; +use crate::agent::HandlerRequest; +use crate::agent::RequestContext; +use crate::agent::Result as AgentResult; +use crate::tests::default_listening_agent; +use crate::tests::default_sending_agent; +use crate::tests::remote_account::IdentityGet; +use crate::tests::remote_account::IdentityList; +use crate::tests::try_init_logger; + +#[tokio::test] +async fn test_handler_end_to_end() -> AgentResult<()> { + try_init_logger(); + + #[derive(Debug, Clone)] + struct MyHandler { + counter: Arc, + } + + // Define our request types and implement HandlerRequest for them. + #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] + struct Increment(u32); + + #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] + struct Decrement(u32); + + impl HandlerRequest for Increment { + type Response = u32; + + fn endpoint() -> Endpoint { + "counter/increment".try_into().unwrap() + } + } + + impl HandlerRequest for Decrement { + type Response = u32; + + fn endpoint() -> Endpoint { + "counter/decrement".try_into().unwrap() + } + } + + // States that MyHandler can handle messages of type `Increment`. + #[async_trait::async_trait] + impl Handler for MyHandler { + async fn handle(&self, request: RequestContext) -> u32 { + self.counter.fetch_add(request.input.0, Ordering::SeqCst); + self.counter.load(Ordering::SeqCst) + } + } + + // States that MyHandler can handle messages of type `Decrement`. + #[async_trait::async_trait] + impl Handler for MyHandler { + async fn handle(&self, request: RequestContext) -> u32 { + self.counter.fetch_sub(request.input.0, Ordering::SeqCst); + self.counter.load(Ordering::SeqCst) + } + } + + let handler = MyHandler { + counter: Arc::new(AtomicU32::new(0)), + }; + + // Create a new agent and attach the handler. + // Each attachment is for one request type, so we have to do it twice. + let mut builder = AgentBuilder::new(); + builder.attach::(handler.clone()); + builder.attach::(handler.clone()); + + // Build the listening agent and let it listen on a default address. + let mut listening_agent: Agent = builder.build().await.unwrap(); + + let _ = listening_agent + .start_listening("/ip4/0.0.0.0/tcp/0".parse().unwrap()) + .await + .unwrap(); + let addresses = listening_agent.addresses().await.unwrap(); + let agent_id = listening_agent.agent_id(); + + let mut sender_agent: Agent = AgentBuilder::new().build().await.unwrap(); + // Add on which which addresses sender_agent can reach agent_id. + sender_agent.add_agent_addresses(agent_id, addresses).await.unwrap(); + + assert_eq!(sender_agent.send_request(agent_id, Increment(3)).await.unwrap(), 3); + assert_eq!(sender_agent.send_request(agent_id, Decrement(2)).await.unwrap(), 1); + + listening_agent.shutdown().await.unwrap(); + sender_agent.shutdown().await.unwrap(); + + Ok(()) +} + +#[tokio::test] +async fn test_unknown_request_returns_error() -> AgentResult<()> { + try_init_logger(); + + let (listening_handler, addrs, agent_id) = default_listening_agent(|builder| builder).await; + + let mut sending_handler = default_sending_agent(|builder| builder).await; + sending_handler.add_agent_addresses(agent_id, addrs).await.unwrap(); + + let result = sending_handler + .send_request( + agent_id, + IdentityGet( + "did:iota:FFFAH6qct9KGQcSenG1iaw2Nj9jP7Zmug2zcmTpF4942" + .try_into() + .unwrap(), + ), + ) + .await; + + assert!(matches!(result.unwrap_err(), Error::UnexpectedRequest(_))); + + listening_handler.shutdown().await.unwrap(); + sending_handler.shutdown().await.unwrap(); + + Ok(()) +} + +/// Test that agent2 can send a request to agent1 if it was previously sent a request from agent1. +#[tokio::test] +async fn test_handlers_can_communicate_bidirectionally() -> AgentResult<()> { + try_init_logger(); + + #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] + struct Dummy(u8); + + impl HandlerRequest for Dummy { + type Response = (); + + fn endpoint() -> Endpoint { + "request/test".try_into().unwrap() + } + } + + #[derive(Debug, Clone)] + struct TestHandler(Arc); + + #[async_trait::async_trait] + impl Handler for TestHandler { + async fn handle(&self, _req: RequestContext) { + self.0.store(true, std::sync::atomic::Ordering::SeqCst); + } + } + + let handler1 = TestHandler(Arc::new(AtomicBool::new(false))); + let handler2 = TestHandler(Arc::new(AtomicBool::new(false))); + + let mut agent1_builder = AgentBuilder::new(); + agent1_builder.attach(handler1.clone()); + let mut agent1: Agent = agent1_builder.build().await.unwrap(); + + let mut agent2_builder = AgentBuilder::new(); + agent2_builder.attach(handler2.clone()); + let mut agent2: Agent = agent2_builder.build().await.unwrap(); + + agent2 + .start_listening("/ip4/0.0.0.0/tcp/0".try_into().unwrap()) + .await + .unwrap(); + + let addr: Multiaddr = agent2.addresses().await.unwrap().into_iter().next().unwrap(); + + agent1.add_agent_address(agent2.agent_id(), addr).await.unwrap(); + + agent1.send_request(agent2.agent_id(), Dummy(42)).await.unwrap(); + + agent2.send_request(agent1.agent_id(), Dummy(43)).await.unwrap(); + + agent1.shutdown().await.unwrap(); + agent2.shutdown().await.unwrap(); + + assert!(handler1.0.load(std::sync::atomic::Ordering::SeqCst)); + assert!(handler2.0.load(std::sync::atomic::Ordering::SeqCst)); + + Ok(()) +} + +#[tokio::test] +async fn test_interacting_with_shutdown_handler_returns_error() { + try_init_logger(); + + let (listening_handler, _, _) = default_listening_agent(|builder| builder).await; + + let mut handler_clone = listening_handler.clone(); + + listening_handler.shutdown().await.unwrap(); + + assert!(matches!(handler_clone.addresses().await.unwrap_err(), Error::Shutdown)); +} + +#[tokio::test] +async fn test_shutdown_returns_errors_through_open_channels() -> AgentResult<()> { + try_init_logger(); + + #[derive(Debug)] + struct TestHandler; + + #[async_trait::async_trait] + impl Handler for TestHandler { + async fn handle(&self, _: RequestContext) -> Vec { + tokio::time::sleep(std::time::Duration::from_millis(50)).await; + vec![] + } + } + + let (listening_agent, addrs, agent_id) = default_listening_agent(|mut builder| { + builder.attach(TestHandler); + builder + }) + .await; + + let mut sending_agent: Agent = AgentBuilder::new().build().await.unwrap(); + sending_agent.add_agent_addresses(agent_id, addrs).await.unwrap(); + + let mut sender1 = sending_agent.clone(); + + // Ensure that a handler shutdown returns errors through open channels, + // such as `EventLoop::await_response`. + // We do not test all `EventLoop::await*` fields, because some are + // much harder to test than others. + // We poll the futures once to ensure that the channels are created, + // before shutting the handler down. If we would call these methods after shutdown, + // they would immediately return a shutdown error (see test_interacting_with_shutdown_handler_returns_error), + // hence the need for manual polling. + // On the next poll after shutdown, we expect the errors. + + let send_request_future = sender1.send_request(agent_id, IdentityList); + pin_mut!(send_request_future); + let result = futures::poll!(&mut send_request_future); + assert!(matches!(result, Poll::Pending)); + + sending_agent.shutdown().await.unwrap(); + + let result = send_request_future.await; + assert!(matches!( + result.unwrap_err(), + Error::OutboundFailure(OutboundFailure::ConnectionClosed) + )); + + listening_agent.shutdown().await.unwrap(); + + Ok(()) +} + +#[tokio::test] +async fn test_endpoint_type_mismatch_results_in_serialization_errors() -> AgentResult<()> { + try_init_logger(); + + // Define two types with identical serialization results, but different `Response` types. + // Sending `CustomRequest2` to an endpoint expecting `CustomRequest`, we expect a local deserialization error. + + #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] + struct CustomRequest(u8); + + #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] + struct CustomRequest2(u8); + + impl HandlerRequest for CustomRequest { + type Response = String; + + fn endpoint() -> Endpoint { + "test/request".try_into().unwrap() + } + } + + impl HandlerRequest for CustomRequest2 { + type Response = u32; + + fn endpoint() -> Endpoint { + "test/request".try_into().unwrap() + } + } + + #[derive(Debug)] + struct TestHandler; + + #[async_trait::async_trait] + impl Handler for TestHandler { + async fn handle(&self, _: RequestContext) -> u32 { + 42 + } + } + + let (listening_handler, addrs, agent_id) = default_listening_agent(|mut builder| { + builder.attach(TestHandler); + builder + }) + .await; + + let mut sending_handler: Agent = AgentBuilder::new().build().await.unwrap(); + sending_handler.add_agent_addresses(agent_id, addrs).await.unwrap(); + + let result = sending_handler.send_request(agent_id, CustomRequest(13)).await; + + assert!(matches!( + result.unwrap_err(), + Error::DeserializationFailure { + location: ErrorLocation::Local, + .. + } + )); + + // Define a third type that has a different serialization result. + // We expect a deserialization error on the remote agent. + #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] + struct CustomRequest3(String); + + impl HandlerRequest for CustomRequest3 { + type Response = String; + + fn endpoint() -> Endpoint { + "test/request".try_into().unwrap() + } + } + + let result = sending_handler + .send_request(agent_id, CustomRequest3("13".to_owned())) + .await; + + assert!(matches!( + result.unwrap_err(), + Error::DeserializationFailure { + location: ErrorLocation::Remote, + .. + } + )); + + listening_handler.shutdown().await.unwrap(); + sending_handler.shutdown().await.unwrap(); + + Ok(()) +} diff --git a/identity_agent/src/tests/mod.rs b/identity_agent/src/tests/mod.rs new file mode 100644 index 0000000000..a2ba0de383 --- /dev/null +++ b/identity_agent/src/tests/mod.rs @@ -0,0 +1,85 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +mod didcomm; +mod handler; +mod presentation; +mod remote_account; + +use identity_core::crypto::KeyPair; +use identity_iota_core::document::IotaDocument; +use libp2p::identity::Keypair; +use libp2p::Multiaddr; + +use crate::agent::Agent; +use crate::agent::AgentBuilder; +use crate::agent::AgentId; +use crate::didcomm::DidCommAgent; +use crate::didcomm::DidCommAgentBuilder; +use crate::didcomm::DidCommAgentIdentity; + +fn try_init_logger() { + let _ = pretty_env_logger::try_init(); +} + +async fn default_listening_agent(f: impl FnOnce(AgentBuilder) -> AgentBuilder) -> (Agent, Vec, AgentId) { + let id_keys = Keypair::generate_ed25519(); + + let addr: Multiaddr = "/ip4/0.0.0.0/tcp/0".parse().unwrap(); + let mut builder = AgentBuilder::new().keypair(id_keys); + + builder = f(builder); + + let mut listening_agent: Agent = builder.build().await.unwrap(); + + let _ = listening_agent.start_listening(addr).await.unwrap(); + let addrs = listening_agent.addresses().await.unwrap(); + + let agent_id = listening_agent.agent_id(); + + (listening_agent, addrs, agent_id) +} + +async fn default_sending_agent(f: impl FnOnce(AgentBuilder) -> AgentBuilder) -> Agent { + let mut builder = AgentBuilder::new(); + + builder = f(builder); + + builder.build().await.unwrap() +} + +async fn default_sending_didcomm_agent(f: impl FnOnce(DidCommAgentBuilder) -> DidCommAgentBuilder) -> DidCommAgent { + let mut builder = DidCommAgentBuilder::new().identity(default_identity()); + + builder = f(builder); + + builder.build().await.unwrap() +} + +async fn default_listening_didcomm_agent( + f: impl FnOnce(DidCommAgentBuilder) -> DidCommAgentBuilder, +) -> (DidCommAgent, Vec, AgentId) { + let id_keys = Keypair::generate_ed25519(); + + let addr: Multiaddr = "/ip4/0.0.0.0/tcp/0".parse().unwrap(); + let mut builder = DidCommAgentBuilder::new().keypair(id_keys).identity(default_identity()); + + builder = f(builder); + + let mut listening_agent: DidCommAgent = builder.build().await.unwrap(); + + let _ = listening_agent.start_listening(addr).await.unwrap(); + let addrs = listening_agent.addresses().await.unwrap(); + + let agent_id = listening_agent.agent_id(); + + (listening_agent, addrs, agent_id) +} + +fn default_identity() -> DidCommAgentIdentity { + let keypair: KeyPair = KeyPair::new(identity_core::crypto::KeyType::Ed25519).unwrap(); + + DidCommAgentIdentity { + document: IotaDocument::new(&keypair).unwrap(), + } +} diff --git a/identity_agent/src/tests/presentation.rs b/identity_agent/src/tests/presentation.rs new file mode 100644 index 0000000000..5c20064cc4 --- /dev/null +++ b/identity_agent/src/tests/presentation.rs @@ -0,0 +1,161 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +//! A conceptual implementation of the IOTA DIDComm presentation protocol. +//! It merely sends the appropriate messages back and forth, but without any actual content. +//! It exists to prove the concept for the DIDComm agent. +//! +//! See for details: https://wiki.iota.org/identity.rs/specs/didcomm/protocols/presentation. + +use serde::Deserialize; +use serde::Serialize; + +use crate::agent::AgentId; +use crate::agent::Endpoint; +use crate::agent::RequestContext; +use crate::agent::Result as AgentResult; +use crate::didcomm::DidCommAgent; +use crate::didcomm::DidCommHandler; +use crate::didcomm::DidCommPlaintextMessage; +use crate::didcomm::DidCommRequest; +use crate::didcomm::ThreadId; + +#[derive(Debug, Clone)] +pub(crate) struct DidCommState; + +impl DidCommState { + pub(crate) fn new() -> Self { + Self + } +} + +#[async_trait::async_trait] +impl DidCommHandler> for DidCommState { + async fn handle(&self, agent: DidCommAgent, request: RequestContext>) { + log::debug!("holder: received presentation request"); + + let result = presentation_holder_handler(agent, request.agent_id, Some(request.input)).await; + + if let Err(err) = result { + log::error!("presentation holder handler errored: {err:?}"); + } + } +} + +#[async_trait::async_trait] +impl DidCommHandler> for DidCommState { + async fn handle(&self, agent: DidCommAgent, request: RequestContext>) { + log::debug!("verifier: received offer from {}", request.agent_id); + + let result = presentation_verifier_handler(agent, request.agent_id, Some(request.input)).await; + + if let Err(err) = result { + log::error!("presentation verifier handler errored: {err:?}"); + } + } +} + +/// The presentation protocol for the handler. +/// +/// If `request` is `None`, the holder initiates the protocol, otherwise the verifier initiated +/// by sending a `PresentationRequest`. +pub(crate) async fn presentation_holder_handler( + mut agent: DidCommAgent, + agent_id: AgentId, + request: Option>, +) -> AgentResult<()> { + let request: DidCommPlaintextMessage = match request { + Some(request) => request, + None => { + log::debug!("holder: sending presentation offer"); + let thread_id = ThreadId::new(); + agent + .send_didcomm_request(agent_id, &thread_id, PresentationOffer::default()) + .await?; + + let req = agent.await_didcomm_request(&thread_id).await; + log::debug!("holder: received presentation request"); + + req? + } + }; + + let thread_id = request.thread_id(); + + log::debug!("holder: sending presentation"); + agent + .send_didcomm_request(agent_id, thread_id, Presentation::default()) + .await?; + + let _result: DidCommPlaintextMessage = agent.await_didcomm_request(thread_id).await?; + log::debug!("holder: received presentation result"); + + Ok(()) +} + +/// The presentation protocol for the verifier. +/// +/// If `offer` is `None`, the verifier initiates the protocol, otherwise the holder initiated +/// by sending a `PresentationOffer`. +pub(crate) async fn presentation_verifier_handler( + mut agent: DidCommAgent, + agent_id: AgentId, + offer: Option>, +) -> AgentResult<()> { + let thread_id: ThreadId = if let Some(offer) = offer { + offer.thread_id().to_owned() + } else { + ThreadId::new() + }; + + log::debug!("verifier: sending request"); + agent + .send_didcomm_request(agent_id, &thread_id, PresentationRequest::default()) + .await?; + + log::debug!("verifier: awaiting presentation"); + let presentation: DidCommPlaintextMessage = agent.await_didcomm_request(&thread_id).await?; + log::debug!("verifier: received presentation: {:?}", presentation); + + log::debug!("verifier: sending presentation result"); + agent + .send_didcomm_request(agent_id, &thread_id, PresentationResult::default()) + .await?; + Ok(()) +} + +#[derive(Clone, Debug, Deserialize, Serialize, Default)] +pub(crate) struct PresentationRequest([u8; 2]); + +impl DidCommRequest for PresentationRequest { + fn endpoint() -> Endpoint { + "didcomm/presentation_request".try_into().unwrap() + } +} + +#[derive(Clone, Debug, Deserialize, Serialize, Default)] +pub(crate) struct PresentationOffer([u8; 3]); + +impl DidCommRequest for PresentationOffer { + fn endpoint() -> Endpoint { + "didcomm/presentation_offer".try_into().unwrap() + } +} + +#[derive(Clone, Debug, Deserialize, Serialize, Default)] +pub(crate) struct Presentation([u8; 4]); + +impl DidCommRequest for Presentation { + fn endpoint() -> Endpoint { + "didcomm/presentation".try_into().unwrap() + } +} + +#[derive(Clone, Debug, Deserialize, Serialize, Default)] +pub(crate) struct PresentationResult([u8; 5]); + +impl DidCommRequest for PresentationResult { + fn endpoint() -> Endpoint { + "didcomm/presentation_result".try_into().unwrap() + } +} diff --git a/identity_agent/src/tests/remote_account/error.rs b/identity_agent/src/tests/remote_account/error.rs new file mode 100644 index 0000000000..25cff072e0 --- /dev/null +++ b/identity_agent/src/tests/remote_account/error.rs @@ -0,0 +1,18 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +/// The error type for the [`RemoteAccount`]. +#[derive(Debug, thiserror::Error, serde::Serialize, serde::Deserialize)] +#[non_exhaustive] +pub(crate) enum RemoteAccountError { + #[error("identity not found")] + IdentityNotFound, + #[error("{0}")] + AccountError(String), +} + +impl From for RemoteAccountError { + fn from(err: identity_account::Error) -> Self { + Self::AccountError(err.to_string()) + } +} diff --git a/identity_agent/src/tests/remote_account/handler.rs b/identity_agent/src/tests/remote_account/handler.rs new file mode 100644 index 0000000000..d6e56b3032 --- /dev/null +++ b/identity_agent/src/tests/remote_account/handler.rs @@ -0,0 +1,65 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::sync::Arc; + +use dashmap::DashMap; +use identity_account::account::Account; +use identity_account::account::AccountBuilder; +use identity_iota_core::did::IotaDID; +use identity_iota_core::document::IotaDocument; +use tokio::sync::Mutex; + +use crate::agent::Handler; +use crate::agent::RequestContext; +use crate::tests::remote_account::IdentityCreate; +use crate::tests::remote_account::IdentityGet; +use crate::tests::remote_account::IdentityList; +use crate::tests::remote_account::RemoteAccountError; + +/// A proof-of-concept implementation of a remote `Account` with very basic operations +/// and disabled tangle interaction. +#[derive(Debug, Clone)] +pub(crate) struct RemoteAccount { + builder: Arc>, + accounts: Arc>, +} + +#[async_trait::async_trait] +impl Handler for RemoteAccount { + async fn handle(&self, _: RequestContext) -> Vec { + self.accounts.iter().map(|entry| entry.key().to_owned()).collect() + } +} + +#[async_trait::async_trait] +impl Handler for RemoteAccount { + async fn handle(&self, request: RequestContext) -> Result { + let account: Account = self.builder.lock().await.create_identity(request.input.into()).await?; + let doc = account.document().to_owned(); + self.accounts.insert(account.did().to_owned(), account); + Ok(doc) + } +} + +#[async_trait::async_trait] +impl Handler for RemoteAccount { + async fn handle(&self, request: RequestContext) -> Result { + self + .accounts + .get(&request.input.0) + .map(|account| account.document().to_owned()) + .ok_or(RemoteAccountError::IdentityNotFound) + } +} + +impl RemoteAccount { + pub(crate) fn new() -> identity_account::Result { + let builder: AccountBuilder = Account::builder().autopublish(false); + + Ok(Self { + builder: Arc::new(Mutex::new(builder)), + accounts: Arc::new(DashMap::new()), + }) + } +} diff --git a/identity_agent/src/tests/remote_account/mod.rs b/identity_agent/src/tests/remote_account/mod.rs new file mode 100644 index 0000000000..5346f5858b --- /dev/null +++ b/identity_agent/src/tests/remote_account/mod.rs @@ -0,0 +1,11 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +mod error; +mod handler; +mod requests; +mod tests; + +pub(crate) use error::*; +pub(crate) use handler::*; +pub(crate) use requests::*; diff --git a/identity_agent/src/tests/remote_account/requests.rs b/identity_agent/src/tests/remote_account/requests.rs new file mode 100644 index 0000000000..6ad3b22fb2 --- /dev/null +++ b/identity_agent/src/tests/remote_account/requests.rs @@ -0,0 +1,54 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use identity_account::types::IdentitySetup; +use identity_iota_core::did::IotaDID; +use identity_iota_core::document::IotaDocument; +use serde::Deserialize; +use serde::Serialize; + +use crate::agent::Endpoint; +use crate::agent::HandlerRequest; +use crate::tests::remote_account::RemoteAccountError; + +/// Can be sent to a `RemoteAccount` to instruct it to create an identity. +#[derive(Debug, Default, Clone, Serialize, Deserialize)] +pub(crate) struct IdentityCreate; + +impl From for IdentitySetup { + fn from(_: IdentityCreate) -> Self { + IdentitySetup::default() + } +} + +impl HandlerRequest for IdentityCreate { + type Response = Result; + + fn endpoint() -> Endpoint { + "remote_account/create".try_into().unwrap() + } +} + +/// Can be sent to a `RemoteAccount` to instruct it to return the identities it contains. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct IdentityList; + +impl HandlerRequest for IdentityList { + type Response = Vec; + + fn endpoint() -> Endpoint { + "remote_account/list".try_into().unwrap() + } +} + +/// Can be sent to a `RemoteAccount` to instruct it to return the given identities' DID document. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct IdentityGet(pub(crate) IotaDID); + +impl HandlerRequest for IdentityGet { + type Response = Result; + + fn endpoint() -> Endpoint { + "remote_account/get".try_into().unwrap() + } +} diff --git a/identity_agent/src/tests/remote_account/tests.rs b/identity_agent/src/tests/remote_account/tests.rs new file mode 100644 index 0000000000..cc0cd7c71e --- /dev/null +++ b/identity_agent/src/tests/remote_account/tests.rs @@ -0,0 +1,49 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use identity_iota_core::document::IotaDocument; + +use crate::agent::Result as AgentResult; +use crate::tests::default_listening_agent; +use crate::tests::default_sending_agent; +use crate::tests::remote_account::IdentityCreate; +use crate::tests::remote_account::IdentityGet; +use crate::tests::remote_account::IdentityList; +use crate::tests::remote_account::RemoteAccount; +use crate::tests::try_init_logger; + +#[tokio::test] +async fn test_remote_account() -> AgentResult<()> { + try_init_logger(); + + let (receiver, receiver_addrs, receiver_agent_id) = default_listening_agent(|mut builder| { + let remote_account = RemoteAccount::new().unwrap(); + builder.attach::(remote_account.clone()); + builder.attach::(remote_account.clone()); + builder.attach::(remote_account); + builder + }) + .await; + let mut sender = default_sending_agent(|builder| builder).await; + + sender + .add_agent_addresses(receiver_agent_id, receiver_addrs) + .await + .unwrap(); + + let doc: IotaDocument = sender.send_request(receiver_agent_id, IdentityCreate).await?.unwrap(); + + assert_eq!(sender.send_request(receiver_agent_id, IdentityList).await?.len(), 1); + + let doc2: IotaDocument = sender + .send_request(receiver_agent_id, IdentityGet(doc.id().clone())) + .await? + .unwrap(); + + assert_eq!(doc, doc2); + + sender.shutdown().await.unwrap(); + receiver.shutdown().await.unwrap(); + + Ok(()) +} diff --git a/identity_core/src/common/one_or_many.rs b/identity_core/src/common/one_or_many.rs index cf0c0b1671..e16ca88476 100644 --- a/identity_core/src/common/one_or_many.rs +++ b/identity_core/src/common/one_or_many.rs @@ -7,6 +7,7 @@ use core::hash::Hash; use core::mem::replace; use core::ops::Deref; use core::slice::from_ref; +use std::vec::IntoIter; use serde; use serde::Deserialize; @@ -183,6 +184,7 @@ impl FromIterator for OneOrMany { // Iterator // ============================================================================= +/// This struct is created by the `iter` method on [`OneOrMany`]. struct OneOrManyIter<'a, T> { inner: &'a OneOrMany, index: usize, @@ -203,6 +205,53 @@ impl<'a, T> Iterator for OneOrManyIter<'a, T> { } } +// ============================================================================= +// IntoIterator +// ============================================================================= + +enum Either { + Left(L), + Right(R), +} + +/// This struct is created by the `into_iter` method on [`OneOrMany`] +/// (provided by the [IntoIterator](https://doc.rust-lang.org/std/iter/trait.IntoIterator.html) trait). +pub struct OneOrManyIntoIterator { + iter: Either, IntoIter>, +} + +impl OneOrManyIntoIterator { + fn new(inner: OneOrMany) -> Self { + let iter = match inner { + OneOrMany::One(item) => Either::Left(Some(item)), + OneOrMany::Many(vec) => Either::Right(vec.into_iter()), + }; + + Self { iter } + } +} + +impl Iterator for OneOrManyIntoIterator { + type Item = T; + + fn next(&mut self) -> Option { + match self.iter { + Either::Left(ref mut item_opt) => item_opt.take(), + Either::Right(ref mut iter) => iter.next(), + } + } +} + +impl IntoIterator for OneOrMany { + type Item = T; + + type IntoIter = OneOrManyIntoIterator; + + fn into_iter(self) -> Self::IntoIter { + OneOrManyIntoIterator::new(self) + } +} + #[cfg(test)] mod tests { use super::*; @@ -258,4 +307,44 @@ mod tests { collection.push(42); assert_eq!(collection, OneOrMany::Many((0..=42).collect())); } + + #[test] + fn test_iter() { + let one_or_many = OneOrMany::Many(Vec::::new()); + assert!(one_or_many.iter().next().is_none()); + + let one_or_many = OneOrMany::One(1u32); + assert_eq!(one_or_many.iter().next().unwrap(), &1); + + let one_or_many = OneOrMany::Many(vec![42u32]); + let mut iter = one_or_many.iter(); + assert_eq!(iter.next().unwrap(), &42); + assert!(iter.next().is_none()); + + let one_or_many = OneOrMany::Many(vec![42u32, 1337u32]); + let mut iter = one_or_many.iter(); + assert_eq!(iter.next().unwrap(), &42); + assert_eq!(iter.next().unwrap(), &1337); + assert!(iter.next().is_none()); + } + + #[test] + fn test_into_iter() { + let one_or_many = OneOrMany::Many(Vec::::new()); + assert!(one_or_many.into_iter().next().is_none()); + + let one_or_many = OneOrMany::One(1u32); + assert_eq!(one_or_many.into_iter().next().unwrap(), 1); + + let one_or_many = OneOrMany::Many(vec![42u32]); + let mut into_iter = one_or_many.into_iter(); + assert_eq!(into_iter.next().unwrap(), 42); + assert!(into_iter.next().is_none()); + + let one_or_many = OneOrMany::Many(vec![42u32, 1337u32]); + let mut into_iter = one_or_many.into_iter(); + assert_eq!(into_iter.next().unwrap(), 42); + assert_eq!(into_iter.next().unwrap(), 1337); + assert!(into_iter.next().is_none()); + } } diff --git a/identity_iota/Cargo.toml b/identity_iota/Cargo.toml index 4c7e852a06..09943a90c6 100644 --- a/identity_iota/Cargo.toml +++ b/identity_iota/Cargo.toml @@ -16,6 +16,7 @@ description = "Framework for Self-Sovereign Identity with IOTA DID." # identity_comm = { version = "=0.5.0-dev.4", path = "../identity_comm", optional = true } identity_account = { version = "=0.6.0", path = "../identity_account", default-features = false, optional = true } identity_account_storage = { version = "=0.6.0", path = "../identity_account_storage", default-features = false, optional = true } +identity_agent = { version = "=0.6.0", path = "../identity_agent", default-features = false, optional = true } identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } identity_credential = { version = "=0.6.0", path = "../identity_credential", default-features = false } identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } @@ -59,6 +60,10 @@ revocation-bitmap = [ # Breaking changes to types and functions behind this flag are not covered by semver. unstable-encryption = ["identity_account/encryption"] +# Enables support for the unstable identity agent. +# Breaking changes to types and functions behind this flag are not covered by semver. +unstable-agent = ["dep:identity_agent"] + [package.metadata.docs.rs] # To build locally: # RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features --no-deps --workspace --open diff --git a/identity_iota/src/lib.rs b/identity_iota/src/lib.rs index 44ace7ad22..4aa06b5d58 100644 --- a/identity_iota/src/lib.rs +++ b/identity_iota/src/lib.rs @@ -139,3 +139,13 @@ pub mod prelude { pub use identity_iota_client::Result; pub use identity_iota_core::document::IotaDocument; } + +#[cfg(feature = "unstable-agent")] +pub mod agent { + //! Identity agent types + + pub use identity_agent::agent::*; + pub use identity_agent::didcomm::*; + pub use identity_agent::IdentityKeypair; + pub use identity_agent::Multiaddr; +} From f947e5e9b2325161577b58b4a616df5d48f9955d Mon Sep 17 00:00:00 2001 From: Philipp Date: Thu, 23 Jun 2022 17:29:31 +0300 Subject: [PATCH 07/89] Fix IOTA Discord link on GitHub (#919) * Fix IOTA Discord link * Use just the IOTA Discord URL * Fix typo in task template * Fix discord channel link in READMEs --- .github/ISSUE_TEMPLATE/config.yml | 4 ++-- .github/ISSUE_TEMPLATE/task.md | 4 ++-- README.md | 2 +- identity_iota/README.md | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 0363152f6a..ed23e17ecf 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1,5 @@ blank_issues_enabled: false contact_links: - - name: Official Discord + - name: Official IOTA Discord url: https://discord.iota.org/ - about: Contact the maintainers by joining the official Discord. Look for the channel #identity-discussion. + about: "Contact the maintainers by joining the #identity channel on the IOTA Discord." diff --git a/.github/ISSUE_TEMPLATE/task.md b/.github/ISSUE_TEMPLATE/task.md index 5a3735e8d4..f8af1ff261 100644 --- a/.github/ISSUE_TEMPLATE/task.md +++ b/.github/ISSUE_TEMPLATE/task.md @@ -14,10 +14,10 @@ Describe the task, this may be scoped beyond a single PR. Describe the motivation for the feature to be developed. ## Resources -Link to any resources relevant for the task such as Issues, PRs, reference implementations, or specifications. +Link to any resources relevant for the task such as issues, PRs, reference implementations, or specifications. ## To-do list -Create a task-specific to-do list . Please link PRs that match the To-do list item behind the item after it has been submitted. +Create a task-specific to-do list. Please link PRs that match the TODO list item behind the item after it has been submitted. - [ ] Item one - [ ] Item two diff --git a/README.md b/README.md index 6ec6af4a61..4fd9e6422d 100644 --- a/README.md +++ b/README.md @@ -194,4 +194,4 @@ Please review the [contribution](https://wiki.iota.org/identity.rs/contribute) a To contribute directly to the repository, simply fork the project, push your changes to your fork and create a pull request to get them included! -The best place to get involved in discussions about this framework or to look for support at is the `#identity-discussion` channel on the [IOTA Discord](https://discord.iota.org). You can also ask questions on our [Stack Exchange](https://iota.stackexchange.com/). +The best place to get involved in discussions about this framework or to look for support at is the `#identity` channel on the [IOTA Discord](https://discord.iota.org). You can also ask questions on our [Stack Exchange](https://iota.stackexchange.com/). diff --git a/identity_iota/README.md b/identity_iota/README.md index 6ec6af4a61..4fd9e6422d 100644 --- a/identity_iota/README.md +++ b/identity_iota/README.md @@ -194,4 +194,4 @@ Please review the [contribution](https://wiki.iota.org/identity.rs/contribute) a To contribute directly to the repository, simply fork the project, push your changes to your fork and create a pull request to get them included! -The best place to get involved in discussions about this framework or to look for support at is the `#identity-discussion` channel on the [IOTA Discord](https://discord.iota.org). You can also ask questions on our [Stack Exchange](https://iota.stackexchange.com/). +The best place to get involved in discussions about this framework or to look for support at is the `#identity` channel on the [IOTA Discord](https://discord.iota.org). You can also ask questions on our [Stack Exchange](https://iota.stackexchange.com/). From dd08f3c966460e2ae1bdbcf5a170af67c9ee41e5 Mon Sep 17 00:00:00 2001 From: Philipp Date: Thu, 23 Jun 2022 18:23:23 +0300 Subject: [PATCH 08/89] Enable `doc_cfg` on `docsrs` feature (#922) * Enable `doc_cfg` on `docsrs` feature * Show doc feature-gate on `unstable-agent` --- identity_iota/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/identity_iota/src/lib.rs b/identity_iota/src/lib.rs index 4aa06b5d58..7447e12e51 100644 --- a/identity_iota/src/lib.rs +++ b/identity_iota/src/lib.rs @@ -1,6 +1,7 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +#![cfg_attr(docsrs, feature(doc_cfg))] #![forbid(unsafe_code)] #![allow(deprecated)] #![doc = include_str!("./../README.md")] @@ -141,6 +142,7 @@ pub mod prelude { } #[cfg(feature = "unstable-agent")] +#[cfg_attr(docsrs, doc(cfg(feature = "unstable-agent")))] pub mod agent { //! Identity agent types From 7788b26b445cba15eaf984285f1d27589b06071f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Ha=C3=9F?= Date: Tue, 28 Jun 2022 13:18:51 +0200 Subject: [PATCH 09/89] Fix broken links (#924) (#926) --- .../docs/concepts/decentralized_identifiers/create.mdx | 2 +- .../docs/concepts/decentralized_identifiers/resolve.mdx | 2 +- documentation/docs/getting_started/create_and_publish.mdx | 4 ++-- documentation/docs/getting_started/overview.md | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/documentation/docs/concepts/decentralized_identifiers/create.mdx b/documentation/docs/concepts/decentralized_identifiers/create.mdx index abe57cd28a..ed39d59f83 100644 --- a/documentation/docs/concepts/decentralized_identifiers/create.mdx +++ b/documentation/docs/concepts/decentralized_identifiers/create.mdx @@ -38,4 +38,4 @@ Next, the identity is created and published to the IOTA Tangle. This operation w ### Identity Generation Process -The generation of an identity starts with a randomly generated asymmetric key pair. This can be generated by the IOTA Identity framework or can be provided as a parameter during the creation process. The public key is hashed using the `Blake2b-256` algorithm. This hash becomes the DID, creating a permanent and provable link between the initial keypair and the DID. The public key is then embedded into the initial DID Document and is used for verifying signatures created with the corresponding private key. This process can be observed and manipulated in depth by using the low-level API available for the IOTA Identity framework. These low-level APIs are available in [Rust](../libraries/rust/api_reference) and [WASM](../libraries/wasm/api_reference) but are only recommended for complex use cases that require maximum flexibility in the framework. +The generation of an identity starts with a randomly generated asymmetric key pair. This can be generated by the IOTA Identity framework or can be provided as a parameter during the creation process. The public key is hashed using the `Blake2b-256` algorithm. This hash becomes the DID, creating a permanent and provable link between the initial keypair and the DID. The public key is then embedded into the initial DID Document and is used for verifying signatures created with the corresponding private key. This process can be observed and manipulated in depth by using the low-level API available for the IOTA Identity framework. These low-level APIs are available in [Rust](../../libraries/rust/api_reference) and [WASM](../../libraries/wasm/api_reference) but are only recommended for complex use cases that require maximum flexibility in the framework. diff --git a/documentation/docs/concepts/decentralized_identifiers/resolve.mdx b/documentation/docs/concepts/decentralized_identifiers/resolve.mdx index 19eaf577b1..b636bbb5fb 100644 --- a/documentation/docs/concepts/decentralized_identifiers/resolve.mdx +++ b/documentation/docs/concepts/decentralized_identifiers/resolve.mdx @@ -104,7 +104,7 @@ async function buildAndResolve(client, did) { -In the example above the resolver will automatically try to resolve the DID from the network specified in the `did` (See [DID Format](./../specs/did/iota_did_method_spec#did-format)). +In the example above the resolver will automatically try to resolve the DID from the network specified in the `did` (See [DID Format](../../specs/did/iota_did_method_spec#did-format)). If the resolver was not built with a client configured for the given network name then an error will be thrown. Note that the `ResolverBuilder` can configure the `Resolver` to use multiple networks as long as they have distinct valid names (max six characters). diff --git a/documentation/docs/getting_started/create_and_publish.mdx b/documentation/docs/getting_started/create_and_publish.mdx index 240b47de53..4c4479dce5 100644 --- a/documentation/docs/getting_started/create_and_publish.mdx +++ b/documentation/docs/getting_started/create_and_publish.mdx @@ -13,7 +13,7 @@ keywords: import CodeSnippet from '../../src/components/CodeSnippetComponent' import createDidRustExample from '!!raw-loader!../../../examples/account/create_did.rs'; -If you want to benefit from Self-Sovereign Identity, you need to create a [Decentralized Identity](../decentralized_identifiers/overview). This identity consists of many parts that have different functions. This page will cover the basics about identity creation and publishing to the Tangle. +If you want to benefit from Self-Sovereign Identity, you need to create a [Decentralized Identity](../concepts/decentralized_identifiers/overview). This identity consists of many parts that have different functions. This page will cover the basics about identity creation and publishing to the Tangle. ## Identity Generation Process @@ -36,7 +36,7 @@ Select your programming language of choice and press the green play button to ex rustGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/examples/account/create_did.rs" /> -The first step in this example is the creation of an account. The account is a stateful object that manages one or more identities. The account provides an interface to execute high-level operations on identities, such as [creating](../decentralized_identifiers/create) and [updating](../decentralized_identifiers/update)) them. +The first step in this example is the creation of an account. The account is a stateful object that manages one or more identities. The account provides an interface to execute high-level operations on identities, such as [creating](../concepts/decentralized_identifiers/create) and [updating](../concepts/decentralized_identifiers/update)) them. Next, the identity is created and published to the IOTA Tangle. This operation will: diff --git a/documentation/docs/getting_started/overview.md b/documentation/docs/getting_started/overview.md index 8fdffcc50f..aaabf754b3 100644 --- a/documentation/docs/getting_started/overview.md +++ b/documentation/docs/getting_started/overview.md @@ -12,9 +12,9 @@ keywords: # Overview -Using the [standards proposed by W3C](https://www.w3.org/TR/did-core/), this section explains the IOTA Identity implementation. You can use this implementation to create a new digital identity for anyone or anything at any time. To do so, you must first generate a [Decentralized Identifier (DID)](../decentralized_identifiers/overview) that will serve as a reference to the [DID Document](../decentralized_identifiers/overview#did-documents). The DID Document contains public keys and other mechanisms to enable the subject to prove their association with the DID. +Using the [standards proposed by W3C](https://www.w3.org/TR/did-core/), this section explains the IOTA Identity implementation. You can use this implementation to create a new digital identity for anyone or anything at any time. To do so, you must first generate a [Decentralized Identifier (DID)](../concepts/decentralized_identifiers/overview) that will serve as a reference to the [DID Document](../concepts/decentralized_identifiers/overview#did-documents). The DID Document contains public keys and other mechanisms to enable the subject to prove their association with the DID. -However, you cannot tell much about the subject from a DID. You need to combine the DID with [Verifiable Credentials](../verifiable_credentials/overview). Verifiable Credentials are statements about the creator of the DID. They can be shared and verified online in a "Bring Your Own Identity" (BYOI) manner, and the DID creator remains in complete control of the process. +However, you cannot tell much about the subject from a DID. You need to combine the DID with [Verifiable Credentials](../concepts/verifiable_credentials/overview). Verifiable Credentials are statements about the creator of the DID. They can be shared and verified online in a "Bring Your Own Identity" (BYOI) manner, and the DID creator remains in complete control of the process. You can use this framework in processes such as: @@ -36,4 +36,4 @@ The IOTA Identity framework is developed in the Rust programming language. We al The following applications are currently utilizing the IOTA Identity framework: -- [Selv app](https://selv.iota.org/) \ No newline at end of file +- [Selv app](https://selv.iota.org/) From 04649c6bdbc14aca222824578bcfe43b90756a6f Mon Sep 17 00:00:00 2001 From: Henrique Nogara Date: Wed, 29 Jun 2022 17:05:01 -0300 Subject: [PATCH 10/89] Update stronghold to 0.6.4 (#928) --- identity_account_storage/Cargo.toml | 2 +- identity_account_storage/src/storage/stronghold.rs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/identity_account_storage/Cargo.toml b/identity_account_storage/Cargo.toml index 3aae2871e9..bb07c6d839 100644 --- a/identity_account_storage/Cargo.toml +++ b/identity_account_storage/Cargo.toml @@ -20,7 +20,7 @@ identity_core = { version = "=0.6.0", path = "../identity_core", default-feature identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } identity_iota_core = { version = "=0.6.0", path = "../identity_iota_core", default-features = false } iota-crypto = { version = ">=0.7, <0.10", default-features = false, features = ["hmac", "pbkdf", "sha", "std", "aes", "aes-kw"] } -iota_stronghold = { version = "0.5.1", default-features = false, features = ["std"], optional = true } +iota_stronghold = { version = "0.6.4", default-features = false, features = ["std"], optional = true } once_cell = { version = "1.7", default-features = false, features = ["std"], optional = true } parking_lot = { version = "0.12" } rand = { version = "0.8", default-features = false, features = ["std", "std_rng"], optional = true } diff --git a/identity_account_storage/src/storage/stronghold.rs b/identity_account_storage/src/storage/stronghold.rs index fd75806c07..715c11d4f1 100644 --- a/identity_account_storage/src/storage/stronghold.rs +++ b/identity_account_storage/src/storage/stronghold.rs @@ -399,6 +399,7 @@ impl Storage for Stronghold { store .insert(CHAIN_STATE_STORE_KEY.as_bytes().to_vec(), json, None) + .map(|_| ()) .map_err(|err| StrongholdError::Store(StoreOperation::Insert, err).into()) }) } @@ -425,6 +426,7 @@ impl Storage for Stronghold { store .insert(DOCUMENT_STORE_KEY.as_bytes().to_vec(), json, None) + .map(|_| ()) .map_err(|err| StrongholdError::Store(StoreOperation::Insert, err).into()) }) } From e507a4a7e1a42c44ce292fd12f4a12af8ca48b8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Ha=C3=9F?= Date: Thu, 30 Jun 2022 09:31:36 +0200 Subject: [PATCH 11/89] use latest version of tutorial (#927) --- documentation/docs/tutorials/overview.md | 2 +- .../tutorials/validate_university_degree.mdx | 292 +++++++----------- .../static/img/identity_tutorial_chart.png | Bin 61678 -> 0 bytes documentation/static/img/sequence-diagram.png | Bin 0 -> 242953 bytes 4 files changed, 111 insertions(+), 183 deletions(-) delete mode 100644 documentation/static/img/identity_tutorial_chart.png create mode 100644 documentation/static/img/sequence-diagram.png diff --git a/documentation/docs/tutorials/overview.md b/documentation/docs/tutorials/overview.md index d771ed1629..8c51ca0a83 100644 --- a/documentation/docs/tutorials/overview.md +++ b/documentation/docs/tutorials/overview.md @@ -10,4 +10,4 @@ keywords: In this section you will find end-to-end examples using the library to achieve common use-cases. List of tutorials: -- [validate_university_degree](./validate_university_degree): Use the Wasm bindings to digitally prove the existence and validity of a degree. \ No newline at end of file +- [Digitally Validate a Degree](./validate_university_degree): Use the Wasm bindings to digitally prove the existence and validity of a degree. \ No newline at end of file diff --git a/documentation/docs/tutorials/validate_university_degree.mdx b/documentation/docs/tutorials/validate_university_degree.mdx index db3ebbe4f9..621f8eb65e 100644 --- a/documentation/docs/tutorials/validate_university_degree.mdx +++ b/documentation/docs/tutorials/validate_university_degree.mdx @@ -1,6 +1,6 @@ --- -description: In this tutorial you will utilize the WASM binding of the IOTA Identity framework to digitally prove the existence and validity of a degree. -image: /img/identity_tutorial_chart.png +description: In this tutorial, you will use the WASM binding of the IOTA Identity framework to digitally prove the existence and validity of a university degree. +image: /img/sequence_diagram.png keywords: - wasm - decentralized identifiers @@ -12,19 +12,21 @@ keywords: - university --- -TODO: update this tutorial to remove references to `MerkleKeyCollection` and instead use `credentialStatus` with e.g. `RevocationList2020`. - # Digitally Validate a Degree -In this tutorial, you will use the [WASM binding of the IOTA Identity framework](../libraries/wasm/getting_started) to digitally prove the existence and validity of a degree. +In this tutorial, you will use the WASM binding of the IOTA Identity framework to digitally prove the existence and validity of a university degree. To follow along please clone [this repository](https://github.com/iotaledger/iota-identity-tutorial). + -To follow along, please clone [the tutorials repository](https://github.com/adrian-grassl/iota-identity-tutorial/) and install the npm/yarn package [@iota/identity-wasm**@dev**](https://www.npmjs.com/package/@iota/identity-wasm), as described in the [WASM binding documentation](../libraries/wasm/getting_started#install-the-library). +The `src/` directory contains scripts that can be run separately by providing command line arguments. Make sure that the npm dependencies - which include +the wasm bindings for the IOTA Identity Framework - are installed by running: -:::info Problem Description +```bash +npm install +``` +## Degree Validation Alice recently graduated from the University of Oslo with a Bachelor of Computer Science. Now, she wants to apply for a remote job at the IOTA Foundation and needs to digitally prove the existence and validity of her degree. What she needs is an immutable and verifiable credential, approved by both the University of Oslo and herself, before presenting it to her potential employer. -::: ## Roles @@ -46,252 +48,178 @@ As described in the [Digital Identities Solution](https://www.iota.org/solutions | Verifiable Presentation | A Verifiable Presentation is the format in which a (collection of) Verifiable Credential(s) gets shared. It is signed by the subject, to prove control over the Verifiable Credential with a nonce or timestamp. | | [DID Resolution](https://www.w3.org/TR/did-core/#dfn-did-resolution) | The process that takes as its input a DID and a set of resolution options and returns a DID document in a conforming representation plus additional metadata. | -## Flow-Chart - -[![banner](/img/identity_tutorial_chart.png)](/img/identity_tutorial_chart.png) - -## Key Storage - -- In this tutorial, you will store the key pairs for every newly created or updated DID document in Weakhold. -- Ok, ok, it’s just a couple of JSON files in a folder, but it gets the job done. -- The files are stored in the [Weakhold Folder](https://github.com/adrian-grassl/iota-identity-tutorial/tree/master/weakhold) (e.g. ./weakhold/Alice.json). - -:::warning This is no proper key storage solution. - -For professional IOTA implementations we strongly recommend using our key management framework [Stronghold](https://github.com/iotaledger/stronghold.rs). +## Sequence-Chart -::: +![banner](/img/sequence-diagram.png) -### Example Weakhold file: +## Storage -```json -{ - "subject": "Alice", - "did": "did:iota:Bakoe4HD4uwekMuyMkeo7mCsA2frXej68M4QyFvEpo2G", - "messageId": "7c25309fe97f2cf2d609cf83f31e8838795dd16d235c7a56566970309a0d6dbd", - "explorerUrl": "https://explorer.iota.org/mainnet/message/7c25309fe97f2cf2d609cf83f31e8838795dd16d235c7a56566970309a0d6dbd", - "authKey": { - "type": "ed25519", - "public": "ExwZKmF9y2N4mKnEaeUU7bFyCkZ5oVjjK3ojooJKNxUK", - "secret": "G83815cmpPadAzs52GmpwS614xpaAWWQxUexmRVNkg75" - }, - "verifKey": { - "type": "ed25519", - "public": "F9aM5Q9gGXb6Dswe8eSdsz5eDQX2ErTnpGDjFj5LMVvx", - "secret": "12S3U2u8ofyju53tmGsG9PKQfkBM8rhzL9BUBhfGqpdm" - } -} - -``` +In this tutorial, [Stronghold](https://github.com/iotaledger/stronghold.rs) will be used to securely store private keys. The Identity Framework already has [Stronghold bindings for Node.js](https://github.com/iotaledger/identity.rs/tree/dev/bindings/stronghold-nodejs). We will be using them in this tutorial. +For simplicity, each stronghold file will be responsible for storing only one DID. ## Steps In this process, you will complete the different steps from the perspective of one of the mentioned roles above: -### 1. **Holder**: Create a DID. +### 1. **Holder**: Create a DID -The first thing you will need to do in this tutorial is to create a DID(Decentralized Identifier) Document for Alice. +The first thing you will need to do in this tutorial is to create a DID (Decentralized Identifier) Document for Alice. +The script [createDid.ts](https://github.com/iotaledger/iota-identity-tutorial/tree/master/src/createDid.ts) can be used to create DIDs using the command: + +```bash +npm run start create-did +``` -After this step, you will find Alice's weakhold file in `./weakhold/Alice.json`. +For Alice, a DID can be created using: -- [createDid.js](https://github.com/adrian-grassl/iota-identity-tutorial/blob/master/createDid.js) +```bash +npm run start create-did alice alice-password -```javascript - createDid('Alice'); ``` -### 2. **Issuer**: Create a DID +This will create a minimal DID document for alice, and publish it to the Tangle. A Stronghold file `alice.hodl` will be created under `/stronghold-files` which contains the Account's state and the private key of the main verification method of the DID. +`alice-password` will be used as a password for the stronghold storage. Obviously this password must be more secure in production applications. -Once you have created the Alice's DID(Decentralized Identifier), you should do the same for the University of Oslo. +See [Creating a Decentralized Identity](https://wiki.iota.org/identity.rs/concepts/decentralized_identifiers/create) for more information about generating DIDs. -After this step, you will find the University of Oslo's weakhold file in `./weakhold/UniversityofOslo.json`. +### 2. **Issuer**: Create a DID -- [createDid.js](https://github.com/adrian-grassl/iota-identity-tutorial/blob/master/createDid.js) +Once you have created Alice's DID, you should do the same for the University of Oslo. -```javascript - createDid('University of Oslo'); +```bash +npm run start create-did uni-of-oslo uni-password ``` +with that `uni-of-oslo.hodl` will be created under `/stronhold-files`. + ### 3. **Issuer**: Add a Verification Method -Since the university will need to verify Alice's degree, you should add a "degreeVerifications" verification method to the University's DID document. +Since the university will need to issue a signed verifiable credential for Alice, a verification method should be added to the university's DID document. +Read more about adding verification methods in [update DID Documents](https://wiki.iota.org/identity.rs/concepts/decentralized_identifiers/update). -The University will have to sign more than just Alice's degree, so you should generate this verification method with a set of Merkle keys. These signatures can all be proved by a single public key, while retaining the ability to revoke them separately. +To add a Verification Method the following command can be used: -Note that the newly added verification method is of the *type* `MerkleKeyCollection`. +```bash +npm run start create-vm +``` -- [addVerificationMethod.js](https://github.com/adrian-grassl/iota-identity-tutorial/blob/master/addVerificationMethod.js) +This command will invoke [verificationMethods.ts](https://github.com/iotaledger/iota-identity-tutorial/tree/master/src/verificationMethods.ts). -```javascript - //Add verification method with collection of merkle keys to issuer DID - //This enables the issuer to sign and revoke multiple documents without having to remove the verification method for each revocation - let issuer = getWeakholdObject('./weakhold/UniversityofOslo.json') - let issuerVerificationMethod = "degreeVerifications"; +Note that `identity-name` is used to identify the Stronghold file location in `/stronghold-files` while `verification-fragment` is used to identify the Verification Method inside the DID Document. +To create a Verification Method for the issuer, use the following command: - addVerificationMethod( - subjectName = issuer.subject, - did = issuer.did, - authKey = KeyPair.fromJSON(issuer.authKey), - verificationMethodName = issuerVerificationMethod, - merkleKeys = true); +```bash +npm run start create-vm uni-of-oslo uni-password key-1 ``` ### 4. **Holder**: Add a Verification Method -Alice will need a verification method to present her degree to a third party, so you should add a verification method to her DID document. +Alice will need a verification method to sign verifiable presentations before sending them to third parties. Hence a verification method also needs to be added to her DID document. -Since Alice only needs one key pair to her credential's verifiable presentation, she will generate this verification method with a simple private/public key pair. +Similar to the issuer, the following command can be run to add a verification method to Alice's DID Document. -Note that the newly added verification method is of the *type* "Ed25519VerificationKey". +```bash +npm run start create-vm alice alice-password key-1 +``` -- [addVerificationMethod.js](https://github.com/adrian-grassl/iota-identity-tutorial/blob/master/addVerificationMethod.js) +### 5: **Issuer**: Create Revocation list -```javascript - //Add verification method to holder DID - let holder = getWeakholdObject('./weakhold/Alice.json') - let holderVerificationMethod = "aliceDegreePresentation"; +In order for the issuer to be able to revoke credentials in the future, a revocation list is needed. See [Verifiable Credential Revocation](https://wiki.iota.org/identity.rs/concepts/verifiable_credentials/revocation) for further details. +The following command can be used to create a revocation list: - addVerificationMethod( - subjectName = holder.subject, - did = holder.did, - authKey = KeyPair.fromJSON(holder.authKey), - verificationMethodName = holderVerificationMethod, - merkleKeys = false); +```bash +npm run start add-revocation-list ``` -###5. **Holder**: Set Up a Document - -You should set up a document representing Alice's degree, containing her DID which will later be signed by the **issuer**. +This will invoke [revocationBitmap.ts](https://github.com/iotaledger/iota-identity-tutorial/tree/master/src/revocationBitmap.ts). -- [createVerifiableCredential.js](https://github.com/adrian-grassl/iota-identity-tutorial/blob/master/createVerifiableCredential.js) +For the University of Oslo use: -```javascript - //This part is already hard coded in "createVerifiableCredential.js" - //Create credential indicating the degree earned by Alice - const credentialSubject = { - "id": holderDid, - "name": holderSubject, - "degreeName": "Bachelor of Computer Science", - "degreeType": "BachelorDegree", - "GPA": "4.0" - } +```bash +npm run start add-revocation-list uni-of-oslo uni-password rev-1 ``` -### 6. **Issuer**: Sign the Document - -To verify the degree document created in step 5, you should sign the degree document with the first key in the Merkle key collection of the University's verification method. - -This step will generate a verifiable credential. After this step you will find the verifiable credential for Alice's degree in [./signedCredentials/offlineVerifiableCredential.json](https://github.com/adrian-grassl/iota-identity-tutorial/tree/master/signedCredentials/). +Notice that `rev-1` is used to identity this revocation list inside the DID document. -- [createVerifiableCredential.js](https://github.com/adrian-grassl/iota-identity-tutorial/blob/master/createVerifiableCredential.js) +### 5 **Issuer**: Create Verifiable Credential -```javascript - //Issue and sign verifiable credential from weakhold object - let issuer = getWeakholdObject('./weakhold/UniversityofOslo.json') - let issuerVerificationMethod = "degreeVerifications"; - let holder = getWeakholdObject('./weakhold/Alice.json') +University of Oslo can now issue a verifiable credential to Alice. The following command can be used to create a verifiable credential: - createVerifiableCredential( - issuer.subject, - issuer.did, - KeyCollection.fromJSON(issuer.verifKey), - issuerVerificationMethod, - holder.did, - holder.subject); +```bash +npm run start create-vc ``` -### 7. **Holder**: Verify the Credentials +This will invoke [verifiableCredentials.ts](https://github.com/iotaledger/iota-identity-tutorial/tree/master/src/verifiableCredentials.ts). -Since Alice wants to be sure that her credentials are properly verified, you should verify the credentials to make sure it was actually signed by a key associated to the University DID. +To create a verifiable credential for Alice, run the following command: -- [checkVerifiableCredential.js](https://github.com/adrian-grassl/iota-identity-tutorial/blob/master/checkVerifiableCredential.js) - -```javascript - let signedVcPath = './signedCredentials/offlineVerifiableCredential.json'; - checkVerifiableCredential(signedVcPath); +```bash +npm run start create-vc uni-of-oslo uni-password alice key-1 rev-1 5 ``` -### 8. **Holder**: Sign a Verifiable Credential - -Alice need a verifiable presentation to send to the IOTA Foundation, so you should sign the verifiable credential with a private key of Alice's verification method. +Notice that `` needs to be replaced with Alice's DID. The reason we didn't use Alice's Stronghold file, is that the issuer doesn't have access to it in a real world scenario. +If you didn't note Alice's DID upon creating the DID, use `npm run start get-did alice alice-password` to log the DID saved in Alice's Stronghold file. -This step will generate a verifiable presentation. After this step you will find the verifiable presentation of Alice's degree in [./signedCredentials/offlineVerifiablePresentation.json](https://github.com/adrian-grassl/iota-identity-tutorial/tree/master/signedCredentials). +This verifiable credential is given a revocation index of `5`, this will be used later when the verifiable credential will be revoked. \ +The command will execute the script in [verifiableCredentials.ts](https://github.com/iotaledger/iota-identity-tutorial/tree/master/src/verifiableCredentials.ts) which creates a verifiable credential using values provided as arguments +and hard-coded values to describe the issued degree. This credential will be tied to `rev-1` revocation list and then signed with `key-1` verification method.\ +Once the script execution finishes, the file `alice-credential.json` will be created in the `credentials/` directory. The file contains the credential in JSON format +and is usually sent back to Alice to store and enable her to prove her degree. - - [createVerifiablePresentation.js](https://github.com/adrian-grassl/iota-identity-tutorial/blob/master/createVerifiablePresentation.js) +### 6 **Holder**: Create Verifiable Presentation -```javascript - //Issue and sign verifiable credential from weakhold object - let holder = getWeakholdObject('./weakhold/Alice.json') - let holderVerificationMethod = "aliceDegreePresentation"; - let signedVcPath = './signedCredentials/aliceVerifiableCredential.json'; +After Alice received the verifiable credential from the university, she applies for a job at the IOTA Foundation. The foundation requests a verifiable presentation +to be signed by alice that includes the challenge 'xyz123'. +The script [verifiablePresentation.ts](https://github.com/iotaledger/iota-identity-tutorial/tree/master/src/verifiablePresentation.ts) can be run with the command: - createVerifiablePresentation( - holder.subject, - holder.did, - KeyPair.fromJSON(holder.verifKey), - holderVerificationMethod, - signedVcPath); +```bash +npm run start create-vp ``` -### 9. **Verifier**: Verify Alice's and the University's Signatures - -The IOTA Foundation need to verify the presentation's signatures, so you should use Alice's and the University's public keys to verify their verifiable presentation. - - - [checkVerifiablePresentation.js](https://github.com/adrian-grassl/iota-identity-tutorial/blob/master/checkVerifiablePresentation.js) +For Alice's case: -```javascript - let signedVpPath = './signedCredentials/offlineVerifiablePresentation.json'; - checkVerifiablePresentation(signedVpPath); +```bash +npm run start create-vp alice alice-password alice-credential.json key-1 xyz123 ``` -### 10. **Issuer**: Revoke the Verification for Alice's Credential. +This will create a verifiable presentation of Alice's credential that includes the challenge and signed by Alice's `key-1` verification method. +The resulted presentation is saved in `presentations/alice-presentation.json`. -Unfortunately the University found out, that Alice had cheated on her final exam. Therefore, the University wants to revoke the verification of Alice's credential. +### 7 **Verifier**: Verification -Since they used a Merkle key collection as a verification method, you can do this two ways: +Now alice sends the signed verifiable presentation to the IOTA Foundation. The foundation now has to verify if everything is correct and the credential is valid. -#### 1. Remove the whole verification method +The script [checkVerifiablePresentation](https://github.com/iotaledger/iota-identity-tutorial/tree/master/src/verifiablePresentation.ts) can be run with the command: -- [removeVerificationMethod.js](https://github.com/adrian-grassl/iota-identity-tutorial/blob/master/removeVerificationMethod.js) - -```javascript -//Remove whole verification method and thus also the used key pair for signatures -let issuer = getWeakholdObject('./weakhold/UniversityofOslo.json'); -let verificationMethodName = "degreeVerifications"; - -removeVerificationMethod( - issuer.subject, - issuer.did, - KeyPair.fromJSON(issuer.authKey), - verificationMethodName ); +```bash +npm run start verify-vp ``` -#### 2. Only revoke the one Merkle key used for the signature. +So the foundation can run: -- [removeMerkleKey.js](https://github.com/adrian-grassl/iota-identity-tutorial/blob/master/removeMerkleKey.js) +```bash +npm run start verify-vp alice-presentation.json xyz123 -```javascript -//Revoke signatures, which used the first key in the Merkle key collection -let issuer = getWeakholdObject('./weakhold/UniversityofOslo.json'); -let verificationMethodName = "degreeVerifications"; - -removeMerkleKey( - issuer.subject, - issuer.did, - KeyPair.fromJSON(issuer.authKey), - verificationMethodName, - KeyCollection.fromJSON(issuer.verifKey)); ``` -Note that you could also revoke Alice's signature on the verifiable presentation, by removing her verification method. +Since everything was signed correctly, the verification should succeed. -### 11. **Verifier**: Verify Signatures Again +### 8 **Issuer**: Revocation -The IOTA Foundation verifies Alice's and the University's signatures again by checking the verifiable presentation and finds out that the University revoked their signature. +Unfortunately the university found out, that Alice had cheated on her final exam. Therefore, the university wants to revoke the validity of Alice's credential. +Since the revocation list `rev-1` with revocation index `5` were used upon creating the verifiable credential, revocation is now possible by updating the revocation list. -- [checkVerifiablePresentation.js](https://github.com/adrian-grassl/iota-identity-tutorial/blob/master/checkVerifiablePresentation.js) +[revocation.ts](https://github.com/iotaledger/iota-identity-tutorial/tree/master/src/revocation.ts) can be run with the command: -```javascript -let signedVpPath = './signedCredentials/signedVP.json'; -checkVerifiablePresentation(signedVpPath); +```bash +npm run start revoke-vc ``` + +To revoke Alice's Credential you can run: + +```bash +npm run start revoke-vc uni-of-oslo uni-password rev-1 5 +``` + +This will update the revocation list inside the issuer's DID Document and publish it to the tangle. Now if the IOTA Foundation tries to verify the credential again +e.g. by running `npm run start verify-vp alice-presentation.json xyz123`, This will throw an error since the verification now fails. diff --git a/documentation/static/img/identity_tutorial_chart.png b/documentation/static/img/identity_tutorial_chart.png deleted file mode 100644 index b46db4d8d5d73e576da1d924fefcda5cd7c41ab2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 61678 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 P|5cx9JS}-*9{hg*(d%;5 diff --git a/documentation/static/img/sequence-diagram.png b/documentation/static/img/sequence-diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..f7c822c9cb56073ecffdbdb906095aa394bd14fd GIT binary patch literal 242953 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

P?Pl*iW*h&*HqQ|=v~YjkuWbba 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 Date: Thu, 30 Jun 2022 09:45:31 +0200 Subject: [PATCH 12/89] use compressed sequence diagram (#929) --- documentation/static/img/sequence-diagram.png | Bin 242953 -> 77986 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/documentation/static/img/sequence-diagram.png b/documentation/static/img/sequence-diagram.png index f7c822c9cb56073ecffdbdb906095aa394bd14fd..048f942271d3e3c9f0738cc6951eea7355bd1305 100644 GIT binary patch literal 77986 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%0Dm<}c0*7dWF9n<%E${__>=a=mF2bQ?8#AcAg7 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 literal 242953 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

P?Pl*iW*h&*HqQ|=v~YjkuWbba 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 Date: Fri, 1 Jul 2022 14:36:44 +0300 Subject: [PATCH 13/89] Link Rust API reference in Wiki (#933) * Link Rust API reference * Rm unused lifetimes; rm let unit binding * Bump rust and `rustfmt` version in CI * Allow `drop_non_drop` clippy lint --- .github/workflows/coverage.yml | 2 +- .github/workflows/format.yml | 2 +- .github/workflows/scripts/coverage.sh | 2 +- bindings/wasm/src/lib.rs | 2 ++ .../concepts/advanced/storage_interface.mdx | 8 +------- .../docs/libraries/rust/api_reference.md | 20 ------------------- .../docs/libraries/rust/getting_started.md | 8 +++++--- documentation/sidebars.js | 1 - examples/low-level-api/key_exchange.rs | 2 +- identity_iota_core/src/did/iota_did.rs | 2 +- identity_iota_core/src/did/macros.rs | 5 ++++- .../src/document/iota_document.rs | 13 ++++++++---- 12 files changed, 26 insertions(+), 41 deletions(-) delete mode 100644 documentation/docs/libraries/rust/api_reference.md diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 6eebfaa3a1..fa0572ff32 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -26,7 +26,7 @@ jobs: - uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: nightly-2022-04-08 + toolchain: nightly-2022-06-30 override: true components: llvm-tools-preview diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index ca84cb7125..9e8894b595 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -27,7 +27,7 @@ jobs: uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: nightly-2022-03-11 + toolchain: nightly-2022-06-30 override: true components: rustfmt diff --git a/.github/workflows/scripts/coverage.sh b/.github/workflows/scripts/coverage.sh index 45f728125f..edba1030d7 100755 --- a/.github/workflows/scripts/coverage.sh +++ b/.github/workflows/scripts/coverage.sh @@ -5,7 +5,7 @@ set -e rm -rf coverage mkdir coverage -NIGHTLY="+nightly-2022-04-08" +NIGHTLY="+nightly-2022-06-30" # Run tests with profiling instrumentation echo "Running instrumented unit tests..." diff --git a/bindings/wasm/src/lib.rs b/bindings/wasm/src/lib.rs index 7f18069a82..38ac329906 100644 --- a/bindings/wasm/src/lib.rs +++ b/bindings/wasm/src/lib.rs @@ -4,6 +4,8 @@ #![forbid(unsafe_code)] #![allow(deprecated)] #![allow(clippy::upper_case_acronyms)] +// wasm_bindgen calls drop on non-Drop types. When/If this is fixed, this can be removed (no issue to link here yet). +#![allow(clippy::drop_non_drop)] #![allow(clippy::unused_unit)] #![allow(clippy::await_holding_refcell_ref)] // complains about RefCell in future_to_promise, should only panic in multithreaded code/web workers diff --git a/documentation/docs/concepts/advanced/storage_interface.mdx b/documentation/docs/concepts/advanced/storage_interface.mdx index 7ad6a5a559..26846128f7 100644 --- a/documentation/docs/concepts/advanced/storage_interface.mdx +++ b/documentation/docs/concepts/advanced/storage_interface.mdx @@ -109,13 +109,7 @@ Since this also needs to happen before the DID can be derived from the public ke ### Storage Test Suite -The `StorageTestSuite` can be used to test the basic functionality of storage implementations. See its documentation for more details. - -:::note - -This test suite is available in newer versions of the framework, but not yet in version 0.5. - -::: +The `StorageTestSuite` can be used to test the basic functionality of storage implementations. See its documentation ([Rust docs](https://docs.rs/identity_iota/latest/identity_iota/account_storage/struct.StorageTestSuite.html), [Wasm docs](../../libraries/wasm/api_reference#StorageTestSuite)) for more details. ### Examples diff --git a/documentation/docs/libraries/rust/api_reference.md b/documentation/docs/libraries/rust/api_reference.md deleted file mode 100644 index fdfcb4e8b5..0000000000 --- a/documentation/docs/libraries/rust/api_reference.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -title: Rust API Reference -sidebar_label: API Reference -description: Rust API reference. Learn how generate docs for the whole crate. -image: /img/Identity_icon.png -keywords: -- Rust -- API Reference ---- - -Link to hosted api documentation: - -> TODO! - - -If you'd like to explore the implementation in more depth, the following command generates docs for the whole crate: - -``` -cargo doc --document-private-items --no-deps --open -``` \ No newline at end of file diff --git a/documentation/docs/libraries/rust/getting_started.md b/documentation/docs/libraries/rust/getting_started.md index c73ceb972e..ecd62a51d5 100644 --- a/documentation/docs/libraries/rust/getting_started.md +++ b/documentation/docs/libraries/rust/getting_started.md @@ -58,8 +58,10 @@ cargo run --example getting_started ## API Reference -If you would like to build the [API Reference](api_reference) yourself from source, you can do so by running the following command: +The API reference for the Rust library can be found on [docs.rs](https://docs.rs/identity_iota/latest/identity_iota/index.html). -```rust -cargo doc --document-private-items --no-deps --open +If you would like to build the documentation locally you can do so with the following command: + +``` +RUSTDOCFLAGS='--cfg docsrs' cargo +nightly doc -p identity_iota --all-features --no-deps --open ``` diff --git a/documentation/sidebars.js b/documentation/sidebars.js index 416fd88e60..92f26a07ef 100644 --- a/documentation/sidebars.js +++ b/documentation/sidebars.js @@ -76,7 +76,6 @@ module.exports = { collapsed: true, items: [ 'libraries/rust/getting_started', - 'libraries/rust/api_reference', ], }, { diff --git a/examples/low-level-api/key_exchange.rs b/examples/low-level-api/key_exchange.rs index 024789fce4..bcd7598ea3 100644 --- a/examples/low-level-api/key_exchange.rs +++ b/examples/low-level-api/key_exchange.rs @@ -99,6 +99,6 @@ pub async fn run() -> Result<()> { #[tokio::main] async fn main() -> Result<()> { - let _ = run().await?; + run().await?; Ok(()) } diff --git a/identity_iota_core/src/did/iota_did.rs b/identity_iota_core/src/did/iota_did.rs index c69e1762a1..c8a456e0d6 100644 --- a/identity_iota_core/src/did/iota_did.rs +++ b/identity_iota_core/src/did/iota_did.rs @@ -194,7 +194,7 @@ impl IotaDID { if segments.count() == 2 && segments.network() == Self::DEFAULT_NETWORK { let method_id: String = segments.tag().to_string(); - let _ = did + did .set_method_id(method_id) .expect("this method_id is from a valid did"); } diff --git a/identity_iota_core/src/did/macros.rs b/identity_iota_core/src/did/macros.rs index afb9decf56..f2c5b4833e 100644 --- a/identity_iota_core/src/did/macros.rs +++ b/identity_iota_core/src/did/macros.rs @@ -14,7 +14,10 @@ /// # use identity_iota_core::try_construct_did; /// # /// let did = try_construct_did!(b"public-key")?; -/// assert_eq!(did.as_str(), "did:iota:2xQiiGHDq5gCi1H7utY1ni7cf65fTay3G11S4xKp1vkS"); +/// assert_eq!( +/// did.as_str(), +/// "did:iota:2xQiiGHDq5gCi1H7utY1ni7cf65fTay3G11S4xKp1vkS" +/// ); /// /// let did = try_construct_did!(b"public-key", "com")?; /// assert_eq!( diff --git a/identity_iota_core/src/document/iota_document.rs b/identity_iota_core/src/document/iota_document.rs index 3bff1f6722..875e98008d 100644 --- a/identity_iota_core/src/document/iota_document.rs +++ b/identity_iota_core/src/document/iota_document.rs @@ -123,10 +123,17 @@ impl IotaDocument { /// # /// // Create a new DID Document for the devnet from a new Ed25519 keypair. /// let keypair = KeyPair::new(KeyType::Ed25519).unwrap(); - /// let document = IotaDocument::new_with_options(&keypair, Some(Network::Devnet.name()), Some("auth-key")).unwrap(); + /// let document = + /// IotaDocument::new_with_options(&keypair, Some(Network::Devnet.name()), Some("auth-key")) + /// .unwrap(); /// assert_eq!(document.id().network_str(), "dev"); /// assert_eq!( - /// document.default_signing_method().unwrap().id().fragment().unwrap(), + /// document + /// .default_signing_method() + /// .unwrap() + /// .id() + /// .fragment() + /// .unwrap(), /// "auth-key" /// ); /// ``` @@ -669,8 +676,6 @@ mod iota_document_revocation { } } -impl<'a, 'b, 'c> IotaDocument {} - impl From<(IotaCoreDocument, IotaDocumentMetadata, Option)> for IotaDocument { fn from((document, metadata, proof): (IotaCoreDocument, IotaDocumentMetadata, Option)) -> Self { Self { From 22974da8e6eb8a5004b5c0aee704dcf4f98dd49a Mon Sep 17 00:00:00 2001 From: Philipp Date: Fri, 1 Jul 2022 16:42:53 +0300 Subject: [PATCH 14/89] Fix rust API ref links (#934) --- .../docs/concepts/decentralized_identifiers/create.mdx | 2 +- documentation/docs/getting_started/create_and_publish.mdx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/docs/concepts/decentralized_identifiers/create.mdx b/documentation/docs/concepts/decentralized_identifiers/create.mdx index ed39d59f83..1039d269aa 100644 --- a/documentation/docs/concepts/decentralized_identifiers/create.mdx +++ b/documentation/docs/concepts/decentralized_identifiers/create.mdx @@ -38,4 +38,4 @@ Next, the identity is created and published to the IOTA Tangle. This operation w ### Identity Generation Process -The generation of an identity starts with a randomly generated asymmetric key pair. This can be generated by the IOTA Identity framework or can be provided as a parameter during the creation process. The public key is hashed using the `Blake2b-256` algorithm. This hash becomes the DID, creating a permanent and provable link between the initial keypair and the DID. The public key is then embedded into the initial DID Document and is used for verifying signatures created with the corresponding private key. This process can be observed and manipulated in depth by using the low-level API available for the IOTA Identity framework. These low-level APIs are available in [Rust](../../libraries/rust/api_reference) and [WASM](../../libraries/wasm/api_reference) but are only recommended for complex use cases that require maximum flexibility in the framework. +The generation of an identity starts with a randomly generated asymmetric key pair. This can be generated by the IOTA Identity framework or can be provided as a parameter during the creation process. The public key is hashed using the `Blake2b-256` algorithm. This hash becomes the DID, creating a permanent and provable link between the initial keypair and the DID. The public key is then embedded into the initial DID Document and is used for verifying signatures created with the corresponding private key. This process can be observed and manipulated in depth by using the low-level API available for the IOTA Identity framework. These low-level APIs are available in [Rust](../../libraries/rust/getting_started#api-reference) and [WASM](../../libraries/wasm/api_reference) but are only recommended for complex use cases that require maximum flexibility in the framework. diff --git a/documentation/docs/getting_started/create_and_publish.mdx b/documentation/docs/getting_started/create_and_publish.mdx index 4c4479dce5..3c3ec1601b 100644 --- a/documentation/docs/getting_started/create_and_publish.mdx +++ b/documentation/docs/getting_started/create_and_publish.mdx @@ -21,7 +21,7 @@ The generation of an identity starts with a randomly generated [asymmetric key p ## Using the Account Module -The following example uses the high-level account module of the IOTA Identity framework to create an identity. You should use the account module for most of your use cases, but a lower-level API is also available should you need more flexibility at the cost of more complexity. For more information on APIs please visit the [Rust API Reference](../libraries/rust/api_reference) or the [WASM API Reference](../libraries/wasm/api_reference). +The following example uses the high-level account module of the IOTA Identity framework to create an identity. You should use the account module for most of your use cases, but a lower-level API is also available should you need more flexibility at the cost of more complexity. For more information on APIs please visit the [Rust API Reference](../libraries/rust/getting_started#api-reference) or the [WASM API Reference](../libraries/wasm/api_reference). :::tip Using Replit From d3920c2c90221a9a57246bf8e7fce7929d3e062c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Ha=C3=9F?= Date: Fri, 1 Jul 2022 15:47:40 +0200 Subject: [PATCH 15/89] remove documentation field for identity_iota (#932) --- identity_iota/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/identity_iota/Cargo.toml b/identity_iota/Cargo.toml index 09943a90c6..a86366d2a7 100644 --- a/identity_iota/Cargo.toml +++ b/identity_iota/Cargo.toml @@ -2,7 +2,6 @@ name = "identity_iota" version = "0.6.0" authors = ["IOTA Stiftung"] -documentation = "https://wiki.iota.org/identity.rs/introduction" edition = "2021" homepage = "https://www.iota.org" keywords = ["iota", "tangle", "identity", "did", "ssi"] From 88f8a5a7d6786b7d2d4c670ce7725b4f5564df84 Mon Sep 17 00:00:00 2001 From: Philipp Date: Tue, 5 Jul 2022 19:08:05 +0300 Subject: [PATCH 16/89] Fix brittle agent test (#936) * Fix spurious agent test * Change description to be in line with README --- identity_agent/Cargo.toml | 2 +- identity_agent/src/tests/handler.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/identity_agent/Cargo.toml b/identity_agent/Cargo.toml index 5729cca4f9..435b364295 100644 --- a/identity_agent/Cargo.toml +++ b/identity_agent/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["iota", "tangle", "identity", "p2p", "agent"] license = "Apache-2.0" readme = "./README.md" repository = "https://github.com/iotaledger/identity.rs" -description = "A peer-to-peer communication framework for building digital agents on IOTA Identity" +description = "A peer-to-peer communication framework for building SSI agents on IOTA Identity" [dependencies] async-trait = { version = "0.1", default-features = false } diff --git a/identity_agent/src/tests/handler.rs b/identity_agent/src/tests/handler.rs index 709d753bac..feb76e3a00 100644 --- a/identity_agent/src/tests/handler.rs +++ b/identity_agent/src/tests/handler.rs @@ -180,9 +180,9 @@ async fn test_handlers_can_communicate_bidirectionally() -> AgentResult<()> { .await .unwrap(); - let addr: Multiaddr = agent2.addresses().await.unwrap().into_iter().next().unwrap(); + let addrs: Vec = agent2.addresses().await.unwrap(); - agent1.add_agent_address(agent2.agent_id(), addr).await.unwrap(); + agent1.add_agent_addresses(agent2.agent_id(), addrs).await.unwrap(); agent1.send_request(agent2.agent_id(), Dummy(42)).await.unwrap(); From f0e4529c5c0770b629ce2bb18de2c74468064cae Mon Sep 17 00:00:00 2001 From: cycraig Date: Mon, 11 Jul 2022 09:13:07 +0200 Subject: [PATCH 17/89] Generalise `CredentialValidator`, `PresentationValidator` to support arbitrary DID Documents (#935) * Add dynamic dispatch traits * Implement Document for IotaDocument * Generalise CredentialValidator to use ValidatorDocument * Generalise PresentationValidator to use ValidatorDocument * Move validator module to identity_credential * Add CredentialValidator::extract_issuer, PresentationValidator::extract_holder * Fix tests, examples compilation * Fix identity_account_storage test compilation with no-default-features * Add unit test for presentation, credential validation with mixed DID Method issuers * Add extract_issuer, extract_holder to Wasm bindings * Update Wasm bindings * Remove old comments, unused imports * Switch parameters back to generics where possible * Change error type * Fix intra-doc links * Fix error documentation --- bindings/wasm/docs/api-reference.md | 135 ++++--- .../src/credential/credential_validator.rs | 33 +- .../src/credential/presentation_validator.rs | 30 +- .../wasm/src/credential/validation_options.rs | 10 +- bindings/wasm/src/did/mod.rs | 2 +- bindings/wasm/src/did/wasm_document.rs | 36 +- .../wasm/src/did/wasm_resolved_document.rs | 4 +- bindings/wasm/src/error.rs | 10 +- bindings/wasm/src/tangle/resolver.rs | 36 +- examples/account/create_vc.rs | 9 +- examples/account/create_vp.rs | 14 +- examples/account/revoke_vc.rs | 19 +- .../src/storage/memstore.rs | 1 + .../src/stronghold/tests.rs | 161 ++++---- identity_core/src/crypto/proof/jcs_ed25519.rs | 2 +- identity_core/src/crypto/signature/core.rs | 8 +- identity_credential/Cargo.toml | 10 +- identity_credential/README.md | 7 +- .../credential/revocation_bitmap_status.rs | 8 +- identity_credential/src/error.rs | 22 +- identity_credential/src/lib.rs | 9 +- .../src/validator}/credential_validator.rs | 245 +++++------- .../src/validator}/errors.rs | 39 +- .../src/validator}/mod.rs | 2 + .../src/validator}/presentation_validator.rs | 231 +++++------ .../src/validator}/test_utils.rs | 38 +- .../src/validator}/validation_options.rs | 5 +- .../src/validator/validator_document.rs | 111 +++++ identity_did/src/document/core_document.rs | 113 +++++- identity_did/src/document/mod.rs | 10 +- identity_did/src/document/traits.rs | 88 ++++ identity_did/src/revocation/bitmap.rs | 8 +- identity_iota/src/lib.rs | 2 +- identity_iota_client/Cargo.toml | 6 +- identity_iota_client/README.md | 5 - identity_iota_client/src/error.rs | 6 +- identity_iota_client/src/lib.rs | 2 - identity_iota_client/src/tangle/resolver.rs | 378 ++++++++++++++++-- .../src/document/iota_document.rs | 114 +++--- 39 files changed, 1331 insertions(+), 638 deletions(-) rename {identity_iota_client/src/credential => identity_credential/src/validator}/credential_validator.rs (84%) rename {identity_iota_client/src/credential => identity_credential/src/validator}/errors.rs (76%) rename {identity_iota_client/src/credential => identity_credential/src/validator}/mod.rs (91%) rename {identity_iota_client/src/credential => identity_credential/src/validator}/presentation_validator.rs (84%) rename {identity_iota_client/src/credential => identity_credential/src/validator}/test_utils.rs (63%) rename {identity_iota_client/src/credential => identity_credential/src/validator}/validation_options.rs (96%) create mode 100644 identity_credential/src/validator/validator_document.rs create mode 100644 identity_did/src/document/traits.rs diff --git a/bindings/wasm/docs/api-reference.md b/bindings/wasm/docs/api-reference.md index 81d4124b69..cd49b97317 100644 --- a/bindings/wasm/docs/api-reference.md +++ b/bindings/wasm/docs/api-reference.md @@ -222,9 +222,9 @@ publishing to the Tangle. **Kind**: global class * [Account](#Account) - * [.attachMethodRelationships(options)](#Account+attachMethodRelationships) ⇒ Promise.<void> + * [.createService(options)](#Account+createService) ⇒ Promise.<void> * [.createMethod(options)](#Account+createMethod) ⇒ Promise.<void> - * [.detachMethodRelationships(options)](#Account+detachMethodRelationships) ⇒ Promise.<void> + * [.attachMethodRelationships(options)](#Account+attachMethodRelationships) ⇒ Promise.<void> * [.did()](#Account+did) ⇒ [DID](#DID) * [.autopublish()](#Account+autopublish) ⇒ boolean * [.autosave()](#Account+autosave) ⇒ [AutoSave](#AutoSave) @@ -242,25 +242,22 @@ publishing to the Tangle. * [.unrevokeCredentials(fragment, credentialIndices)](#Account+unrevokeCredentials) ⇒ Promise.<void> * [.encryptData(plaintext, associated_data, encryption_algorithm, cek_algorithm, public_key)](#Account+encryptData) ⇒ [Promise.<EncryptedData>](#EncryptedData) * [.decryptData(data, encryption_algorithm, cek_algorithm, fragment)](#Account+decryptData) ⇒ Promise.<Uint8Array> - * [.setAlsoKnownAs(options)](#Account+setAlsoKnownAs) ⇒ Promise.<void> - * [.deleteMethod(options)](#Account+deleteMethod) ⇒ Promise.<void> + * [.detachMethodRelationships(options)](#Account+detachMethodRelationships) ⇒ Promise.<void> * [.deleteService(options)](#Account+deleteService) ⇒ Promise.<void> + * [.setAlsoKnownAs(options)](#Account+setAlsoKnownAs) ⇒ Promise.<void> * [.setController(options)](#Account+setController) ⇒ Promise.<void> - * [.createService(options)](#Account+createService) ⇒ Promise.<void> - - + * [.deleteMethod(options)](#Account+deleteMethod) ⇒ Promise.<void> -### account.attachMethodRelationships(options) ⇒ Promise.<void> -Attach one or more verification relationships to a method. + -Note: the method must exist and be in the set of verification methods; -it cannot be an embedded method. +### account.createService(options) ⇒ Promise.<void> +Adds a new Service to the DID Document. **Kind**: instance method of [Account](#Account) | Param | Type | | --- | --- | -| options | AttachMethodRelationshipOptions | +| options | CreateServiceOptions | @@ -273,16 +270,19 @@ Adds a new verification method to the DID document. | --- | --- | | options | CreateMethodOptions | - + -### account.detachMethodRelationships(options) ⇒ Promise.<void> -Detaches the given relationship from the given method, if the method exists. +### account.attachMethodRelationships(options) ⇒ Promise.<void> +Attach one or more verification relationships to a method. + +Note: the method must exist and be in the set of verification methods; +it cannot be an embedded method. **Kind**: instance method of [Account](#Account) | Param | Type | | --- | --- | -| options | DetachMethodRelationshipOptions | +| options | AttachMethodRelationshipOptions | @@ -475,38 +475,38 @@ Returns the decrypted text. | cek_algorithm | [CekAlgorithm](#CekAlgorithm) | | fragment | string | - + -### account.setAlsoKnownAs(options) ⇒ Promise.<void> -Sets the `alsoKnownAs` property in the DID document. +### account.detachMethodRelationships(options) ⇒ Promise.<void> +Detaches the given relationship from the given method, if the method exists. **Kind**: instance method of [Account](#Account) | Param | Type | | --- | --- | -| options | SetAlsoKnownAsOptions | +| options | DetachMethodRelationshipOptions | - + -### account.deleteMethod(options) ⇒ Promise.<void> -Deletes a verification method if the method exists. +### account.deleteService(options) ⇒ Promise.<void> +Deletes a Service if it exists. **Kind**: instance method of [Account](#Account) | Param | Type | | --- | --- | -| options | DeleteMethodOptions | +| options | DeleteServiceOptions | - + -### account.deleteService(options) ⇒ Promise.<void> -Deletes a Service if it exists. +### account.setAlsoKnownAs(options) ⇒ Promise.<void> +Sets the `alsoKnownAs` property in the DID document. **Kind**: instance method of [Account](#Account) | Param | Type | | --- | --- | -| options | DeleteServiceOptions | +| options | SetAlsoKnownAsOptions | @@ -519,16 +519,16 @@ Sets the controllers of the DID document. | --- | --- | | options | SetControllerOptions | - + -### account.createService(options) ⇒ Promise.<void> -Adds a new Service to the DID Document. +### account.deleteMethod(options) ⇒ Promise.<void> +Deletes a verification method if the method exists. **Kind**: instance method of [Account](#Account) | Param | Type | | --- | --- | -| options | CreateServiceOptions | +| options | DeleteMethodOptions | @@ -1176,6 +1176,7 @@ Deserializes a `CredentialValidationOptions` from a JSON object. * [.verifySignature(credential, trusted_issuers, options)](#CredentialValidator.verifySignature) * [.check_subject_holder_relationship(credential, holder_url, relationship)](#CredentialValidator.check_subject_holder_relationship) * [.checkStatus(credential, trustedIssuers, statusCheck)](#CredentialValidator.checkStatus) + * [.extractIssuer(credential)](#CredentialValidator.extractIssuer) ⇒ [DID](#DID) @@ -1269,7 +1270,7 @@ to verify the credential's signature will be made and an error is returned upon | Param | Type | | --- | --- | | credential | [Credential](#Credential) | -| trusted_issuers | [Array.<Document>](#Document) \| [Array.<ResolvedDocument>](#ResolvedDocument) | +| trusted_issuers | Array.<(Document\|ResolvedDocument)> | | options | [VerifierOptions](#VerifierOptions) | @@ -1298,9 +1299,24 @@ Only supports `BitmapRevocation2022`. | Param | Type | | --- | --- | | credential | [Credential](#Credential) | -| trustedIssuers | [Array.<Document>](#Document) \| [Array.<ResolvedDocument>](#ResolvedDocument) | +| trustedIssuers | Array.<(Document\|ResolvedDocument)> | | statusCheck | number | + + +### CredentialValidator.extractIssuer(credential) ⇒ [DID](#DID) +Utility for extracting the issuer field of a `Credential` as a DID. + +### Errors + +Fails if the issuer field is not a valid DID. + +**Kind**: static method of [CredentialValidator](#CredentialValidator) + +| Param | Type | +| --- | --- | +| credential | [Credential](#Credential) | + ## DID @@ -1755,6 +1771,7 @@ Deserializes a `DiffMessage` from a JSON object. * [.service()](#Document+service) ⇒ [Array.<Service>](#Service) * [.insertService(service)](#Document+insertService) ⇒ boolean * [.removeService(did)](#Document+removeService) ⇒ boolean + * [.resolveService(query)](#Document+resolveService) ⇒ [Service](#Service) \| undefined * [.methods()](#Document+methods) ⇒ [Array.<VerificationMethod>](#VerificationMethod) * [.insertMethod(method, scope)](#Document+insertMethod) * [.removeMethod(did)](#Document+removeMethod) @@ -1782,8 +1799,8 @@ Deserializes a `DiffMessage` from a JSON object. * [.metadataPreviousMessageId()](#Document+metadataPreviousMessageId) ⇒ string * [.setMetadataPreviousMessageId(value)](#Document+setMetadataPreviousMessageId) * [.proof()](#Document+proof) ⇒ [Proof](#Proof) \| undefined - * [.revokeCredentials(fragment, credentialIndices)](#Document+revokeCredentials) - * [.unrevokeCredentials(fragment, credentialIndices)](#Document+unrevokeCredentials) + * [.revokeCredentials(serviceQuery, credentialIndices)](#Document+revokeCredentials) + * [.unrevokeCredentials(serviceQuery, credentialIndices)](#Document+unrevokeCredentials) * [.toJSON()](#Document+toJSON) ⇒ any * [.clone()](#Document+clone) ⇒ [Document](#Document) * _static_ @@ -1916,6 +1933,18 @@ Returns `true` if a service was removed. | --- | --- | | did | [DIDUrl](#DIDUrl) | + + +### document.resolveService(query) ⇒ [Service](#Service) \| undefined +Returns the first [Service](#Service) with an `id` property matching the provided `query`, +if present. + +**Kind**: instance method of [Document](#Document) + +| Param | Type | +| --- | --- | +| query | [DIDUrl](#DIDUrl) \| string | + ### document.methods() ⇒ [Array.<VerificationMethod>](#VerificationMethod) @@ -2252,28 +2281,28 @@ Returns a copy of the proof. **Kind**: instance method of [Document](#Document) -### document.revokeCredentials(fragment, credentialIndices) -If the document has a `RevocationBitmap` service identified by `fragment`, +### document.revokeCredentials(serviceQuery, credentialIndices) +If the document has a `RevocationBitmap` service identified by `serviceQuery`, revoke all credentials with a revocationBitmapIndex in `credentialIndices`. **Kind**: instance method of [Document](#Document) | Param | Type | | --- | --- | -| fragment | string | +| serviceQuery | [DIDUrl](#DIDUrl) \| string | | credentialIndices | number \| Array.<number> | -### document.unrevokeCredentials(fragment, credentialIndices) -If the document has a `RevocationBitmap` service identified by `fragment`, +### document.unrevokeCredentials(serviceQuery, credentialIndices) +If the document has a `RevocationBitmap` service identified by `serviceQuery`, unrevoke all credentials with a revocationBitmapIndex in `credentialIndices`. **Kind**: instance method of [Document](#Document) | Param | Type | | --- | --- | -| fragment | string | +| serviceQuery | [DIDUrl](#DIDUrl) \| string | | credentialIndices | number \| Array.<number> | @@ -3591,6 +3620,7 @@ Deserializes a `PresentationValidationOptions` from a JSON object. * [.validate(presentation, holder, issuers, options, fail_fast)](#PresentationValidator.validate) * [.verifyPresentationSignature(presentation, holder, options)](#PresentationValidator.verifyPresentationSignature) * [.checkStructure(presentation)](#PresentationValidator.checkStructure) + * [.extractHolder(presentation)](#PresentationValidator.extractHolder) ⇒ [DID](#DID) @@ -3628,7 +3658,7 @@ An error is returned whenever a validated condition is not satisfied. | --- | --- | | presentation | [Presentation](#Presentation) | | holder | [Document](#Document) \| [ResolvedDocument](#ResolvedDocument) | -| issuers | [Array.<Document>](#Document) \| [Array.<ResolvedDocument>](#ResolvedDocument) | +| issuers | Array.<(Document\|ResolvedDocument)> | | options | [PresentationValidationOptions](#PresentationValidationOptions) | | fail_fast | number | @@ -3663,6 +3693,21 @@ Validates the semantic structure of the `Presentation`. | --- | --- | | presentation | [Presentation](#Presentation) | + + +### PresentationValidator.extractHolder(presentation) ⇒ [DID](#DID) +Utility for extracting the holder field of a `Presentation` as a DID. + +### Errors + +Fails if the holder field is missing or not a valid DID. + +**Kind**: static method of [PresentationValidator](#PresentationValidator) + +| Param | Type | +| --- | --- | +| presentation | [Presentation](#Presentation) | + ## Proof @@ -4185,8 +4230,8 @@ according to the `fail_fast` parameter. | presentation | [Presentation](#Presentation) | | options | [PresentationValidationOptions](#PresentationValidationOptions) | | fail_fast | number | -| holder | [ResolvedDocument](#ResolvedDocument) \| undefined | -| issuers | [Array.<ResolvedDocument>](#ResolvedDocument) \| undefined | +| holder | [Document](#Document) \| [ResolvedDocument](#ResolvedDocument) \| undefined | +| issuers | Array.<(Document\|ResolvedDocument)> \| undefined | diff --git a/bindings/wasm/src/credential/credential_validator.rs b/bindings/wasm/src/credential/credential_validator.rs index 8b26671556..2995e6539a 100644 --- a/bindings/wasm/src/credential/credential_validator.rs +++ b/bindings/wasm/src/credential/credential_validator.rs @@ -1,19 +1,21 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use identity_iota::client::CredentialValidator; use identity_iota::client::ResolvedIotaDocument; -use identity_iota::client::StatusCheck; -use identity_iota::client::ValidationError; use identity_iota::core::Url; +use identity_iota::credential::CredentialValidator; +use identity_iota::credential::StatusCheck; +use identity_iota::credential::ValidationError; +use identity_iota::iota_core::IotaDID; use identity_iota::iota_core::IotaDocument; use wasm_bindgen::prelude::*; use crate::common::WasmTimestamp; use crate::credential::validation_options::WasmFailFast; use crate::credential::validation_options::WasmStatusCheck; -use crate::did::ArrayDocumentOrArrayResolvedDocument; +use crate::did::ArrayDocumentOrResolvedDocument; use crate::did::DocumentOrResolvedDocument; +use crate::did::WasmDID; use crate::did::WasmVerifierOptions; use crate::error::Result; use crate::error::WasmResult; @@ -59,7 +61,7 @@ impl WasmCredentialValidator { fail_fast: WasmFailFast, ) -> Result<()> { let issuer_doc: ResolvedIotaDocument = issuer.into_serde().wasm_result()?; - CredentialValidator::validate(&credential.0, &issuer_doc, &options.0, fail_fast.into()).wasm_result() + CredentialValidator::validate(&credential.0, &issuer_doc.document, &options.0, fail_fast.into()).wasm_result() } /// Validates the semantic structure of the `Credential`. @@ -98,11 +100,11 @@ impl WasmCredentialValidator { #[wasm_bindgen(js_name = verifySignature)] pub fn verify_signature( credential: &WasmCredential, - trusted_issuers: &ArrayDocumentOrArrayResolvedDocument, + trusted_issuers: &ArrayDocumentOrResolvedDocument, options: &WasmVerifierOptions, ) -> Result<()> { - let trusted_issuers: Vec = trusted_issuers.into_serde().wasm_result()?; - CredentialValidator::verify_signature(&credential.0, trusted_issuers.as_slice(), &options.0).wasm_result() + let issuers: Vec = trusted_issuers.into_serde().wasm_result()?; + CredentialValidator::verify_signature(&credential.0, &issuers, &options.0).wasm_result() } /// Validate that the relationship between the `holder` and the credential subjects is in accordance with @@ -123,11 +125,22 @@ impl WasmCredentialValidator { #[allow(non_snake_case)] pub fn check_status( credential: &WasmCredential, - trustedIssuers: &ArrayDocumentOrArrayResolvedDocument, + trustedIssuers: &ArrayDocumentOrResolvedDocument, statusCheck: WasmStatusCheck, ) -> Result<()> { let trusted_issuers: Vec = trustedIssuers.into_serde().wasm_result()?; let status_check: StatusCheck = StatusCheck::from(statusCheck); - CredentialValidator::check_status(&credential.0, trusted_issuers.as_slice(), status_check).wasm_result() + CredentialValidator::check_status(&credential.0, &trusted_issuers, status_check).wasm_result() + } + + /// Utility for extracting the issuer field of a `Credential` as a DID. + /// + /// ### Errors + /// + /// Fails if the issuer field is not a valid DID. + #[wasm_bindgen(js_name = extractIssuer)] + pub fn extract_issuer(credential: &WasmCredential) -> Result { + let did: IotaDID = CredentialValidator::extract_issuer(&credential.0).wasm_result()?; + Ok(WasmDID::from(did)) } } diff --git a/bindings/wasm/src/credential/presentation_validator.rs b/bindings/wasm/src/credential/presentation_validator.rs index 61ca4e556b..df4cf58678 100644 --- a/bindings/wasm/src/credential/presentation_validator.rs +++ b/bindings/wasm/src/credential/presentation_validator.rs @@ -1,17 +1,20 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use identity_iota::credential::PresentationValidator; +use identity_iota::iota_core::IotaDID; +use identity_iota::iota_core::IotaDocument; +use wasm_bindgen::prelude::*; + use crate::credential::WasmFailFast; use crate::credential::WasmPresentation; use crate::credential::WasmPresentationValidationOptions; -use crate::did::ArrayDocumentOrArrayResolvedDocument; +use crate::did::ArrayDocumentOrResolvedDocument; use crate::did::DocumentOrResolvedDocument; +use crate::did::WasmDID; use crate::did::WasmVerifierOptions; use crate::error::Result; use crate::error::WasmResult; -use identity_iota::client::PresentationValidator; -use identity_iota::client::ResolvedIotaDocument; -use wasm_bindgen::prelude::*; #[wasm_bindgen(js_name = PresentationValidator, inspectable)] pub struct WasmPresentationValidator; @@ -48,12 +51,12 @@ impl WasmPresentationValidator { pub fn validate( presentation: &WasmPresentation, holder: &DocumentOrResolvedDocument, - issuers: &ArrayDocumentOrArrayResolvedDocument, + issuers: &ArrayDocumentOrResolvedDocument, options: &WasmPresentationValidationOptions, fail_fast: WasmFailFast, ) -> Result<()> { - let holder: ResolvedIotaDocument = holder.into_serde().wasm_result()?; - let issuers: Vec = issuers.into_serde().wasm_result()?; + let holder: IotaDocument = holder.into_serde().wasm_result()?; + let issuers: Vec = issuers.into_serde().wasm_result()?; PresentationValidator::validate(&presentation.0, &holder, &issuers, &options.0, fail_fast.into()).wasm_result() } @@ -71,7 +74,7 @@ impl WasmPresentationValidator { holder: &DocumentOrResolvedDocument, options: &WasmVerifierOptions, ) -> Result<()> { - let holder: ResolvedIotaDocument = holder.into_serde().wasm_result()?; + let holder: IotaDocument = holder.into_serde().wasm_result()?; PresentationValidator::verify_presentation_signature(&presentation.0, &holder, &options.0).wasm_result() } @@ -80,4 +83,15 @@ impl WasmPresentationValidator { pub fn check_structure(presentation: &WasmPresentation) -> Result<()> { PresentationValidator::check_structure(&presentation.0).wasm_result() } + + /// Utility for extracting the holder field of a `Presentation` as a DID. + /// + /// ### Errors + /// + /// Fails if the holder field is missing or not a valid DID. + #[wasm_bindgen(js_name = extractHolder)] + pub fn extract_holder(presentation: &WasmPresentation) -> Result { + let did: IotaDID = PresentationValidator::extract_holder(&presentation.0).wasm_result()?; + Ok(WasmDID::from(did)) + } } diff --git a/bindings/wasm/src/credential/validation_options.rs b/bindings/wasm/src/credential/validation_options.rs index 7b0958c367..be2f9174ec 100644 --- a/bindings/wasm/src/credential/validation_options.rs +++ b/bindings/wasm/src/credential/validation_options.rs @@ -1,11 +1,11 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use identity_iota::client::CredentialValidationOptions; -use identity_iota::client::FailFast; -use identity_iota::client::PresentationValidationOptions; -use identity_iota::client::StatusCheck; -use identity_iota::client::SubjectHolderRelationship; +use identity_iota::credential::CredentialValidationOptions; +use identity_iota::credential::FailFast; +use identity_iota::credential::PresentationValidationOptions; +use identity_iota::credential::StatusCheck; +use identity_iota::credential::SubjectHolderRelationship; use serde_repr::Deserialize_repr; use serde_repr::Serialize_repr; use wasm_bindgen::prelude::*; diff --git a/bindings/wasm/src/did/mod.rs b/bindings/wasm/src/did/mod.rs index 509133e3c8..37910196ea 100644 --- a/bindings/wasm/src/did/mod.rs +++ b/bindings/wasm/src/did/mod.rs @@ -13,7 +13,7 @@ pub use self::wasm_method_scope::OptionMethodScope; pub use self::wasm_method_scope::RefMethodScope; pub use self::wasm_method_scope::WasmMethodScope; pub use self::wasm_method_type::WasmMethodType; -pub use self::wasm_resolved_document::ArrayDocumentOrArrayResolvedDocument; +pub use self::wasm_resolved_document::ArrayDocumentOrResolvedDocument; pub use self::wasm_resolved_document::ArrayResolvedDocument; pub use self::wasm_resolved_document::DocumentOrResolvedDocument; pub use self::wasm_resolved_document::PromiseArrayResolvedDocument; diff --git a/bindings/wasm/src/did/wasm_document.rs b/bindings/wasm/src/did/wasm_document.rs index 7aee10ed4a..5f95d3e10e 100644 --- a/bindings/wasm/src/did/wasm_document.rs +++ b/bindings/wasm/src/did/wasm_document.rs @@ -13,15 +13,15 @@ use identity_iota::credential::Presentation; use identity_iota::crypto::PrivateKey; use identity_iota::crypto::ProofOptions; use identity_iota::did::verifiable::VerifiableProperties; +use identity_iota::did::Document; use identity_iota::did::MethodScope; -use identity_iota::did::DID; use identity_iota::iota_core::IotaDID; -use identity_iota::iota_core::IotaDIDUrl; use identity_iota::iota_core::IotaDocument; use identity_iota::iota_core::IotaVerificationMethod; use identity_iota::iota_core::MessageId; use identity_iota::iota_core::NetworkName; use wasm_bindgen::prelude::*; +use wasm_bindgen::JsCast; use crate::account::wasm_account::UOneOrManyNumber; use crate::common::MapStringAny; @@ -44,7 +44,6 @@ use crate::did::WasmVerificationMethod; use crate::did::WasmVerifierOptions; use crate::error::Result; use crate::error::WasmResult; -use wasm_bindgen::JsCast; // ============================================================================= // ============================================================================= @@ -229,6 +228,14 @@ impl WasmDocument { self.0.remove_service(&did.0) } + /// Returns the first {@link Service} with an `id` property matching the provided `query`, + /// if present. + #[wasm_bindgen(js_name = resolveService)] + pub fn resolve_service(&self, query: &UDIDUrlQuery) -> Option { + let service_query: String = query.into_serde().ok()?; + self.0.resolve_service(&service_query).cloned().map(WasmService::from) + } + // =========================================================================== // Verification Methods // =========================================================================== @@ -631,33 +638,35 @@ impl WasmDocument { self.0.proof.clone().map(WasmProof::from) } - /// If the document has a `RevocationBitmap` service identified by `fragment`, + /// If the document has a `RevocationBitmap` service identified by `serviceQuery`, /// revoke all credentials with a revocationBitmapIndex in `credentialIndices`. #[wasm_bindgen(js_name = revokeCredentials)] #[allow(non_snake_case)] - pub fn revoke_credentials(&mut self, fragment: &str, credentialIndices: UOneOrManyNumber) -> Result<()> { + pub fn revoke_credentials(&mut self, serviceQuery: &UDIDUrlQuery, credentialIndices: UOneOrManyNumber) -> Result<()> { + let query: String = serviceQuery.into_serde().wasm_result()?; let credentials_indices: OneOrMany = credentialIndices.into_serde().wasm_result()?; - let mut service_id: IotaDIDUrl = self.0.id().to_url(); - service_id.set_fragment(Some(fragment)).wasm_result()?; self .0 - .revoke_credentials(&service_id, credentials_indices.as_slice()) + .revoke_credentials(&query, credentials_indices.as_slice()) .wasm_result() } - /// If the document has a `RevocationBitmap` service identified by `fragment`, + /// If the document has a `RevocationBitmap` service identified by `serviceQuery`, /// unrevoke all credentials with a revocationBitmapIndex in `credentialIndices`. #[wasm_bindgen(js_name = unrevokeCredentials)] #[allow(non_snake_case)] - pub fn unrevoke_credentials(&mut self, fragment: &str, credentialIndices: UOneOrManyNumber) -> Result<()> { + pub fn unrevoke_credentials( + &mut self, + serviceQuery: &UDIDUrlQuery, + credentialIndices: UOneOrManyNumber, + ) -> Result<()> { + let query: String = serviceQuery.into_serde().wasm_result()?; let credentials_indices: OneOrMany = credentialIndices.into_serde().wasm_result()?; - let mut service_id: IotaDIDUrl = self.0.id().to_url(); - service_id.set_fragment(Some(fragment)).wasm_result()?; self .0 - .unrevoke_credentials(&service_id, credentials_indices.as_slice()) + .unrevoke_credentials(&query, credentials_indices.as_slice()) .wasm_result() } @@ -691,6 +700,7 @@ impl From for IotaDocument { wasm_document.0 } } + /// Duck-typed union to pass either a string or WasmDIDUrl as a parameter. #[wasm_bindgen] extern "C" { diff --git a/bindings/wasm/src/did/wasm_resolved_document.rs b/bindings/wasm/src/did/wasm_resolved_document.rs index c90768b886..e7b4e3208a 100644 --- a/bindings/wasm/src/did/wasm_resolved_document.rs +++ b/bindings/wasm/src/did/wasm_resolved_document.rs @@ -35,8 +35,8 @@ extern "C" { #[wasm_bindgen(typescript_type = "Document | ResolvedDocument")] pub type DocumentOrResolvedDocument; - #[wasm_bindgen(typescript_type = "Array | Array")] - pub type ArrayDocumentOrArrayResolvedDocument; + #[wasm_bindgen(typescript_type = "Array")] + pub type ArrayDocumentOrResolvedDocument; } #[wasm_bindgen(js_class = ResolvedDocument)] diff --git a/bindings/wasm/src/error.rs b/bindings/wasm/src/error.rs index 296b42b315..ed0cbf80cb 100644 --- a/bindings/wasm/src/error.rs +++ b/bindings/wasm/src/error.rs @@ -97,7 +97,7 @@ impl_wasm_error_from!( identity_iota::did::Error, identity_iota::did::DIDError, identity_iota::iota_core::Error, - identity_iota::client::ValidationError + identity_iota::credential::ValidationError ); // Similar to `impl_wasm_error_from`, but uses the types name instead of requiring/calling Into &'static str @@ -177,8 +177,8 @@ impl From for WasmError<'_> { } } -impl From for WasmError<'_> { - fn from(error: identity_iota::client::CompoundCredentialValidationError) -> Self { +impl From for WasmError<'_> { + fn from(error: identity_iota::credential::CompoundCredentialValidationError) -> Self { Self { name: Cow::Borrowed("CompoundCredentialValidationError"), message: Cow::Owned(error.to_string()), @@ -186,8 +186,8 @@ impl From for WasmErro } } -impl From for WasmError<'_> { - fn from(error: identity_iota::client::CompoundPresentationValidationError) -> Self { +impl From for WasmError<'_> { + fn from(error: identity_iota::credential::CompoundPresentationValidationError) -> Self { Self { name: Cow::Borrowed("CompoundPresentationValidationError"), message: Cow::Owned(error.to_string()), diff --git a/bindings/wasm/src/tangle/resolver.rs b/bindings/wasm/src/tangle/resolver.rs index d2c6b83ba8..531f6ce550 100644 --- a/bindings/wasm/src/tangle/resolver.rs +++ b/bindings/wasm/src/tangle/resolver.rs @@ -10,8 +10,10 @@ use identity_iota::client::ClientBuilder; use identity_iota::client::ResolvedIotaDocument; use identity_iota::client::Resolver; use identity_iota::client::ResolverBuilder; -use identity_iota::core::Url; +use identity_iota::credential::PresentationValidator; +use identity_iota::credential::ValidatorDocument; use identity_iota::iota_core::IotaDID; +use identity_iota::iota_core::IotaDocument; use identity_iota::iota_core::NetworkName; use js_sys::Promise; use wasm_bindgen::prelude::*; @@ -27,7 +29,8 @@ use crate::credential::WasmCredential; use crate::credential::WasmFailFast; use crate::credential::WasmPresentation; use crate::credential::WasmPresentationValidationOptions; -use crate::did::ArrayResolvedDocument; +use crate::did::ArrayDocumentOrResolvedDocument; +use crate::did::DocumentOrResolvedDocument; use crate::did::PromiseArrayResolvedDocument; use crate::did::PromiseResolvedDocument; use crate::did::UWasmDID; @@ -45,10 +48,6 @@ extern "C" { // Workaround for Typescript type annotations on async function returns. #[wasm_bindgen(typescript_type = "Promise")] pub type PromiseResolver; - - // Workaround for lack of &Option/Option<&Type> support. - #[wasm_bindgen(typescript_type = "ResolvedDocument")] - pub type RefResolvedDocument; } #[wasm_bindgen(js_class = Resolver)] @@ -215,14 +214,7 @@ impl WasmResolver { pub fn resolve_presentation_holder(&self, presentation: &WasmPresentation) -> Result { // TODO: reimplemented function to avoid cloning the entire presentation. // Would be solved with Rc internal representation, pending memory leak discussions. - let holder_url: &Url = presentation - .0 - .holder - .as_ref() - .ok_or(identity_iota::client::ValidationError::MissingPresentationHolder) - .map_err(identity_iota::client::Error::from) - .wasm_result()?; - let holder: IotaDID = IotaDID::parse(holder_url.as_str()).wasm_result()?; + let holder: IotaDID = PresentationValidator::extract_holder(&presentation.0).wasm_result()?; let resolver: Rc>> = Rc::clone(&self.0); let promise: Promise = future_to_promise(async move { @@ -260,25 +252,29 @@ impl WasmResolver { presentation: &WasmPresentation, options: &WasmPresentationValidationOptions, fail_fast: WasmFailFast, - holder: Option, - issuers: Option, + holder: Option, + issuers: Option, ) -> Result { // TODO: reimplemented function to avoid cloning the entire presentation and validation options. // Would be solved with Rc internal representation, pending memory leak discussions. - let holder: Option = holder.map(|js| js.into_serde().wasm_result()).transpose()?; - let issuers: Option> = issuers.map(|js| js.into_serde().wasm_result()).transpose()?; + let holder: Option = holder.map(|js| js.into_serde().wasm_result()).transpose()?; + let issuers: Option> = issuers.map(|js| js.into_serde().wasm_result()).transpose()?; + let resolver: Rc>> = Rc::clone(&self.0); let presentation: WasmPresentation = presentation.clone(); let options: WasmPresentationValidationOptions = options.clone(); let promise: Promise = future_to_promise(async move { + let issuer_refs: Option> = issuers + .as_ref() + .map(|issuers| issuers.iter().map(ValidatorDocument::as_validator).collect()); resolver .verify_presentation( &presentation.0, &options.0, fail_fast.into(), - holder.as_ref(), - issuers.as_deref(), + holder.as_ref().map(ValidatorDocument::as_validator), + issuer_refs.as_deref(), ) .await .map(|_| JsValue::UNDEFINED) diff --git a/examples/account/create_vc.rs b/examples/account/create_vc.rs index 760e2c6c96..fdf432ae43 100644 --- a/examples/account/create_vc.rs +++ b/examples/account/create_vc.rs @@ -12,16 +12,15 @@ use identity_iota::account::AccountBuilder; use identity_iota::account::IdentitySetup; use identity_iota::account::MethodContent; use identity_iota::account::Result; - -use identity_iota::client::CredentialValidationOptions; -use identity_iota::client::CredentialValidator; -use identity_iota::client::FailFast; use identity_iota::core::json; use identity_iota::core::FromJson; use identity_iota::core::ToJson; use identity_iota::core::Url; use identity_iota::credential::Credential; use identity_iota::credential::CredentialBuilder; +use identity_iota::credential::CredentialValidationOptions; +use identity_iota::credential::CredentialValidator; +use identity_iota::credential::FailFast; use identity_iota::credential::Subject; use identity_iota::crypto::ProofOptions; use identity_iota::did::DID; @@ -79,7 +78,7 @@ pub async fn create_vc() -> Result { // that the issuance date is not in the future and that the expiration date is not in the past: CredentialValidator::validate( &credential, - &issuer.document(), + issuer.document(), &CredentialValidationOptions::default(), FailFast::FirstError, ) diff --git a/examples/account/create_vp.rs b/examples/account/create_vp.rs index 091afef443..8534195661 100644 --- a/examples/account/create_vp.rs +++ b/examples/account/create_vp.rs @@ -10,6 +10,8 @@ use identity_iota::account::Account; use identity_iota::account::AccountBuilder; use identity_iota::account::IdentitySetup; use identity_iota::account::MethodContent; +use identity_iota::account::Result; +use identity_iota::client::Resolver; use identity_iota::core::json; use identity_iota::core::Duration; use identity_iota::core::FromJson; @@ -18,20 +20,16 @@ use identity_iota::core::ToJson; use identity_iota::core::Url; use identity_iota::credential::Credential; use identity_iota::credential::CredentialBuilder; +use identity_iota::credential::CredentialValidationOptions; +use identity_iota::credential::FailFast; use identity_iota::credential::Presentation; use identity_iota::credential::PresentationBuilder; +use identity_iota::credential::PresentationValidationOptions; use identity_iota::credential::Subject; +use identity_iota::credential::SubjectHolderRelationship; use identity_iota::crypto::ProofOptions; use identity_iota::did::verifiable::VerifierOptions; -use identity_iota::account::Result; -use identity_iota::client::CredentialValidationOptions; -use identity_iota::client::FailFast; -use identity_iota::client::PresentationValidationOptions; - -use identity_iota::client::Resolver; -use identity_iota::client::SubjectHolderRelationship; - #[tokio::main] async fn main() -> Result<()> { // =========================================================================== diff --git a/examples/account/revoke_vc.rs b/examples/account/revoke_vc.rs index 4518a9a98b..3154c345ea 100644 --- a/examples/account/revoke_vc.rs +++ b/examples/account/revoke_vc.rs @@ -17,19 +17,20 @@ use identity_iota::account::AccountBuilder; use identity_iota::account::IdentitySetup; use identity_iota::account::MethodContent; use identity_iota::account::Result; -use identity_iota::client::CredentialValidationOptions; -use identity_iota::client::CredentialValidator; use identity_iota::client::ResolvedIotaDocument; use identity_iota::client::Resolver; -use identity_iota::client::ValidationError; use identity_iota::core::json; use identity_iota::core::FromJson; use identity_iota::core::Url; use identity_iota::credential::Credential; use identity_iota::credential::CredentialBuilder; +use identity_iota::credential::CredentialValidationOptions; +use identity_iota::credential::CredentialValidator; +use identity_iota::credential::FailFast; use identity_iota::credential::RevocationBitmapStatus; use identity_iota::credential::Status; use identity_iota::credential::Subject; +use identity_iota::credential::ValidationError; use identity_iota::crypto::ProofOptions; use identity_iota::did::RevocationBitmap; use identity_iota::did::DID; @@ -97,9 +98,9 @@ async fn main() -> Result<()> { let validation_result = CredentialValidator::validate( &credential, - &issuer.document(), + issuer.document(), &CredentialValidationOptions::default(), - identity_iota::client::FailFast::FirstError, + FailFast::FirstError, ); // The credential wasn't revoked, so we expect the validation to succeed. @@ -117,9 +118,9 @@ async fn main() -> Result<()> { let validation_result = CredentialValidator::validate( &credential, - &issuer.document(), + issuer.document(), &CredentialValidationOptions::default(), - identity_iota::client::FailFast::FirstError, + FailFast::FirstError, ); // We expect validation to no longer succeed because the credential was revoked. @@ -146,9 +147,9 @@ async fn main() -> Result<()> { let resolved_issuer_doc: ResolvedIotaDocument = resolver.resolve_credential_issuer(&credential).await?; let validation_result = CredentialValidator::validate( &credential, - &resolved_issuer_doc, + &resolved_issuer_doc.document, &CredentialValidationOptions::default(), - identity_iota::client::FailFast::FirstError, + FailFast::FirstError, ); println!("VC validation result: {:?}", validation_result); diff --git a/identity_account_storage/src/storage/memstore.rs b/identity_account_storage/src/storage/memstore.rs index b404830e36..48303a220d 100644 --- a/identity_account_storage/src/storage/memstore.rs +++ b/identity_account_storage/src/storage/memstore.rs @@ -549,6 +549,7 @@ impl Default for MemStore { } #[cfg(test)] +#[cfg(feature = "storage-test-suite")] mod tests { use crate::storage::Storage; use crate::storage::StorageTestSuite; diff --git a/identity_account_storage/src/stronghold/tests.rs b/identity_account_storage/src/stronghold/tests.rs index 00025a2321..e4b47acdb5 100644 --- a/identity_account_storage/src/stronghold/tests.rs +++ b/identity_account_storage/src/stronghold/tests.rs @@ -1,6 +1,12 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use iota_stronghold::procedures; +use iota_stronghold::procedures::GenerateKey; +use iota_stronghold::Client; +use iota_stronghold::ClientVault; +use iota_stronghold::Location; + use identity_core::crypto::KeyPair; use identity_core::crypto::KeyType; use identity_core::crypto::PrivateKey; @@ -9,11 +15,6 @@ use identity_core::crypto::X25519; use identity_core::utils::Base::Base16Lower; use identity_core::utils::BaseEncoding; use identity_iota_core::did::IotaDID; -use iota_stronghold::procedures; -use iota_stronghold::procedures::GenerateKey; -use iota_stronghold::Client; -use iota_stronghold::ClientVault; -use iota_stronghold::Location; use crate::storage::stronghold::aead_decrypt; use crate::storage::stronghold::aead_encrypt; @@ -23,8 +24,6 @@ use crate::storage::stronghold::generate_private_key; use crate::storage::stronghold::insert_private_key; use crate::storage::stronghold::random_location; use crate::storage::stronghold::retrieve_public_key; -use crate::storage::Storage; -use crate::storage::StorageTestSuite; use crate::stronghold::test_util::random_did; use crate::stronghold::test_util::random_key_location; use crate::stronghold::test_util::random_string; @@ -190,74 +189,82 @@ async fn test_ecdhes_encryption() { assert_eq!(plaintext.to_vec(), data); } -async fn test_stronghold() -> impl Storage { - Stronghold::new(&random_temporary_path(), random_string(), Some(false)) - .await - .unwrap() -} - -#[tokio::test] -async fn test_stronghold_did_create_with_private_key() { - StorageTestSuite::did_create_private_key_test(test_stronghold().await) - .await - .unwrap() -} - -#[tokio::test] -async fn test_stronghold_did_create_generate_key() { - StorageTestSuite::did_create_generate_key_test(test_stronghold().await) - .await - .unwrap() -} - -#[tokio::test] -async fn test_stronghold_key_generate() { - StorageTestSuite::key_generate_test(test_stronghold().await) - .await - .unwrap() -} - -#[tokio::test] -async fn test_stronghold_key_delete() { - StorageTestSuite::key_delete_test(test_stronghold().await) - .await - .unwrap() -} - -#[tokio::test] -async fn test_stronghold_did_list() { - StorageTestSuite::did_list_test(test_stronghold().await).await.unwrap() -} - -#[tokio::test] -async fn test_stronghold_key_insert() { - StorageTestSuite::key_insert_test(test_stronghold().await) - .await - .unwrap() -} - -#[tokio::test] -async fn test_stronghold_key_sign_ed25519() { - StorageTestSuite::key_sign_ed25519_test(test_stronghold().await) - .await - .unwrap() -} - -#[tokio::test] -async fn test_stronghold_key_value_store() { - StorageTestSuite::key_value_store_test(test_stronghold().await) - .await - .unwrap() -} - -#[tokio::test] -async fn test_stronghold_did_purge() { - StorageTestSuite::did_purge_test(test_stronghold().await).await.unwrap() -} - -#[tokio::test] -async fn test_stronghold_encryption() { - StorageTestSuite::encryption_test(test_stronghold().await, test_stronghold().await) - .await - .unwrap() +#[cfg(feature = "storage-test-suite")] +mod stronghold_storage_test_suite { + use crate::storage::Storage; + use crate::storage::StorageTestSuite; + + use super::*; + + async fn test_stronghold() -> impl Storage { + Stronghold::new(&random_temporary_path(), random_string(), Some(false)) + .await + .unwrap() + } + + #[tokio::test] + async fn test_stronghold_did_create_with_private_key() { + StorageTestSuite::did_create_private_key_test(test_stronghold().await) + .await + .unwrap() + } + + #[tokio::test] + async fn test_stronghold_did_create_generate_key() { + StorageTestSuite::did_create_generate_key_test(test_stronghold().await) + .await + .unwrap() + } + + #[tokio::test] + async fn test_stronghold_key_generate() { + StorageTestSuite::key_generate_test(test_stronghold().await) + .await + .unwrap() + } + + #[tokio::test] + async fn test_stronghold_key_delete() { + StorageTestSuite::key_delete_test(test_stronghold().await) + .await + .unwrap() + } + + #[tokio::test] + async fn test_stronghold_did_list() { + StorageTestSuite::did_list_test(test_stronghold().await).await.unwrap() + } + + #[tokio::test] + async fn test_stronghold_key_insert() { + StorageTestSuite::key_insert_test(test_stronghold().await) + .await + .unwrap() + } + + #[tokio::test] + async fn test_stronghold_key_sign_ed25519() { + StorageTestSuite::key_sign_ed25519_test(test_stronghold().await) + .await + .unwrap() + } + + #[tokio::test] + async fn test_stronghold_key_value_store() { + StorageTestSuite::key_value_store_test(test_stronghold().await) + .await + .unwrap() + } + + #[tokio::test] + async fn test_stronghold_did_purge() { + StorageTestSuite::did_purge_test(test_stronghold().await).await.unwrap() + } + + #[tokio::test] + async fn test_stronghold_encryption() { + StorageTestSuite::encryption_test(test_stronghold().await, test_stronghold().await) + .await + .unwrap() + } } diff --git a/identity_core/src/crypto/proof/jcs_ed25519.rs b/identity_core/src/crypto/proof/jcs_ed25519.rs index b41d586c25..272fb39dac 100644 --- a/identity_core/src/crypto/proof/jcs_ed25519.rs +++ b/identity_core/src/crypto/proof/jcs_ed25519.rs @@ -56,7 +56,7 @@ where { fn verify(data: &X, signature: &ProofValue, public: &T::Public) -> Result<()> where - X: Serialize, + X: Serialize + ?Sized, { let signature: &str = signature .as_signature() diff --git a/identity_core/src/crypto/signature/core.rs b/identity_core/src/crypto/signature/core.rs index c5adbb2664..ea97561f99 100644 --- a/identity_core/src/crypto/signature/core.rs +++ b/identity_core/src/crypto/signature/core.rs @@ -73,17 +73,17 @@ pub trait Signer: Named { // ============================================================================= // ============================================================================= -/// A common interface for digital signature verification +/// A common interface for digital signature verification. pub trait Verifier: Named { /// Verifies the authenticity of `data` and `signature`. fn verify(data: &T, signature: &ProofValue, public: &Public) -> Result<()> where - T: Serialize; + T: Serialize + ?Sized; /// Extracts and verifies a proof [signature][`Proof`] from the given `data`. fn verify_signature(data: &T, public: &Public) -> Result<()> where - T: Serialize + GetSignature, + T: Serialize + GetSignature + ?Sized, { let signature: &Proof = data.signature().ok_or(Error::MissingSignature)?; @@ -93,7 +93,7 @@ pub trait Verifier: Named { signature.hide_value(); - Self::verify(&data, signature.value(), public)?; + Self::verify(data, signature.value(), public)?; signature.show_value(); diff --git a/identity_credential/Cargo.toml b/identity_credential/Cargo.toml index 520fc407d3..4e386489c7 100644 --- a/identity_credential/Cargo.toml +++ b/identity_credential/Cargo.toml @@ -8,17 +8,22 @@ keywords = ["iota", "tangle", "identity"] license = "Apache-2.0" readme = "./README.md" repository = "https://github.com/iotaledger/identity.rs" +rust-version = "1.60" description = "An implementation of the Verifiable Credentials standard." [dependencies] +erased-serde = { version = "0.3.21", default-features = false, features = ["std"], optional = true } identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } +itertools = { version = "0.10", default-features = false, features = ["use_std"], optional = true } lazy_static = { version = "1.4", default-features = false } serde = { version = "1.0", default-features = false, features = ["std", "derive"] } +serde_repr = { version = "0.1", default-features = false, optional = true } strum = { version = "0.24.0", default-features = false, features = ["std", "derive"] } thiserror = { version = "1.0", default-features = false } [dev-dependencies] +proptest = { version = "1.0.0", default-features = false, features = ["std"] } serde_json = { version = "1.0", default-features = false } [package.metadata.docs.rs] @@ -28,5 +33,6 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [features] -default = ["revocation-bitmap"] -revocation-bitmap = [] +default = ["revocation-bitmap", "validator"] +revocation-bitmap = ["identity_did/revocation-bitmap"] +validator = ["dep:itertools", "dep:erased-serde", "dep:serde_repr"] diff --git a/identity_credential/README.md b/identity_credential/README.md index b7fa06a6d2..715b84c59b 100644 --- a/identity_credential/README.md +++ b/identity_credential/README.md @@ -5,6 +5,11 @@ This crate contains types representing verifiable credentials and verifiable pre The [IOTA Identity Framework Wiki](https://wiki.iota.org/identity.rs/concepts/verifiable_credentials/overview) provides an overview of verifiable credentials and demonstrates how they may be constructed using the building blocks from this crate. +Convenience methods for validating [Verifiable Credentials](https://wiki.iota.org/identity.rs/concepts/verifiable_credentials/overview) and [Verifiable Presentations](https://wiki.iota.org/identity.rs/concepts/verifiable_credentials/verifiable_presentations) are also provided: + +- [`CredentialValidator`](crate::validator::CredentialValidator) +- [`PresentationValidator`](crate::validator::PresentationValidator) + ## Construction This crate follows the [builder pattern](https://rust-unofficial.github.io/patterns/patterns/creational/builder.html) for the creation of [`Credentials`](crate::credential::Credential) and [`Presentations`](crate::presentation::Presentation). @@ -158,4 +163,4 @@ let presentation_json: &'static str = r#"{ "#; let presentation: Presentation = serde_json::from_str(presentation_json).unwrap(); - ``` \ No newline at end of file +``` diff --git a/identity_credential/src/credential/revocation_bitmap_status.rs b/identity_credential/src/credential/revocation_bitmap_status.rs index ed8a295fc7..e26f0f59a1 100644 --- a/identity_credential/src/credential/revocation_bitmap_status.rs +++ b/identity_credential/src/credential/revocation_bitmap_status.rs @@ -13,13 +13,14 @@ use crate::credential::Status; use crate::error::Error; use crate::error::Result; -/// Information used to determine the current status of a [`Credential`][crate::credential::Credential]. +/// Information used to determine the current status of a [`Credential`][crate::credential::Credential] +/// using the `RevocationBitmap2022` specification. #[derive(Clone, Debug, PartialEq)] pub struct RevocationBitmapStatus(Status); impl RevocationBitmapStatus { const INDEX_PROPERTY_NAME: &'static str = "revocationBitmapIndex"; - /// The type name of the revocation bitmap. + /// Type name of the revocation bitmap. pub const TYPE: &'static str = "RevocationBitmap2022"; /// Creates a new `RevocationBitmapStatus`. @@ -33,7 +34,8 @@ impl RevocationBitmapStatus { )) } - /// Returns the [`DIDUrl`] of the bitmap status. + /// Returns the [`DIDUrl`] of the `RevocationBitmapStatus`, which should resolve + /// to a `RevocationBitmap2022` service in a DID Document. pub fn id(&self) -> Result> { DIDUrl::parse(self.0.id.as_str()) .map_err(|err| Error::InvalidStatus(format!("invalid DID Url '{}': {:?}", self.0.id, err))) diff --git a/identity_credential/src/error.rs b/identity_credential/src/error.rs index bd5ca9da2e..f713166b62 100644 --- a/identity_credential/src/error.rs +++ b/identity_credential/src/error.rs @@ -6,23 +6,23 @@ /// Alias for a `Result` with the error type [`Error`]. pub type Result = ::core::result::Result; -/// This type represents all possible errors that can occur in the library. +/// This type represents errors that can occur when constructing credentials and presentations. #[derive(Debug, thiserror::Error, strum::IntoStaticStr)] pub enum Error { - /// Caused when validating a Credential without a valid base context. - #[error("Missing Base Context")] + /// Caused when constructing a credential or presentation without a valid base context. + #[error("missing base context")] MissingBaseContext, - /// Caused when validating a Credential without a valid base type. - #[error("Missing Base Type")] + /// Caused when constructing a credential or presentation without a valid base type. + #[error("missing base type")] MissingBaseType, - /// Caused when validating a Credential without an issuer. - #[error("Missing Credential Issuer")] + /// Caused when constructing a credential without an issuer. + #[error("missing credential issuer")] MissingIssuer, - /// Caused when validating a Credential without a subject. - #[error("Missing Credential Subject")] + /// Caused when constructing a credential without a subject. + #[error("missing Credential subject")] MissingSubject, - /// Caused when validating a Credential with a malformed subject. - #[error("Invalid Credential Subject")] + /// Caused when constructing a credential with a malformed subject. + #[error("invalid credential subject")] InvalidSubject, /// Caused when trying to construct an invalid status. #[error("invalid credential status: {0}")] diff --git a/identity_credential/src/lib.rs b/identity_credential/src/lib.rs index 454447edf7..11e39cce7f 100644 --- a/identity_credential/src/lib.rs +++ b/identity_credential/src/lib.rs @@ -3,7 +3,6 @@ #![forbid(unsafe_code)] #![doc = include_str!("./../README.md")] -#![allow(clippy::upper_case_acronyms)] #![warn( rust_2018_idioms, unreachable_pub, @@ -18,13 +17,15 @@ #[macro_use] extern crate lazy_static; - #[macro_use] extern crate serde; +pub use self::error::Error; +pub use self::error::Result; + pub mod credential; pub mod error; pub mod presentation; -pub use self::error::Error; -pub use self::error::Result; +#[cfg(feature = "validator")] +pub mod validator; diff --git a/identity_iota_client/src/credential/credential_validator.rs b/identity_credential/src/validator/credential_validator.rs similarity index 84% rename from identity_iota_client/src/credential/credential_validator.rs rename to identity_credential/src/validator/credential_validator.rs index e6bda891db..81cb80eca9 100644 --- a/identity_iota_client/src/credential/credential_validator.rs +++ b/identity_credential/src/validator/credential_validator.rs @@ -1,23 +1,23 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use std::str::FromStr; + use serde::Serialize; use identity_core::common::OneOrMany; use identity_core::common::Timestamp; use identity_core::common::Url; -use identity_credential::credential::Credential; -#[cfg(feature = "revocation-bitmap")] -use identity_credential::credential::RevocationBitmapStatus; +use identity_did::did::CoreDID; +use identity_did::did::CoreDIDUrl; +use identity_did::did::DID; #[cfg(feature = "revocation-bitmap")] use identity_did::revocation::RevocationBitmap; use identity_did::verifiable::VerifierOptions; -use identity_iota_core::did::IotaDID; -#[cfg(feature = "revocation-bitmap")] -use identity_iota_core::did::IotaDIDUrl; -use identity_iota_core::document::IotaDocument; -use crate::Result; +use crate::credential::Credential; +#[cfg(feature = "revocation-bitmap")] +use crate::credential::RevocationBitmapStatus; use super::errors::CompoundCredentialValidationError; use super::errors::SignerContext; @@ -27,6 +27,7 @@ use super::validation_options::StatusCheck; use super::CredentialValidationOptions; use super::FailFast; use super::SubjectHolderRelationship; +use super::ValidatorDocument; /// A struct for validating [`Credential`]s. #[derive(Debug, Clone)] @@ -51,9 +52,7 @@ impl CredentialValidator { /// calling this method. /// /// ## The state of the issuer's DID Document - /// The caller must ensure that `issuer` represents an up-to-date DID Document. The convenience method - /// [`Resolver::resolve_credential_issuer`](crate::tangle::Resolver::resolve_credential_issuer()) can help extract - /// the latest available state of the issuer's DID Document. + /// The caller must ensure that `issuer` represents an up-to-date DID Document. /// /// ## Properties that are not validated /// There are many properties defined in [The Verifiable Credentials Data Model](https://www.w3.org/TR/vc-data-model/) that are **not** validated, such as: @@ -62,9 +61,9 @@ impl CredentialValidator { /// /// # Errors /// An error is returned whenever a validated condition is not satisfied. - pub fn validate>( + pub fn validate( credential: &Credential, - issuer: &D, + issuer: &DOC, options: &CredentialValidationOptions, fail_fast: FailFast, ) -> CredentialValidationResult { @@ -107,42 +106,24 @@ impl CredentialValidator { /// This method immediately returns an error if /// the credential issuer' url cannot be parsed to a DID belonging to one of the trusted issuers. Otherwise an attempt /// to verify the credential's signature will be made and an error is returned upon failure. - pub fn verify_signature>( + pub fn verify_signature( credential: &Credential, - trusted_issuers: &[D], + trusted_issuers: &[DOC], options: &VerifierOptions, ) -> ValidationUnitResult { - // try to extract the corresponding issuer from `trusted_issuers` - let extracted_issuer_result: std::result::Result<&IotaDocument, ValidationError> = { - let issuer_did: Result = credential.issuer.url().as_str().parse().map_err(Into::into); - match issuer_did { - Ok(did) => { - // if the issuer_did corresponds to one of the trusted issuers we use the corresponding DID Document to verify - // the signature - trusted_issuers - .iter() - .map(|issuer_doc| issuer_doc.as_ref()) - .find(|issuer_doc| issuer_doc.id() == &did) - .ok_or(ValidationError::DocumentMismatch(SignerContext::Issuer)) - } - Err(error) => { - // the issuer's url could not be parsed to a valid IotaDID - Err(ValidationError::SignerUrl { - source: error.into(), + let issuer_did: CoreDID = Self::extract_issuer(credential)?; + trusted_issuers + .iter() + .find(|issuer_doc| issuer_doc.did_str() == issuer_did.as_str()) + .ok_or(ValidationError::DocumentMismatch(SignerContext::Issuer)) + .and_then(|issuer| { + issuer + .verify_data(credential, options) + .map_err(|err| ValidationError::Signature { + source: err.into(), signer_ctx: SignerContext::Issuer, }) - } - } - }; - // use the extracted document to verify the signature - extracted_issuer_result.and_then(|issuer| { - issuer - .verify_data(credential, options) - .map_err(|error| ValidationError::Signature { - source: error.into(), - signer_ctx: SignerContext::Issuer, - }) - }) + }) } /// Validate that the relationship between the `holder` and the credential subjects is in accordance with @@ -184,9 +165,9 @@ impl CredentialValidator { /// /// Only supports `BitmapRevocation2022`. #[cfg(feature = "revocation-bitmap")] - pub fn check_status>( + pub fn check_status( credential: &Credential, - trusted_issuers: &[D], + trusted_issuers: &[DOC], status_check: StatusCheck, ) -> ValidationUnitResult { if status_check == StatusCheck::SkipAll { @@ -201,22 +182,19 @@ impl CredentialValidator { if status_check == StatusCheck::SkipUnsupported { return Ok(()); } - return Err(ValidationError::InvalidStatus( - identity_credential::Error::InvalidStatus(format!("unsupported type '{}'", status.type_)), - )); + return Err(ValidationError::InvalidStatus(crate::Error::InvalidStatus(format!( + "unsupported type '{}'", + status.type_ + )))); } let status: RevocationBitmapStatus = RevocationBitmapStatus::try_from(status.clone()).map_err(ValidationError::InvalidStatus)?; // Check the credential index against the issuer's DID Document. - let issuer_did: IotaDID = - IotaDID::parse(credential.issuer.url().as_str()).map_err(|e| ValidationError::SignerUrl { - source: e.into(), - signer_ctx: SignerContext::Issuer, - })?; + let issuer_did: CoreDID = Self::extract_issuer(credential)?; trusted_issuers .iter() - .find(|issuer| issuer.as_ref().id() == &issuer_did) + .find(|issuer| issuer.did_str() == issuer_did.as_str()) .ok_or(ValidationError::DocumentMismatch(SignerContext::Issuer)) .and_then(|issuer| CredentialValidator::check_revocation_bitmap_status(issuer, status)) } @@ -226,24 +204,16 @@ impl CredentialValidator { /// Check the given `status` against the matching [`RevocationBitmap`] service in the /// issuer's DID Document. #[cfg(feature = "revocation-bitmap")] - fn check_revocation_bitmap_status>( - issuer: D, + fn check_revocation_bitmap_status( + issuer: &DOC, status: RevocationBitmapStatus, ) -> ValidationUnitResult { - let issuer_service_url: IotaDIDUrl = status.id().map_err(ValidationError::InvalidStatus)?; - - // Lookup service. - let service = issuer - .as_ref() - .service() - .iter() - .find(|service| &issuer_service_url == service.id()) - .ok_or(ValidationError::InvalidService(identity_did::Error::InvalidService( - "revocation bitmap service not found", - )))?; + let issuer_service_url: CoreDIDUrl = status.id().map_err(ValidationError::InvalidStatus)?; // Check whether index is revoked. - let revocation_bitmap: RevocationBitmap = service.try_into().map_err(ValidationError::InvalidService)?; + let revocation_bitmap: RevocationBitmap = issuer + .resolve_revocation_bitmap(issuer_service_url.into()) + .map_err(ValidationError::InvalidService)?; let index: u32 = status.index().map_err(ValidationError::InvalidStatus)?; if revocation_bitmap.is_revoked(index) { Err(ValidationError::Revoked) @@ -255,9 +225,9 @@ impl CredentialValidator { // This method takes a slice of issuer's instead of a single issuer in order to better accommodate presentation // validation. It also validates the relation ship between a holder and the credential subjects when // `relationship_criterion` is Some. - pub(crate) fn validate_extended>( + pub(crate) fn validate_extended( credential: &Credential, - issuers: &[D], + issuers: &[DOC], options: &CredentialValidationOptions, relationship_criterion: Option<(&Url, SubjectHolderRelationship)>, fail_fast: FailFast, @@ -306,6 +276,21 @@ impl CredentialValidator { Err(CompoundCredentialValidationError { validation_errors }) } } + + /// Utility for extracting the issuer field of a [`Credential`] as a DID. + /// + /// # Errors + /// + /// Fails if the issuer field is not a valid DID. + pub fn extract_issuer(credential: &Credential) -> std::result::Result + where + ::Err: std::error::Error + Send + Sync + 'static, + { + D::from_str(credential.issuer.url().as_str()).map_err(|err| ValidationError::SignerUrl { + signer_ctx: SignerContext::Issuer, + source: err.into(), + }) + } } #[cfg(test)] @@ -319,14 +304,14 @@ mod tests { use identity_core::convert::FromJson; use identity_core::crypto::KeyPair; use identity_core::crypto::ProofOptions; - use identity_credential::credential::Status; - use identity_credential::credential::Subject; use identity_did::did::DID; - use identity_iota_core::document::IotaDocument; - use identity_iota_core::document::IotaService; + use identity_did::document::CoreDocument; + use identity_did::service::Service; - use crate::credential::test_utils; - use crate::credential::CredentialValidationOptions; + use crate::credential::Status; + use crate::credential::Subject; + use crate::validator::test_utils; + use crate::validator::CredentialValidationOptions; use super::*; @@ -359,7 +344,7 @@ mod tests { // Setup parameters shared by many of the tests in this module struct Setup { - issuer_doc: IotaDocument, + issuer_doc: CoreDocument, issuer_key: KeyPair, unsigned_credential: Credential, issuance_date: Timestamp, @@ -427,12 +412,10 @@ mod tests { issuance_date, } = Setup::new(); issuer_doc - .sign_data( - &mut credential, - issuer_key.private(), - issuer_doc.default_signing_method().unwrap().id(), - ProofOptions::default(), - ) + .signer(issuer_key.private()) + .options(ProofOptions::default()) + .method(issuer_doc.methods().next().unwrap().id()) + .sign(&mut credential) .unwrap(); // declare the credential validation parameters @@ -499,12 +482,10 @@ mod tests { issuance_date, } = Setup::new(); issuer_doc - .sign_data( - &mut credential, - issuer_key.private(), - issuer_doc.default_signing_method().unwrap().id(), - ProofOptions::default(), - ) + .signer(issuer_key.private()) + .options(ProofOptions::default()) + .method(issuer_doc.methods().next().unwrap().id()) + .sign(&mut credential) .unwrap(); // declare the credential validation parameters @@ -539,15 +520,13 @@ mod tests { expiration_date, } = Setup::new(); issuer_doc - .sign_data( - &mut credential, - issuer_key.private(), - issuer_doc.default_signing_method().unwrap().id(), - ProofOptions::default(), - ) + .signer(issuer_key.private()) + .options(ProofOptions::default()) + .method(issuer_doc.methods().next().unwrap().id()) + .sign(&mut credential) .unwrap(); - // declare the credential validation parameters + // declare the credential validation parameters let issued_on_or_before = issuance_date.checked_add(Duration::days(14)).unwrap(); let expires_on_or_after = expiration_date.checked_sub(Duration::hours(1)).unwrap(); let options = CredentialValidationOptions::default() @@ -567,12 +546,10 @@ mod tests { } = Setup::new(); let (other_doc, _) = test_utils::generate_document_with_keys(); issuer_doc - .sign_data( - &mut credential, - issuer_key.private(), - issuer_doc.default_signing_method().unwrap().id(), - ProofOptions::default(), - ) + .signer(issuer_key.private()) + .options(ProofOptions::default()) + .method(issuer_doc.methods().next().unwrap().id()) + .sign(&mut credential) .unwrap(); // the credential was not signed by this issuer @@ -580,12 +557,7 @@ mod tests { // check that `verify_signature` returns the expected error assert!(matches!( - CredentialValidator::verify_signature( - &credential, - std::slice::from_ref(&other_doc), - &VerifierOptions::default() - ) - .unwrap_err(), + CredentialValidator::verify_signature(&credential, &[&other_doc], &VerifierOptions::default()).unwrap_err(), ValidationError::DocumentMismatch { .. } )); @@ -621,22 +593,15 @@ mod tests { let (_, other_keys) = test_utils::generate_document_with_keys(); issuer_doc - .sign_data( - &mut credential, - other_keys.private(), // sign with other keys - issuer_doc.default_signing_method().unwrap().id(), - ProofOptions::default(), - ) + .signer(other_keys.private()) + .options(ProofOptions::default()) + .method(issuer_doc.methods().next().unwrap().id()) + .sign(&mut credential) .unwrap(); // run the validation unit assert!(matches!( - CredentialValidator::verify_signature( - &credential, - std::slice::from_ref(&issuer_doc), - &VerifierOptions::default() - ) - .unwrap_err(), + CredentialValidator::verify_signature(&credential, &[&issuer_doc], &VerifierOptions::default()).unwrap_err(), ValidationError::Signature { .. } )); @@ -797,7 +762,7 @@ mod tests { } // Add a RevocationBitmap status to the credential. - let service_url: IotaDIDUrl = issuer_doc.id().to_url().join("#revocation-service").unwrap(); + let service_url: CoreDIDUrl = issuer_doc.id().to_url().join("#revocation-service").unwrap(); let index: u32 = 42; credential.credential_status = Some(RevocationBitmapStatus::new(service_url.clone(), index).into()); @@ -815,13 +780,13 @@ mod tests { // Add a RevocationBitmap service to the issuer. let bitmap: RevocationBitmap = RevocationBitmap::new(); - assert!(issuer_doc.insert_service( - IotaService::builder(Object::new()) + assert!(issuer_doc.service_mut().append( + Service::builder(Object::new()) .id(service_url.clone()) .type_(RevocationBitmap::TYPE) .service_endpoint(bitmap.to_endpoint().unwrap()) .build() - .unwrap(), + .unwrap() )); // 3: un-revoked index always succeeds. @@ -854,12 +819,10 @@ mod tests { } = Setup::new(); issuer_doc - .sign_data( - &mut credential, - issuer_key.private(), - issuer_doc.default_signing_method().unwrap().id(), - ProofOptions::default(), - ) + .signer(issuer_key.private()) + .options(ProofOptions::default()) + .method(issuer_doc.methods().next().unwrap().id()) + .sign(&mut credential) .unwrap(); // the credential now has no credential subjects which is not semantically correct credential.credential_subject = OneOrMany::default(); @@ -896,12 +859,10 @@ mod tests { let (other_doc, _) = test_utils::generate_document_with_keys(); issuer_doc - .sign_data( - &mut credential, - issuer_key.private(), - issuer_doc.default_signing_method().unwrap().id(), - ProofOptions::default(), - ) + .signer(issuer_key.private()) + .options(ProofOptions::default()) + .method(issuer_doc.methods().next().unwrap().id()) + .sign(&mut credential) .unwrap(); // the credential now has no credential subjects which is not semantically correct credential.credential_subject = OneOrMany::default(); @@ -937,12 +898,10 @@ mod tests { let (other_doc, _) = test_utils::generate_document_with_keys(); issuer_doc - .sign_data( - &mut credential, - issuer_key.private(), - issuer_doc.default_signing_method().unwrap().id(), - ProofOptions::default(), - ) + .signer(issuer_key.private()) + .options(ProofOptions::default()) + .method(issuer_doc.methods().next().unwrap().id()) + .sign(&mut credential) .unwrap(); // the credential now has no credential subjects which is not semantically correct credential.credential_subject = OneOrMany::default(); diff --git a/identity_iota_client/src/credential/errors.rs b/identity_credential/src/validator/errors.rs similarity index 76% rename from identity_iota_client/src/credential/errors.rs rename to identity_credential/src/validator/errors.rs index edcaebc38a..1ae8683876 100644 --- a/identity_iota_client/src/credential/errors.rs +++ b/identity_credential/src/validator/errors.rs @@ -4,6 +4,8 @@ use std::collections::BTreeMap; use std::fmt::Display; +use itertools; + #[derive(Debug, thiserror::Error, strum::IntoStaticStr)] #[non_exhaustive] /// An error associated with validating credentials and presentations. @@ -14,38 +16,44 @@ pub enum ValidationError { /// Indicates that the issuance date of the credential is not considered valid. #[error("issuance date is in the future or later than required")] IssuanceDate, - /// Indicates that the credential's (resp. presentation's) signature could not be verified using the issuer's (resp. - /// holder's) DID Document. + /// Indicates that the credential's (resp. presentation's) signature could not be verified using + /// the issuer's (resp. holder's) DID Document. #[error("could not verify the {signer_ctx}'s signature")] #[non_exhaustive] Signature { + /// Signature verification error. source: Box, + /// Specifies whether the error was from the DID Document of a credential issuer + /// or the presentation holder. signer_ctx: SignerContext, }, - /// Indicates that the credential's (resp. presentation's) issuer's (resp. holder's) URL could not be parsed as a - /// valid DID. + /// Indicates that the credential's (resp. presentation's) issuer's (resp. holder's) URL could + /// not be parsed as a valid DID. #[error("{signer_ctx} URL is not a valid DID")] #[non_exhaustive] SignerUrl { + /// DID parsing error. source: Box, + /// Specifies whether the error relates to the DID of a credential issuer + /// or the presentation holder. signer_ctx: SignerContext, }, - /// Indicates an attempt to verify a signature of a credential (resp. presentation) using a DID Document not matching - /// the issuer's (resp. holder's) id. + /// Indicates an attempt to verify a signature of a credential (resp. presentation) using a + /// DID Document not matching the issuer's (resp. holder's) id. #[error("the {0}'s id does not match the provided DID Document(s)")] #[non_exhaustive] DocumentMismatch(SignerContext), - /// Indicates that the structure of the [Credential](identity_credential::credential::Credential) is not semantically + /// Indicates that the structure of the [Credential](crate::credential::Credential) is not semantically /// correct. #[error("the credential's structure is not semantically correct")] - CredentialStructure(#[source] identity_credential::Error), - /// Indicates that the structure of the [Presentation](identity_credential::presentation::Presentation) is not + CredentialStructure(#[source] crate::Error), + /// Indicates that the structure of the [Presentation](crate::presentation::Presentation) is not /// semantically correct. #[error("the presentation's structure is not semantically correct")] - PresentationStructure(#[source] identity_credential::Error), + PresentationStructure(#[source] crate::Error), /// Indicates that the relationship between the presentation holder and one of the credential subjects is not valid. #[error("expected holder = subject of the credential")] #[non_exhaustive] @@ -55,7 +63,7 @@ pub enum ValidationError { MissingPresentationHolder, /// Indicates that the credential's status is invalid. #[error("invalid credential status")] - InvalidStatus(#[source] identity_credential::Error), + InvalidStatus(#[source] crate::Error), /// Indicates that the the credential's service is invalid. #[error("invalid service")] InvalidService(#[source] identity_did::Error), @@ -64,10 +72,13 @@ pub enum ValidationError { Revoked, } +/// Specifies whether an error is related to a credential issuer or the presentation holder. #[derive(Debug)] #[non_exhaustive] pub enum SignerContext { + /// Credential issuer. Issuer, + /// Presentation holder. Holder, } @@ -81,9 +92,10 @@ impl Display for SignerContext { } } +/// Errors caused by a failure to validate a credential. #[derive(Debug)] -/// An error caused by a failure to validate a Credential. pub struct CompoundCredentialValidationError { + /// List of credential validation errors. pub validation_errors: Vec, } @@ -104,7 +116,10 @@ impl std::error::Error for CompoundCredentialValidationError {} #[derive(Debug)] /// An error caused by a failure to validate a Presentation. pub struct CompoundPresentationValidationError { + /// Errors that occurred during validation of individual credentials, mapped by index of their + /// order in the presentation. pub credential_errors: BTreeMap, + /// Errors that occurred during validation of the presentation. pub presentation_validation_errors: Vec, } diff --git a/identity_iota_client/src/credential/mod.rs b/identity_credential/src/validator/mod.rs similarity index 91% rename from identity_iota_client/src/credential/mod.rs rename to identity_credential/src/validator/mod.rs index 35a87e8491..f494dc2bdf 100644 --- a/identity_iota_client/src/credential/mod.rs +++ b/identity_credential/src/validator/mod.rs @@ -14,6 +14,7 @@ pub use self::validation_options::FailFast; pub use self::validation_options::PresentationValidationOptions; pub use self::validation_options::StatusCheck; pub use self::validation_options::SubjectHolderRelationship; +pub use self::validator_document::ValidatorDocument; mod credential_validator; mod errors; @@ -21,3 +22,4 @@ mod presentation_validator; #[cfg(test)] mod test_utils; mod validation_options; +mod validator_document; diff --git a/identity_iota_client/src/credential/presentation_validator.rs b/identity_credential/src/validator/presentation_validator.rs similarity index 84% rename from identity_iota_client/src/credential/presentation_validator.rs rename to identity_credential/src/validator/presentation_validator.rs index 0aa23bc385..ddae49f703 100644 --- a/identity_iota_client/src/credential/presentation_validator.rs +++ b/identity_credential/src/validator/presentation_validator.rs @@ -2,20 +2,25 @@ // SPDX-License-Identifier: Apache-2.0 use std::collections::BTreeMap; +use std::str::FromStr; -use identity_credential::presentation::Presentation; -use identity_did::verifiable::VerifierOptions; -use identity_iota_core::did::IotaDID; -use identity_iota_core::document::IotaDocument; use serde::Serialize; +use identity_core::common::Url; +use identity_did::did::CoreDID; +use identity_did::did::DID; +use identity_did::verifiable::VerifierOptions; + +use crate::presentation::Presentation; + use super::errors::CompoundCredentialValidationError; use super::errors::CompoundPresentationValidationError; use super::errors::SignerContext; use super::errors::ValidationError; +use super::CredentialValidator; use super::FailFast; use super::PresentationValidationOptions; -use crate::credential::credential_validator::CredentialValidator; +use super::ValidatorDocument; /// A struct for validating [`Presentation`]s. #[derive(Debug, Clone)] @@ -41,10 +46,7 @@ impl PresentationValidator { /// calling this method. /// /// ## The state of the supplied DID Documents. - /// The caller must ensure that the DID Documents in `holder` and `issuers` are up-to-date. The convenience methods - /// [`Resolver::resolve_presentation_holder`](crate::tangle::Resolver::resolve_presentation_holder()) - /// and [`Resolver::resolve_presentation_issuers`](crate::tangle::Resolver::resolve_presentation_issuers()) - /// can help extract the latest available states of these DID Documents. + /// The caller must ensure that the DID Documents in `holder` and `issuers` are up-to-date. /// /// ## Properties that are not validated /// There are many properties defined in [The Verifiable Credentials Data Model](https://www.w3.org/TR/vc-data-model/) that are **not** validated, such as: @@ -53,10 +55,10 @@ impl PresentationValidator { /// /// # Errors /// An error is returned whenever a validated condition is not satisfied. - pub fn validate>( + pub fn validate( presentation: &Presentation, - holder: &D, - issuers: &[D], + holder: &HDOC, + issuers: &[IDOC], options: &PresentationValidationOptions, fail_fast: FailFast, ) -> PresentationValidationResult { @@ -109,29 +111,19 @@ impl PresentationValidator { /// # Errors /// Fails if the `holder` does not match the `presentation`'s holder property. /// Fails if signature verification against the holder document fails. - pub fn verify_presentation_signature>( + pub fn verify_presentation_signature( presentation: &Presentation, - holder: &D, + holder: &DOC, options: &VerifierOptions, ) -> ValidationUnitResult { - let did: IotaDID = presentation - .holder - .as_ref() - .ok_or(ValidationError::MissingPresentationHolder) - .and_then(|value| { - IotaDID::parse(value.as_str()).map_err(|error| ValidationError::SignerUrl { - source: error.into(), - signer_ctx: SignerContext::Holder, - }) - })?; - if &did != holder.as_ref().id() { + let did: CoreDID = Self::extract_holder(presentation)?; + if did.as_str() != holder.did_str() { return Err(ValidationError::DocumentMismatch(SignerContext::Holder)); } holder - .as_ref() .verify_data(&presentation, options) - .map_err(|error| ValidationError::Signature { - source: error.into(), + .map_err(|err| ValidationError::Signature { + source: err.into(), signer_ctx: SignerContext::Holder, }) } @@ -148,9 +140,9 @@ impl PresentationValidator { // The following properties are validated according to `options`: // - the semantic structure of the presentation, // - the holder's signature, - fn validate_presentation_without_credentials>( + fn validate_presentation_without_credentials( presentation: &Presentation, - holder: &D, + holder: &DOC, options: &PresentationValidationOptions, fail_fast: FailFast, ) -> Result<(), Vec> { @@ -181,9 +173,9 @@ impl PresentationValidator { // - the relationship between the holder and the credential subjects, // - the signatures and some properties of the constituent credentials (see // [`CredentialValidator::validate`]). - fn validate_credentials>( + fn validate_credentials( presentation: &Presentation, - issuers: &[D], + issuers: &[DOC], options: &PresentationValidationOptions, fail_fast: FailFast, ) -> Result<(), BTreeMap> { @@ -219,6 +211,25 @@ impl PresentationValidator { Err(credential_errors) } } + + /// Utility for extracting the holder field of a [`Presentation`] as a DID. + /// + /// # Errors + /// + /// Fails if the holder field is missing or not a valid DID. + pub fn extract_holder(presentation: &Presentation) -> std::result::Result + where + ::Err: std::error::Error + Send + Sync + 'static, + { + let holder: &Url = presentation + .holder + .as_ref() + .ok_or(ValidationError::MissingPresentationHolder)?; + D::from_str(holder.as_str()).map_err(|err| ValidationError::SignerUrl { + signer_ctx: SignerContext::Issuer, + source: err.into(), + }) + } } #[cfg(test)] @@ -227,18 +238,19 @@ mod tests { use identity_core::common::Url; use identity_core::crypto::KeyPair; use identity_core::crypto::ProofOptions; - use identity_credential::credential::Credential; - use identity_credential::presentation::PresentationBuilder; - use identity_iota_core::document::IotaDocument; + use identity_did::document::CoreDocument; + + use crate::credential::Credential; + use crate::presentation::PresentationBuilder; + use crate::validator::test_utils; + use crate::validator::CredentialValidationOptions; + use crate::validator::SubjectHolderRelationship; use super::*; - use crate::credential::test_utils; - use crate::credential::CredentialValidationOptions; - use crate::credential::SubjectHolderRelationship; - fn build_presentation(holder: &IotaDocument, credentials: Vec) -> Presentation { + fn build_presentation(holder: &CoreDocument, credentials: Vec) -> Presentation { let mut builder = PresentationBuilder::default() - .id(Url::parse("http://example.org/credentials/3732").unwrap()) + .id(Url::parse("https://example.org/credentials/3732").unwrap()) .holder(Url::parse(holder.id().as_ref()).unwrap()); for credential in credentials { builder = builder.credential(credential); @@ -249,14 +261,14 @@ mod tests { // Convenience struct for setting up tests. struct TestSetup { // issuer of credential_foo - issuer_foo_doc: IotaDocument, + issuer_foo_doc: CoreDocument, issuer_foo_key: KeyPair, // subject of credential_foo - subject_foo_doc: IotaDocument, + subject_foo_doc: CoreDocument, subject_foo_key: KeyPair, credential_foo: Credential, // issuer of credential_bar - issuer_bar_doc: IotaDocument, + issuer_bar_doc: CoreDocument, issuer_bar_key: KeyPair, credential_bar: Credential, } @@ -304,21 +316,17 @@ mod tests { } = setup; // sign the credential issuer_foo_doc - .sign_data( - credential_foo, - issuer_foo_key.private(), - issuer_foo_doc.default_signing_method().unwrap().id(), - ProofOptions::default(), - ) + .signer(issuer_foo_key.private()) + .options(ProofOptions::default()) + .method(issuer_foo_doc.methods().next().unwrap().id()) + .sign(credential_foo) .unwrap(); issuer_bar_doc - .sign_data( - credential_bar, - issuer_bar_key.private(), - issuer_bar_doc.default_signing_method().unwrap().id(), - ProofOptions::default(), - ) + .signer(issuer_bar_key.private()) + .options(ProofOptions::default()) + .method(issuer_bar_doc.methods().next().unwrap().id()) + .sign(credential_bar) .unwrap(); setup } @@ -339,14 +347,11 @@ mod tests { let mut presentation = build_presentation(&subject_foo_doc, [credential_foo, credential_bar].to_vec()); // sign the presentation using subject_foo's document and private key - subject_foo_doc - .sign_data( - &mut presentation, - subject_foo_key.private(), - subject_foo_doc.default_signing_method().unwrap().id(), - ProofOptions::new().challenge("475a7984-1bb5-4c4c-a56f-822bccd46440".to_owned()), - ) + .signer(subject_foo_key.private()) + .options(ProofOptions::new().challenge("475a7984-1bb5-4c4c-a56f-822bccd46440".to_owned())) + .method(subject_foo_doc.methods().next().unwrap().id()) + .sign(&mut presentation) .unwrap(); // validate the presentation @@ -362,15 +367,13 @@ mod tests { .presentation_verifier_options(presentation_verifier_options) .subject_holder_relationship(SubjectHolderRelationship::SubjectOnNonTransferable); - let trusted_issuers = [issuer_foo_doc, issuer_bar_doc]; - let holder_doc = subject_foo_doc; assert!(dbg!(PresentationValidator::validate( &presentation, &holder_doc, - &trusted_issuers, + &[&issuer_foo_doc, &issuer_bar_doc], &presentation_validation_options, - FailFast::FirstError + FailFast::FirstError, )) .is_ok()); } @@ -390,18 +393,14 @@ mod tests { let mut presentation = build_presentation(&subject_foo_doc, [credential_foo, credential_bar].to_vec()); // sign the presentation using subject_foo's document and private key - subject_foo_doc - .sign_data( - &mut presentation, - subject_foo_key.private(), - subject_foo_doc.default_signing_method().unwrap().id(), - ProofOptions::new().challenge("some challenge".to_owned()), - ) + .signer(subject_foo_key.private()) + .options(ProofOptions::new().challenge("some challenge".to_owned())) + .method(subject_foo_doc.methods().next().unwrap().id()) + .sign(&mut presentation) .unwrap(); // check the validation unit - let trusted_issuers = [issuer_foo_doc, issuer_bar_doc]; let presentation_verifier_options = VerifierOptions::default().challenge("another challenge".to_owned()); //validate with another challenge let holder_doc = subject_foo_doc; @@ -410,7 +409,7 @@ mod tests { assert!(PresentationValidator::verify_presentation_signature( &presentation, &holder_doc, - &presentation_verifier_options + &presentation_verifier_options, ) .is_err()); @@ -429,7 +428,7 @@ mod tests { let error = PresentationValidator::validate( &presentation, &holder_doc, - &trusted_issuers, + &[&issuer_foo_doc, &issuer_bar_doc], &presentation_validation_options, FailFast::FirstError, ) @@ -459,14 +458,11 @@ mod tests { let mut presentation = build_presentation(&subject_foo_doc, [credential_foo, credential_bar].to_vec()); // sign the presentation using subject_foo's document and private key - subject_foo_doc - .sign_data( - &mut presentation, - subject_foo_key.private(), - subject_foo_doc.default_signing_method().unwrap().id(), - ProofOptions::new().challenge("some challenge".to_owned()), - ) + .signer(subject_foo_key.private()) + .options(ProofOptions::new().challenge("some challenge".to_owned())) + .method(subject_foo_doc.methods().next().unwrap().id()) + .sign(&mut presentation) .unwrap(); // validate the presentation @@ -481,13 +477,11 @@ mod tests { .presentation_verifier_options(presentation_verifier_options) .subject_holder_relationship(SubjectHolderRelationship::SubjectOnNonTransferable); - let trusted_issuers = [issuer_foo_doc, issuer_bar_doc]; - let holder_doc = subject_foo_doc; let error = PresentationValidator::validate( &presentation, &holder_doc, - &trusted_issuers, + &[&issuer_foo_doc, &issuer_bar_doc], &presentation_validation_options, FailFast::FirstError, ) @@ -517,12 +511,10 @@ mod tests { credential_bar.non_transferable = Some(true); // sign the credential issuer_bar_doc - .sign_data( - &mut credential_bar, - issuer_bar_key.private(), - issuer_bar_doc.default_signing_method().unwrap().id(), - ProofOptions::default(), - ) + .signer(issuer_bar_key.private()) + .options(ProofOptions::default()) + .method(issuer_bar_doc.methods().next().unwrap().id()) + .sign(&mut credential_bar) .unwrap(); // create a presentation where the subject of the first credential is the holder. @@ -530,14 +522,11 @@ mod tests { let mut presentation = build_presentation(&subject_foo_doc, [credential_foo, credential_bar].to_vec()); // sign the presentation using subject_foo's document and private key. - subject_foo_doc - .sign_data( - &mut presentation, - subject_foo_key.private(), - subject_foo_doc.default_signing_method().unwrap().id(), - ProofOptions::new().challenge("some challenge".to_owned()), - ) + .signer(subject_foo_key.private()) + .options(ProofOptions::new().challenge("some challenge".to_owned())) + .method(subject_foo_doc.methods().next().unwrap().id()) + .sign(&mut presentation) .unwrap(); // validate the presentation @@ -552,14 +541,13 @@ mod tests { .presentation_verifier_options(presentation_verifier_options) .subject_holder_relationship(SubjectHolderRelationship::SubjectOnNonTransferable); - let trusted_issuers = [issuer_foo_doc, issuer_bar_doc]; - + let trusted_issuers = &[issuer_foo_doc, issuer_bar_doc]; let holder_doc = subject_foo_doc; let error = PresentationValidator::validate( &presentation, &holder_doc, - &trusted_issuers, + trusted_issuers, &presentation_validation_options, FailFast::FirstError, ) @@ -580,7 +568,7 @@ mod tests { assert!(PresentationValidator::validate( &presentation, &holder_doc, - &trusted_issuers, + trusted_issuers, &options, FailFast::FirstError, ) @@ -591,7 +579,7 @@ mod tests { assert!(PresentationValidator::validate( &presentation, &holder_doc, - &trusted_issuers, + trusted_issuers, &options, FailFast::FirstError, ) @@ -628,14 +616,11 @@ mod tests { .build() .unwrap(); // sign the presentation using subject_foo's document and private key - subject_foo_doc - .sign_data( - &mut presentation, - subject_foo_key.private(), - subject_foo_doc.default_signing_method().unwrap().id(), - ProofOptions::new().challenge("some challenge".to_owned()), - ) + .signer(subject_foo_key.private()) + .options(ProofOptions::new().challenge("some challenge".to_owned())) + .method(subject_foo_doc.methods().next().unwrap().id()) + .sign(&mut presentation) .unwrap(); // validate the presentation @@ -649,8 +634,7 @@ mod tests { .shared_validation_options(credential_validation_options) .presentation_verifier_options(presentation_verifier_options); - let trusted_issuers = [issuer_foo_doc, issuer_bar_doc]; - + let trusted_issuers = &[issuer_foo_doc, issuer_bar_doc]; let holder_document = subject_foo_doc; let CompoundPresentationValidationError { @@ -659,19 +643,19 @@ mod tests { } = PresentationValidator::validate( &presentation, &holder_document, - &trusted_issuers, + trusted_issuers, &presentation_validation_options, FailFast::FirstError, ) .unwrap_err(); - assert!( + assert_eq!( + 1, presentation_validation_errors.len() + credential_errors .values() .map(|error| error.validation_errors.len()) .sum::() - == 1 ); } @@ -706,12 +690,10 @@ mod tests { // sign the presentation using subject_foo's document and private key subject_foo_doc - .sign_data( - &mut presentation, - subject_foo_key.private(), - subject_foo_doc.default_signing_method().unwrap().id(), - ProofOptions::new().challenge("some challenge".to_owned()), - ) + .signer(subject_foo_key.private()) + .options(ProofOptions::new().challenge("some challenge".to_owned())) + .method(subject_foo_doc.methods().next().unwrap().id()) + .sign(&mut presentation) .unwrap(); // validate the presentation @@ -726,8 +708,7 @@ mod tests { .shared_validation_options(credential_validation_options) .presentation_verifier_options(presentation_verifier_options); - let trusted_issuers = [issuer_foo_doc, issuer_bar_doc]; - + let trusted_issuers = &[issuer_foo_doc, issuer_bar_doc]; let holder_doc = subject_foo_doc; let CompoundPresentationValidationError { @@ -736,7 +717,7 @@ mod tests { } = PresentationValidator::validate( &presentation, &holder_doc, - &trusted_issuers, + trusted_issuers, &presentation_validation_options, FailFast::AllErrors, ) diff --git a/identity_iota_client/src/credential/test_utils.rs b/identity_credential/src/validator/test_utils.rs similarity index 63% rename from identity_iota_client/src/credential/test_utils.rs rename to identity_credential/src/validator/test_utils.rs index b568fe8f70..45e70aa44f 100644 --- a/identity_iota_client/src/credential/test_utils.rs +++ b/identity_credential/src/validator/test_utils.rs @@ -1,33 +1,45 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use identity_core::common::Object; use identity_core::common::Timestamp; use identity_core::common::Url; use identity_core::convert::FromJson; use identity_core::crypto::KeyPair; use identity_core::crypto::KeyType; use identity_core::json; -use identity_credential::credential::Credential; -use identity_credential::credential::CredentialBuilder; -use identity_credential::credential::Subject; +use identity_core::utils::BaseEncoding; +use identity_did::did::CoreDID; use identity_did::did::DID; -use identity_iota_core::document::IotaDocument; +use identity_did::document::CoreDocument; +use identity_did::verification::VerificationMethod; -use crate::Result; +use crate::credential::Credential; +use crate::credential::CredentialBuilder; +use crate::credential::Subject; -pub(super) fn generate_document_with_keys() -> (IotaDocument, KeyPair) { +pub(super) fn generate_document_with_keys() -> (CoreDocument, KeyPair) { let keypair: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); - let document: IotaDocument = IotaDocument::new(&keypair).unwrap(); + let did: CoreDID = CoreDID::parse(&format!( + "did:example:{}", + BaseEncoding::encode_base58(keypair.public()) + )) + .unwrap(); + let document: CoreDocument = CoreDocument::builder(Object::new()) + .id(did.clone()) + .verification_method(VerificationMethod::new(did, KeyType::Ed25519, keypair.public(), "#root").unwrap()) + .build() + .unwrap(); (document, keypair) } pub(super) fn generate_credential( - issuer: &IotaDocument, - subjects: &[IotaDocument], + issuer: &CoreDocument, + subjects: &[CoreDocument], issuance_date: Timestamp, expiration_date: Timestamp, ) -> Credential { - let credential_subjects: Result> = subjects + let credential_subjects: Vec = subjects .iter() .map(|subject| { Subject::from_json_value(json!({ @@ -39,7 +51,7 @@ pub(super) fn generate_credential( }, "GPA": "4.0", })) - .map_err(Into::into) + .unwrap() }) .collect(); @@ -48,7 +60,7 @@ pub(super) fn generate_credential( .id(Url::parse("https://example.edu/credentials/3732").unwrap()) .issuer(Url::parse(issuer.id().as_str()).unwrap()) .type_("UniversityDegreeCredential") - .subjects(credential_subjects.unwrap()) + .subjects(credential_subjects) .issuance_date(issuance_date) .expiration_date(expiration_date) .build() @@ -56,7 +68,7 @@ pub(super) fn generate_credential( } // generates a triple: issuer document, issuer's keys, unsigned credential issued by issuer -pub(super) fn credential_setup() -> (IotaDocument, KeyPair, Credential) { +pub(super) fn credential_setup() -> (CoreDocument, KeyPair, Credential) { let (issuer_doc, issuer_key) = generate_document_with_keys(); let (subject_doc, _) = generate_document_with_keys(); let issuance_date = Timestamp::parse("2020-01-01T00:00:00Z").unwrap(); diff --git a/identity_iota_client/src/credential/validation_options.rs b/identity_credential/src/validator/validation_options.rs similarity index 96% rename from identity_iota_client/src/credential/validation_options.rs rename to identity_credential/src/validator/validation_options.rs index c37a4b74d4..34e7416b4f 100644 --- a/identity_iota_client/src/credential/validation_options.rs +++ b/identity_credential/src/validator/validation_options.rs @@ -124,9 +124,8 @@ pub enum FailFast { FirstError, } -/// Options to declare validation criteria for validation methods such as -/// [`PresentationValidator::validate`](super::PresentationValidator::validate()) and -/// [`Resolver::verify_presentation`](crate::tangle::Resolver::verify_presentation()). +/// Criteria for validating a [`Presentation`](crate::presentation::Presentation), such as with +/// [`PresentationValidator::validate`](crate::validator::PresentationValidator::validate()). #[derive(Debug, Default, Clone, Serialize, Deserialize)] #[non_exhaustive] #[serde(rename_all = "camelCase")] diff --git a/identity_credential/src/validator/validator_document.rs b/identity_credential/src/validator/validator_document.rs new file mode 100644 index 0000000000..118466d9bc --- /dev/null +++ b/identity_credential/src/validator/validator_document.rs @@ -0,0 +1,111 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use identity_core::crypto::GetSignature; +use identity_did::did::DID; +use identity_did::document::Document; +use identity_did::revocation::RevocationBitmap; +use identity_did::utils::DIDUrlQuery; +use identity_did::verifiable::VerifierOptions; + +use self::private::Sealed; +use self::private::Verifiable; + +/// Abstraction over DID Documents for validating presentations and credentials. +/// +/// NOTE: this is a sealed trait and not intended to be used externally or implemented manually. +/// A blanket implementation is provided for the [`Document`] trait, which can be implemented +/// instead to be compatible. Any changes to this trait will be considered non-breaking. +pub trait ValidatorDocument: Sealed { + /// Convenience function for casting self to the trait. + /// + /// Equivalent to `self as &dyn ValidatorDocument`. + fn as_validator(&self) -> &dyn ValidatorDocument + where + Self: Sized, + { + self as &dyn ValidatorDocument + } + + /// Returns the string identifier of the DID Document. + fn did_str(&self) -> &str; + + /// Verifies the signature of the provided data against the DID Document. + /// + /// # Errors + /// + /// Fails if an unsupported verification method is used, data + /// serialization fails, or the verification operation fails. + fn verify_data(&self, data: &dyn Verifiable, options: &VerifierOptions) -> identity_did::Result<()>; + + /// Extracts the `RevocationBitmap` from the referenced service in the DID Document. + /// + /// # Errors + /// + /// Fails if the referenced service is not found, or is not a + /// valid `RevocationBitmap2022` service. + #[cfg(feature = "revocation-bitmap")] + fn resolve_revocation_bitmap(&self, query: DIDUrlQuery<'_>) -> identity_did::Result; +} + +mod private { + use super::*; + + pub trait Sealed {} + + impl Sealed for T where T: Document {} + impl Sealed for &dyn ValidatorDocument {} + + /// Object-safe trait workaround to satisfy the trait bounds + /// [`serde::Serialize`] + [`GetSignature`]. + pub trait Verifiable: erased_serde::Serialize + GetSignature {} + + impl Verifiable for T where T: erased_serde::Serialize + GetSignature {} + + impl<'a> serde::Serialize for dyn Verifiable + 'a { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + erased_serde::serialize(self, serializer) + } + } +} + +impl ValidatorDocument for &dyn ValidatorDocument { + fn did_str(&self) -> &str { + (*self).did_str() + } + + fn verify_data(&self, data: &dyn Verifiable, options: &VerifierOptions) -> identity_did::Result<()> { + (*self).verify_data(data, options) + } + + #[cfg(feature = "revocation-bitmap")] + fn resolve_revocation_bitmap(&self, query: DIDUrlQuery<'_>) -> identity_did::Result { + (*self).resolve_revocation_bitmap(query) + } +} + +impl ValidatorDocument for DOC +where + DOC: Document, +{ + fn did_str(&self) -> &str { + self.id().as_str() + } + + fn verify_data(&self, data: &dyn Verifiable, options: &VerifierOptions) -> identity_did::Result<()> { + self.verify_data(data, options).map_err(Into::into) + } + + #[cfg(feature = "revocation-bitmap")] + fn resolve_revocation_bitmap(&self, query: DIDUrlQuery<'_>) -> identity_did::Result { + self + .resolve_service(query) + .ok_or(identity_did::Error::InvalidService( + "revocation bitmap service not found", + )) + .and_then(RevocationBitmap::try_from) + } +} diff --git a/identity_did/src/document/core_document.rs b/identity_did/src/document/core_document.rs index c3f9860cfe..b3a855f147 100644 --- a/identity_did/src/document/core_document.rs +++ b/identity_did/src/document/core_document.rs @@ -25,6 +25,7 @@ use identity_core::crypto::Verifier; use crate::did::CoreDID; use crate::did::DIDUrl; use crate::did::DID; +use crate::document::Document; use crate::document::DocumentBuilder; use crate::error::Error; use crate::error::Result; @@ -672,7 +673,7 @@ where /// serialization fails, or the verification operation fails. pub fn verify_data(&self, data: &X, options: &VerifierOptions) -> Result<()> where - X: Serialize + GetSignature, + X: Serialize + GetSignature + ?Sized, { let signature: &Proof = data.signature().ok_or(Error::InvalidSignature("missing signature"))?; @@ -736,7 +737,7 @@ where /// serialization fails, or the verification operation fails. fn do_verify(method: &VerificationMethod, data: &X) -> Result<()> where - X: Serialize + GetSignature, + X: Serialize + GetSignature + ?Sized, { let public_key: Vec = method.data().try_decode()?; @@ -753,6 +754,114 @@ where } } +impl Document for CoreDocument +where + D: DID + KeyComparable, +{ + type D = D; + type U = U; + type V = V; + + fn id(&self) -> &Self::D { + CoreDocument::id(self) + } + + fn resolve_service<'query, 'me, Q>(&'me self, query: Q) -> Option<&Service> + where + Q: Into>, + { + self.service().query(query.into()) + } + + fn resolve_method<'query, 'me, Q>( + &'me self, + query: Q, + scope: Option, + ) -> Option<&VerificationMethod> + where + Q: Into>, + { + CoreDocument::resolve_method(self, query, scope) + } + + fn verify_data(&self, data: &X, options: &VerifierOptions) -> Result<()> + where + X: Serialize + GetSignature + ?Sized, + { + CoreDocument::verify_data(self, data, options) + } +} + +#[cfg(feature = "revocation-bitmap")] +mod core_document_revocation { + use identity_core::common::KeyComparable; + + use crate::did::DID; + use crate::revocation::RevocationBitmap; + use crate::service::Service; + use crate::utils::DIDUrlQuery; + use crate::utils::Queryable; + use crate::Error; + use crate::Result; + + use super::CoreDocument; + + impl CoreDocument + where + D: DID + KeyComparable, + { + /// If the document has a [`RevocationBitmap`] service identified by `service_query`, + /// revoke all credentials with a `revocationBitmapIndex` in `credential_indices`. + pub fn revoke_credentials<'query, 'me, Q>(&mut self, service_query: Q, credential_indices: &[u32]) -> Result<()> + where + Q: Into>, + { + self.update_revocation_bitmap(service_query, |revocation_bitmap| { + // Revoke all given credential indices. + for credential in credential_indices { + revocation_bitmap.revoke(*credential); + } + }) + } + + /// If the document has a [`RevocationBitmap`] service identified by `service_query`, + /// unrevoke all credentials with a `revocationBitmapIndex` in `credential_indices`. + pub fn unrevoke_credentials<'query, 'me, Q>( + &'me mut self, + service_query: Q, + credential_indices: &[u32], + ) -> Result<()> + where + Q: Into>, + { + self.update_revocation_bitmap(service_query, |revocation_bitmap| { + // Unrevoke all given credential indices. + for credential in credential_indices { + revocation_bitmap.unrevoke(*credential); + } + }) + } + + fn update_revocation_bitmap<'query, 'me, F, Q>(&'me mut self, service_query: Q, f: F) -> Result<()> + where + F: FnOnce(&mut RevocationBitmap), + Q: Into>, + { + let service: &mut Service = self + .service_mut() + .query_mut(service_query) + .ok_or(Error::InvalidService("invalid id - service not found"))?; + + let mut revocation_bitmap: RevocationBitmap = RevocationBitmap::try_from(&*service)?; + f(&mut revocation_bitmap); + + std::mem::swap(service.service_endpoint_mut(), &mut revocation_bitmap.to_endpoint()?); + + Ok(()) + } + } +} + // ============================================================================= // Signature Extensions // ============================================================================= diff --git a/identity_did/src/document/mod.rs b/identity_did/src/document/mod.rs index bcd021e45e..c3de65a652 100644 --- a/identity_did/src/document/mod.rs +++ b/identity_did/src/document/mod.rs @@ -1,12 +1,14 @@ -// Copyright 2020-2021 IOTA Stiftung +// Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 //! Defines the core (implementation agnostic) DID Document type. #![allow(clippy::module_inception)] -mod builder; -mod core_document; - pub use self::builder::DocumentBuilder; pub use self::core_document::CoreDocument; +pub use self::traits::Document; + +mod builder; +mod core_document; +mod traits; diff --git a/identity_did/src/document/traits.rs b/identity_did/src/document/traits.rs new file mode 100644 index 0000000000..8451786ae2 --- /dev/null +++ b/identity_did/src/document/traits.rs @@ -0,0 +1,88 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use serde::Serialize; + +use identity_core::crypto::GetSignature; + +use crate::did::DID; +use crate::service::Service; +use crate::utils::DIDUrlQuery; +use crate::verifiable::VerifierOptions; +use crate::verification::MethodScope; +use crate::verification::VerificationMethod; +use crate::Result; + +// TODO: add sign_data, split sign/verify to separate trait as first step towards +// supporting custom signature schemes. Replace DocumentSigner with trait? +// Add DocumentMut for mutable function returns? +// Blanket impl for &T, &mut T, Box etc.? +/// Common operations for DID Documents. +pub trait Document { + type D: DID; + type U; + type V; + + /// Returns a reference to the `Document` id. + fn id(&self) -> &Self::D; + + /// Returns the first [`Service`] with an `id` property matching the provided `query`, if present. + fn resolve_service<'query, 'me, Q>(&'me self, query: Q) -> Option<&Service> + where + Q: Into>; + + /// Returns the first [`VerificationMethod`] with an `id` property matching the + /// provided `query` and the verification relationship specified by `scope` if present. + fn resolve_method<'query, 'me, Q>( + &'me self, + query: Q, + scope: Option, + ) -> Option<&VerificationMethod> + where + Q: Into>; + + /// Verifies the signature of the provided data. + /// + /// # Errors + /// + /// Fails if an unsupported verification method is used, data + /// serialization fails, or the verification operation fails. + fn verify_data(&self, data: &X, options: &VerifierOptions) -> Result<()> + where + X: Serialize + GetSignature + ?Sized; +} + +impl Document for &DOC { + type D = DOC::D; + type U = DOC::U; + type V = DOC::V; + + fn id(&self) -> &Self::D { + DOC::id(self) + } + + fn resolve_service<'query, 'me, Q>(&'me self, query: Q) -> Option<&Service> + where + Q: Into>, + { + DOC::resolve_service(self, query) + } + + fn resolve_method<'query, 'me, Q>( + &'me self, + query: Q, + scope: Option, + ) -> Option<&VerificationMethod> + where + Q: Into>, + { + DOC::resolve_method(self, query, scope) + } + + fn verify_data(&self, data: &X, options: &VerifierOptions) -> Result<()> + where + X: Serialize + GetSignature + ?Sized, + { + DOC::verify_data(self, data, options) + } +} diff --git a/identity_did/src/revocation/bitmap.rs b/identity_did/src/revocation/bitmap.rs index bffd474066..70045a844a 100644 --- a/identity_did/src/revocation/bitmap.rs +++ b/identity_did/src/revocation/bitmap.rs @@ -141,14 +141,12 @@ impl RevocationBitmap { } } -impl TryFrom<&Service> for RevocationBitmap { +impl TryFrom<&Service> for RevocationBitmap { type Error = Error; - fn try_from(service: &Service) -> Result { + fn try_from(service: &Service) -> Result { if service.type_() != Self::TYPE { - return Err(Error::InvalidService( - "invalid service - expected a `RevocationBitmap2022`", - )); + return Err(Error::InvalidService("invalid type - expected `RevocationBitmap2022`")); } Self::from_endpoint(service.service_endpoint()) diff --git a/identity_iota/src/lib.rs b/identity_iota/src/lib.rs index 7447e12e51..9b79dc78e2 100644 --- a/identity_iota/src/lib.rs +++ b/identity_iota/src/lib.rs @@ -48,6 +48,7 @@ pub mod credential { pub use identity_credential::credential::*; pub use identity_credential::error::*; pub use identity_credential::presentation::*; + pub use identity_credential::validator::*; } pub mod did { @@ -72,7 +73,6 @@ pub mod client { //! IOTA DID Tangle client and validators. pub use identity_iota_client::chain::*; - pub use identity_iota_client::credential::*; pub use identity_iota_client::document::*; pub use identity_iota_client::tangle::*; diff --git a/identity_iota_client/Cargo.toml b/identity_iota_client/Cargo.toml index de4826834b..c69ac99918 100644 --- a/identity_iota_client/Cargo.toml +++ b/identity_iota_client/Cargo.toml @@ -17,7 +17,7 @@ brotli = { version = "3.3", default-features = false, features = ["std"] } form_urlencoded = { version = "1.0" } futures = { version = "0.3" } identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } -identity_credential = { version = "=0.6.0", path = "../identity_credential" } +identity_credential = { version = "=0.6.0", path = "../identity_credential", default-features = false, features = ["validator"] } identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } identity_iota_core = { version = "=0.6.0", path = "../identity_iota_core", default-features = false } itertools = { version = "0.10" } @@ -26,7 +26,6 @@ log = { version = "0.4", default-features = false } num-derive = { version = "0.3", default-features = false } num-traits = { version = "0.2", default-features = false, features = ["std"] } serde = { version = "1.0", default-features = false, features = ["std", "derive"] } -serde_repr = { version = "0.1", default-features = false } strum = { version = "0.24.0", default-features = false, features = ["std", "derive"] } thiserror = { version = "1.0", default-features = false } @@ -46,14 +45,13 @@ default-features = false features = ["blake2b"] [dev-dependencies] -proptest = { version = "1.0.0", default-features = false, features = ["std"] } tokio = { version = "1.17.0", default-features = false, features = ["macros"] } [features] default = ["revocation-bitmap"] # Enables revocation with `RevocationBitmap2022`. -revocation-bitmap = ["identity_iota_core/revocation-bitmap"] +revocation-bitmap = ["identity_iota_core/revocation-bitmap", "identity_credential/revocation-bitmap"] [package.metadata.docs.rs] # To build locally: diff --git a/identity_iota_client/README.md b/identity_iota_client/README.md index 1564148a8e..76519d0801 100644 --- a/identity_iota_client/README.md +++ b/identity_iota_client/README.md @@ -5,8 +5,3 @@ This crate provides interfaces for publishing and resolving DID Documents to and - [`Client`](crate::tangle::Client) - [`Resolver`](crate::tangle::Resolver) - -Convenience methods for validating [Verifiable Credentials](https://wiki.iota.org/identity.rs/concepts/verifiable_credentials/overview) and [Verifiable Presentations](https://wiki.iota.org/identity.rs/concepts/verifiable_credentials/verifiable_presentations) are also provided: - -- [`CredentialValidator`](crate::credential::CredentialValidator) -- [`PresentationValidator`](crate::credential::PresentationValidator) diff --git a/identity_iota_client/src/error.rs b/identity_iota_client/src/error.rs index a393607ff9..4e207a68af 100644 --- a/identity_iota_client/src/error.rs +++ b/identity_iota_client/src/error.rs @@ -34,11 +34,11 @@ pub enum Error { InvalidMessageFlags, /// Caused by a single concern credential or presentation validation method failing. #[error("A validation unit failed")] - IsolatedValidationError(#[from] crate::credential::ValidationError), + IsolatedValidationError(#[from] identity_credential::validator::ValidationError), /// Caused by one or more failures when validating a credential. #[error("credential validation failed")] - CredentialValidationError(#[from] crate::credential::CompoundCredentialValidationError), + CredentialValidationError(#[from] identity_credential::validator::CompoundCredentialValidationError), /// Caused by one or more failures when validating a presentation. #[error("presentation validation failed")] - PresentationValidationError(#[from] crate::credential::CompoundPresentationValidationError), + PresentationValidationError(#[from] identity_credential::validator::CompoundPresentationValidationError), } diff --git a/identity_iota_client/src/lib.rs b/identity_iota_client/src/lib.rs index 6df6a22f3e..c89051d4ba 100644 --- a/identity_iota_client/src/lib.rs +++ b/identity_iota_client/src/lib.rs @@ -4,7 +4,6 @@ #![forbid(unsafe_code)] #![allow(deprecated)] #![doc = include_str!("./../README.md")] -#![allow(clippy::upper_case_acronyms)] #![warn( rust_2018_idioms, unreachable_pub, @@ -21,7 +20,6 @@ pub use self::error::Error; pub use self::error::Result; pub mod chain; -pub mod credential; pub mod document; pub mod tangle; diff --git a/identity_iota_client/src/tangle/resolver.rs b/identity_iota_client/src/tangle/resolver.rs index 275acc8224..db94ebccc5 100644 --- a/identity_iota_client/src/tangle/resolver.rs +++ b/identity_iota_client/src/tangle/resolver.rs @@ -5,19 +5,22 @@ use std::collections::HashMap; use std::collections::HashSet; use std::sync::Arc; -use identity_core::common::Url; +use serde::Serialize; + use identity_credential::credential::Credential; use identity_credential::presentation::Presentation; +use identity_credential::validator::CredentialValidator; +use identity_credential::validator::FailFast; +use identity_credential::validator::PresentationValidationOptions; +use identity_credential::validator::PresentationValidator; +use identity_credential::validator::ValidatorDocument; use identity_iota_core::did::IotaDID; use identity_iota_core::diff::DiffMessage; +use identity_iota_core::document::IotaDocument; use identity_iota_core::tangle::NetworkName; -use serde::Serialize; use crate::chain::ChainHistory; use crate::chain::DocumentHistory; -use crate::credential::FailFast; -use crate::credential::PresentationValidationOptions; -use crate::credential::PresentationValidator; use crate::document::ResolvedIotaDocument; use crate::error::Error; use crate::error::Result; @@ -106,12 +109,7 @@ where &self, credential: &Credential, ) -> Result { - let issuer: IotaDID = IotaDID::parse(credential.issuer.url().as_str()).map_err(|error| { - Error::IsolatedValidationError(crate::credential::ValidationError::SignerUrl { - signer_ctx: crate::credential::SignerContext::Issuer, - source: error.into(), - }) - })?; + let issuer: IotaDID = CredentialValidator::extract_issuer(credential).map_err(Error::IsolatedValidationError)?; self.resolve(&issuer).await } @@ -129,14 +127,8 @@ where let issuers: HashSet = presentation .verifiable_credential .iter() - .map(|credential| IotaDID::parse(credential.issuer.url().as_str())) - .map(|url_result| { - url_result.map_err(|error| { - Error::IsolatedValidationError(crate::credential::ValidationError::SignerUrl { - signer_ctx: crate::credential::SignerContext::Issuer, - source: error.into(), - }) - }) + .map(|credential| { + CredentialValidator::extract_issuer::(credential).map_err(Error::IsolatedValidationError) }) .collect::>()?; @@ -153,15 +145,8 @@ where &self, presentation: &Presentation, ) -> Result { - let holder_url: &Url = presentation.holder.as_ref().ok_or(Error::IsolatedValidationError( - crate::credential::ValidationError::MissingPresentationHolder, - ))?; - let holder: IotaDID = IotaDID::parse(holder_url.as_str()).map_err(|error| { - Error::IsolatedValidationError(crate::credential::ValidationError::SignerUrl { - signer_ctx: crate::credential::SignerContext::Holder, - source: error.into(), - }) - })?; + let holder: IotaDID = + PresentationValidator::extract_holder(presentation).map_err(Error::IsolatedValidationError)?; self.resolve(&holder).await } @@ -186,28 +171,30 @@ where presentation: &Presentation, options: &PresentationValidationOptions, fail_fast: FailFast, - holder: Option<&ResolvedIotaDocument>, - issuers: Option<&[ResolvedIotaDocument]>, + holder: Option<&dyn ValidatorDocument>, + issuers: Option<&[&dyn ValidatorDocument]>, ) -> Result<()> { match (holder, issuers) { (Some(holder), Some(issuers)) => { - PresentationValidator::validate(presentation, holder, issuers, options, fail_fast) + PresentationValidator::validate(presentation, &holder, issuers, options, fail_fast) } (Some(holder), None) => { - let issuers: Vec = self.resolve_presentation_issuers(presentation).await?; - PresentationValidator::validate(presentation, holder, &issuers, options, fail_fast) + let resolved_issuers: Vec = self.resolve_presentation_issuers(presentation).await?; + let issuers: Vec = resolved_issuers.into_iter().map(|resolved| resolved.document).collect(); + PresentationValidator::validate(presentation, &holder, issuers.as_slice(), options, fail_fast) } (None, Some(issuers)) => { let holder: ResolvedIotaDocument = self.resolve_presentation_holder(presentation).await?; - PresentationValidator::validate(presentation, &holder, issuers, options, fail_fast) + PresentationValidator::validate(presentation, &holder.document, issuers, options, fail_fast) } (None, None) => { - let (holder, issuers): (ResolvedIotaDocument, Vec) = futures::future::try_join( + let (holder, resolved_issuers): (ResolvedIotaDocument, Vec) = futures::future::try_join( self.resolve_presentation_holder(presentation), self.resolve_presentation_issuers(presentation), ) .await?; - PresentationValidator::validate(presentation, &holder, &issuers, options, fail_fast) + let issuers: Vec = resolved_issuers.into_iter().map(|resolved| resolved.document).collect(); + PresentationValidator::validate(presentation, &holder.document, &issuers, options, fail_fast) } } .map_err(Into::into) @@ -282,3 +269,322 @@ impl TangleResolve for Resolver { self.resolve(did).await } } + +#[cfg(test)] +mod tests { + use identity_core::common::Duration; + use identity_core::common::Object; + use identity_core::common::Timestamp; + use identity_core::common::Url; + use identity_core::convert::FromJson; + use identity_core::crypto::KeyPair; + use identity_core::crypto::KeyType; + use identity_core::crypto::ProofOptions; + use identity_core::json; + use identity_core::utils::BaseEncoding; + use identity_credential::credential::Credential; + use identity_credential::credential::CredentialBuilder; + use identity_credential::credential::Subject; + use identity_credential::validator::CredentialValidationOptions; + use identity_credential::validator::SubjectHolderRelationship; + use identity_did::did::CoreDID; + use identity_did::did::DID; + use identity_did::document::CoreDocument; + use identity_did::verifiable::VerifierOptions; + use identity_did::verification::VerificationMethod; + use identity_iota_core::document::IotaDocument; + use identity_iota_core::tangle::Network; + + use super::*; + + fn generate_core_document() -> (CoreDocument, KeyPair) { + let keypair: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); + let did: CoreDID = CoreDID::parse(&format!( + "did:example:{}", + BaseEncoding::encode_base58(keypair.public()) + )) + .unwrap(); + let document: CoreDocument = CoreDocument::builder(Object::new()) + .id(did.clone()) + .verification_method(VerificationMethod::new(did, KeyType::Ed25519, keypair.public(), "#root").unwrap()) + .build() + .unwrap(); + (document, keypair) + } + + fn generate_credential(issuer: &str, subject: &str) -> Credential { + let credential_subject: Subject = Subject::from_json_value(json!({ + "id": subject, + "name": "Alice", + "degree": { + "type": "BachelorDegree", + "name": "Bachelor of Science and Arts", + }, + "GPA": "4.0", + })) + .unwrap(); + + // Build credential using subject above and issuer. + CredentialBuilder::default() + .id(Url::parse("https://example.edu/credentials/3732").unwrap()) + .issuer(Url::parse(issuer).unwrap()) + .type_("UniversityDegreeCredential") + .subject(credential_subject) + .issuance_date(Timestamp::now_utc()) + .expiration_date(Timestamp::now_utc().checked_add(Duration::days(1)).unwrap()) + .build() + .unwrap() + } + + fn generate_presentation(holder: &str, credentials: Vec) -> Presentation { + let mut builder = Presentation::builder(Object::new()) + .id(Url::parse("https://example.org/credentials/3732").unwrap()) + .holder(Url::parse(holder).unwrap()); + for credential in credentials { + builder = builder.credential(credential); + } + builder.build().unwrap() + } + + // Convenience struct for setting up tests. + struct MixedTestSetup { + // Issuer of credential_iota. + issuer_iota_doc: IotaDocument, + issuer_iota_key: KeyPair, + credential_iota: Credential, + // Issuer of credential_core. + issuer_core_doc: CoreDocument, + issuer_core_key: KeyPair, + credential_core: Credential, + // Subject of both credentials. + subject_doc: CoreDocument, + subject_key: KeyPair, + } + + impl MixedTestSetup { + // Creates DID Documents and unsigned credentials. + fn new() -> Self { + let (issuer_iota_doc, issuer_iota_key) = { + let keypair: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); + (IotaDocument::new(&keypair).unwrap(), keypair) + }; + let (subject_doc, subject_key) = generate_core_document(); + let credential_iota = generate_credential(issuer_iota_doc.id().as_str(), subject_doc.id().as_str()); + + let (issuer_core_doc, issuer_core_key) = generate_core_document(); + let credential_core = generate_credential(issuer_core_doc.id().as_str(), subject_doc.id().as_str()); + + Self { + issuer_iota_doc, + issuer_iota_key, + subject_doc, + subject_key, + credential_iota, + issuer_core_doc, + issuer_core_key, + credential_core, + } + } + + // Creates DID Documents with signed credentials. + fn new_with_signed_credentials() -> Self { + let mut setup = Self::new(); + let MixedTestSetup { + ref issuer_iota_doc, + ref issuer_iota_key, + ref mut credential_iota, + ref issuer_core_doc, + ref issuer_core_key, + ref mut credential_core, + .. + } = setup; + + issuer_iota_doc + .sign_data( + credential_iota, + issuer_iota_key.private(), + issuer_iota_doc.default_signing_method().unwrap().id(), + ProofOptions::default(), + ) + .unwrap(); + issuer_core_doc + .signer(issuer_core_key.private()) + .options(ProofOptions::default()) + .method(issuer_core_doc.methods().next().unwrap().id()) + .sign(credential_core) + .unwrap(); + setup + } + } + + #[tokio::test] + async fn test_resolver_verify_presentation_mixed() { + let MixedTestSetup { + issuer_iota_doc, + credential_iota, + issuer_core_doc, + credential_core, + subject_doc, + subject_key, + .. + } = MixedTestSetup::new_with_signed_credentials(); + + // Subject signs the presentation. + let mut presentation = + generate_presentation(subject_doc.id().as_str(), [credential_iota, credential_core].to_vec()); + let challenge: String = "475a7984-1bb5-4c4c-a56f-822bccd46441".to_owned(); + subject_doc + .signer(subject_key.private()) + .options(ProofOptions::new().challenge(challenge.clone())) + .method(subject_doc.methods().next().unwrap().id()) + .sign(&mut presentation) + .unwrap(); + + // VALID: resolver supports presentations with issuers from different DID Methods. + let resolver: Resolver = Resolver::>::builder() + .client_builder(Client::builder().network(Network::Devnet).node_sync_disabled()) + .build() + .await + .unwrap(); + assert!(resolver + .verify_presentation( + &presentation, + &PresentationValidationOptions::new() + .presentation_verifier_options(VerifierOptions::new().challenge(challenge)) + .subject_holder_relationship(SubjectHolderRelationship::AlwaysSubject), + FailFast::FirstError, + Some(&subject_doc), + Some(&[issuer_iota_doc.as_validator(), issuer_core_doc.as_validator()]) + ) + .await + .is_ok()); + } + + #[test] + fn test_validate_presentation_mixed() { + let MixedTestSetup { + issuer_iota_doc, + credential_iota, + issuer_core_doc, + credential_core, + subject_doc, + subject_key, + .. + } = MixedTestSetup::new_with_signed_credentials(); + + // Subject signs the presentation. + let mut presentation = + generate_presentation(subject_doc.id().as_str(), [credential_iota, credential_core].to_vec()); + let challenge: String = "475a7984-1bb5-4c4c-a56f-822bccd46440".to_owned(); + subject_doc + .signer(subject_key.private()) + .options(ProofOptions::new().challenge(challenge.clone())) + .method(subject_doc.methods().next().unwrap().id()) + .sign(&mut presentation) + .unwrap(); + + // Validate presentation. + let presentation_validation_options = PresentationValidationOptions::new() + .shared_validation_options( + CredentialValidationOptions::new() + .earliest_expiry_date(Timestamp::now_utc().checked_add(Duration::days(1)).unwrap()) + .latest_issuance_date(Timestamp::now_utc()), + ) + .presentation_verifier_options(VerifierOptions::new().challenge(challenge)) + .subject_holder_relationship(SubjectHolderRelationship::AlwaysSubject); + + // VALID: presentations with issuers from different DID Methods are supported. + assert!(PresentationValidator::validate( + &presentation, + &subject_doc, + &[issuer_iota_doc.as_validator(), issuer_core_doc.as_validator()], + &presentation_validation_options, + FailFast::FirstError, + ) + .is_ok()); + + // INVALID: wrong holder fails. + assert!(PresentationValidator::validate( + &presentation, + &issuer_iota_doc, + &[issuer_iota_doc.as_validator(), issuer_core_doc.as_validator()], + &presentation_validation_options, + FailFast::FirstError, + ) + .is_err()); + assert!(PresentationValidator::validate( + &presentation, + &issuer_core_doc, + &[issuer_iota_doc.as_validator(), issuer_core_doc.as_validator()], + &presentation_validation_options, + FailFast::FirstError, + ) + .is_err()); + + // INVALID: excluding the IOTA DID Document issuer fails. + assert!(PresentationValidator::validate( + &presentation, + &subject_doc, + &[issuer_core_doc.as_validator()], + &presentation_validation_options, + FailFast::FirstError, + ) + .is_err()); + + // INVALID: excluding the core DID Document issuer fails. + assert!(PresentationValidator::validate( + &presentation, + &subject_doc, + &[&issuer_iota_doc], + &presentation_validation_options, + FailFast::FirstError, + ) + .is_err()); + + // INVALID: using the wrong core DID Document fails. + assert!(PresentationValidator::validate( + &presentation, + &subject_doc, + &[issuer_iota_doc.as_validator(), subject_doc.as_validator()], + &presentation_validation_options, + FailFast::FirstError, + ) + .is_err()); + + // INVALID: excluding all issuers fails. + let empty_issuers: &[&dyn ValidatorDocument] = &[]; + assert!(PresentationValidator::validate( + &presentation, + &subject_doc, + empty_issuers, + &presentation_validation_options, + FailFast::FirstError, + ) + .is_err()); + } + + #[test] + fn test_validate_credential_mixed() { + let MixedTestSetup { + issuer_iota_doc, + credential_iota, + issuer_core_doc, + credential_core, + subject_doc, + .. + } = MixedTestSetup::new_with_signed_credentials(); + let options = CredentialValidationOptions::new() + .earliest_expiry_date(Timestamp::now_utc().checked_add(Duration::days(1)).unwrap()) + .latest_issuance_date(Timestamp::now_utc()); + + // VALID: credential validation works for issuers with different DID Methods. + assert!(CredentialValidator::validate(&credential_iota, &issuer_iota_doc, &options, FailFast::FirstError).is_ok()); + assert!(CredentialValidator::validate(&credential_core, &issuer_core_doc, &options, FailFast::FirstError).is_ok()); + + // INVALID: wrong issuer fails. + assert!(CredentialValidator::validate(&credential_iota, &issuer_core_doc, &options, FailFast::FirstError).is_err()); + assert!(CredentialValidator::validate(&credential_iota, &subject_doc, &options, FailFast::FirstError).is_err()); + assert!(CredentialValidator::validate(&credential_core, &issuer_iota_doc, &options, FailFast::FirstError).is_err()); + assert!(CredentialValidator::validate(&credential_core, &subject_doc, &options, FailFast::FirstError).is_err()); + } +} diff --git a/identity_iota_core/src/document/iota_document.rs b/identity_iota_core/src/document/iota_document.rs index 875e98008d..9ed8315efa 100644 --- a/identity_iota_core/src/document/iota_document.rs +++ b/identity_iota_core/src/document/iota_document.rs @@ -5,6 +5,10 @@ use core::fmt; use core::fmt::Debug; use core::fmt::Display; +use serde; +use serde::Deserialize; +use serde::Serialize; + use identity_core::common::Object; use identity_core::common::OneOrSet; use identity_core::common::OrderedSet; @@ -22,6 +26,7 @@ use identity_core::crypto::PublicKey; use identity_core::crypto::SetSignature; use identity_core::crypto::Signer; use identity_did::document::CoreDocument; +use identity_did::document::Document; use identity_did::service::Service; use identity_did::utils::DIDUrlQuery; use identity_did::verifiable::DocumentSigner; @@ -33,9 +38,6 @@ use identity_did::verification::MethodType; use identity_did::verification::MethodUriType; use identity_did::verification::TryMethod; use identity_did::verification::VerificationMethod; -use serde; -use serde::Deserialize; -use serde::Serialize; use crate::did::IotaDID; use crate::did::IotaDIDUrl; @@ -448,7 +450,7 @@ impl IotaDocument { /// serialization fails, or the verification operation fails. pub fn verify_data(&self, data: &X, options: &VerifierOptions) -> Result<()> where - X: Serialize + GetSignature, + X: Serialize + GetSignature + ?Sized, { self.document.verify_data(data, options).map_err(Into::into) } @@ -619,59 +621,79 @@ impl IotaDocument { } } +impl Document for IotaDocument { + type D = IotaDID; + type U = Object; + type V = Object; + + fn id(&self) -> &Self::D { + IotaDocument::id(self) + } + + fn resolve_service<'query, 'me, Q>(&'me self, query: Q) -> Option<&Service> + where + Q: Into>, + { + self.core_document().resolve_service(query) + } + + fn resolve_method<'query, 'me, Q>( + &'me self, + query: Q, + scope: Option, + ) -> Option<&VerificationMethod> + where + Q: Into>, + { + self.core_document().resolve_method(query, scope) + } + + fn verify_data(&self, data: &X, options: &VerifierOptions) -> identity_did::Result<()> + where + X: Serialize + GetSignature + ?Sized, + { + self.core_document().verify_data(data, options) + } +} + #[cfg(feature = "revocation-bitmap")] mod iota_document_revocation { - use super::IotaDocument; - use crate::did::IotaDID; - use crate::did::IotaDIDUrl; + use identity_did::utils::DIDUrlQuery; + use crate::Error; use crate::Result; - use identity_did::revocation::RevocationBitmap; - use identity_did::service::Service; - impl IotaDocument { - /// If the document has a [`RevocationBitmap`] service identified by `fragment`, - /// revoke all credentials with a `revocationBitmapIndex` in `credential_indices`. - pub fn revoke_credentials(&mut self, service_id: &IotaDIDUrl, credential_indices: &[u32]) -> Result<()> { - self.update_revocation_bitmap(service_id, |revocation_bitmap| { - // Revoke all given credential indices. - for credential in credential_indices { - revocation_bitmap.revoke(*credential); - } - }) - } + use super::IotaDocument; - /// If the document has a [`RevocationBitmap`] service identified by `fragment`, - /// unrevoke all credentials with a `revocationBitmapIndex` in `credential_indices`. - pub fn unrevoke_credentials(&mut self, service_id: &IotaDIDUrl, credential_indices: &[u32]) -> Result<()> { - self.update_revocation_bitmap(service_id, |revocation_bitmap| { - // Unrevoke all given credential indices. - for credential in credential_indices { - revocation_bitmap.unrevoke(*credential); - } - }) + impl IotaDocument { + /// If the document has a [`RevocationBitmap`](identity_did::revocation::RevocationBitmap) + /// service identified by `service_query`, revoke all credentials with a + /// `revocationBitmapIndex` in `credential_indices`. + pub fn revoke_credentials<'query, 'me, Q>(&mut self, service_query: Q, credential_indices: &[u32]) -> Result<()> + where + Q: Into>, + { + self + .core_document_mut() + .revoke_credentials(service_query, credential_indices) + .map_err(Error::RevocationError) } - fn update_revocation_bitmap(&mut self, service_id: &IotaDIDUrl, f: F) -> Result<()> + /// If the document has a [`RevocationBitmap`](identity_did::revocation::RevocationBitmap) + /// service with an id by `service_query`, unrevoke all credentials with a + /// `revocationBitmapIndex` in `credential_indices`. + pub fn unrevoke_credentials<'query, 'me, Q>( + &'me mut self, + service_query: Q, + credential_indices: &[u32], + ) -> Result<()> where - F: FnOnce(&mut RevocationBitmap), + Q: Into>, { - let service: &mut Service = self + self .core_document_mut() - .service_mut() - .iter_mut_unchecked() - .find(|service| service.id() == service_id) - .ok_or(Error::RevocationError(identity_did::Error::InvalidService( - "invalid id - service not found", - )))?; - - let mut revocation_bitmap: RevocationBitmap = (&*service).try_into().map_err(Error::RevocationError)?; - - f(&mut revocation_bitmap); - - std::mem::swap(service.service_endpoint_mut(), &mut revocation_bitmap.to_endpoint()?); - - Ok(()) + .unrevoke_credentials(service_query, credential_indices) + .map_err(Error::RevocationError) } } } From c19b7658d7abe877b312e27023b891ec4506844f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Ha=C3=9F?= Date: Mon, 11 Jul 2022 11:18:34 +0200 Subject: [PATCH 18/89] Fix doc links (#925) --- .github/workflows/test-docs-build.yml | 4 +- bindings/wasm/docs/api-reference.md | 69 +++++++++---------- .../account/wasm_account/account_builder.rs | 2 - bindings/wasm/src/crypto/wasm_ed25519.rs | 4 +- bindings/wasm/src/did/wasm_document.rs | 2 +- 5 files changed, 39 insertions(+), 42 deletions(-) diff --git a/.github/workflows/test-docs-build.yml b/.github/workflows/test-docs-build.yml index 4c8078fbcb..57f917f16c 100644 --- a/.github/workflows/test-docs-build.yml +++ b/.github/workflows/test-docs-build.yml @@ -22,7 +22,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Test Build - # TODO Add 'yarn build' as last line once broken links are fixed + working-directory: documentation run: | - cd documentation yarn install --immutable + yarn build diff --git a/bindings/wasm/docs/api-reference.md b/bindings/wasm/docs/api-reference.md index cd49b97317..02103b8c15 100644 --- a/bindings/wasm/docs/api-reference.md +++ b/bindings/wasm/docs/api-reference.md @@ -197,10 +197,10 @@ This variant is the default used if no other variant is specified when construct
FirstError

Return after the first error occurs.

-
KeyType
-
MethodRelationship
+
KeyType
+
## Functions @@ -222,9 +222,9 @@ publishing to the Tangle. **Kind**: global class * [Account](#Account) - * [.createService(options)](#Account+createService) ⇒ Promise.<void> - * [.createMethod(options)](#Account+createMethod) ⇒ Promise.<void> * [.attachMethodRelationships(options)](#Account+attachMethodRelationships) ⇒ Promise.<void> + * [.createMethod(options)](#Account+createMethod) ⇒ Promise.<void> + * [.detachMethodRelationships(options)](#Account+detachMethodRelationships) ⇒ Promise.<void> * [.did()](#Account+did) ⇒ [DID](#DID) * [.autopublish()](#Account+autopublish) ⇒ boolean * [.autosave()](#Account+autosave) ⇒ [AutoSave](#AutoSave) @@ -242,22 +242,25 @@ publishing to the Tangle. * [.unrevokeCredentials(fragment, credentialIndices)](#Account+unrevokeCredentials) ⇒ Promise.<void> * [.encryptData(plaintext, associated_data, encryption_algorithm, cek_algorithm, public_key)](#Account+encryptData) ⇒ [Promise.<EncryptedData>](#EncryptedData) * [.decryptData(data, encryption_algorithm, cek_algorithm, fragment)](#Account+decryptData) ⇒ Promise.<Uint8Array> - * [.detachMethodRelationships(options)](#Account+detachMethodRelationships) ⇒ Promise.<void> * [.deleteService(options)](#Account+deleteService) ⇒ Promise.<void> * [.setAlsoKnownAs(options)](#Account+setAlsoKnownAs) ⇒ Promise.<void> * [.setController(options)](#Account+setController) ⇒ Promise.<void> * [.deleteMethod(options)](#Account+deleteMethod) ⇒ Promise.<void> + * [.createService(options)](#Account+createService) ⇒ Promise.<void> - + -### account.createService(options) ⇒ Promise.<void> -Adds a new Service to the DID Document. +### account.attachMethodRelationships(options) ⇒ Promise.<void> +Attach one or more verification relationships to a method. + +Note: the method must exist and be in the set of verification methods; +it cannot be an embedded method. **Kind**: instance method of [Account](#Account) | Param | Type | | --- | --- | -| options | CreateServiceOptions | +| options | AttachMethodRelationshipOptions | @@ -270,19 +273,16 @@ Adds a new verification method to the DID document. | --- | --- | | options | CreateMethodOptions | - - -### account.attachMethodRelationships(options) ⇒ Promise.<void> -Attach one or more verification relationships to a method. + -Note: the method must exist and be in the set of verification methods; -it cannot be an embedded method. +### account.detachMethodRelationships(options) ⇒ Promise.<void> +Detaches the given relationship from the given method, if the method exists. **Kind**: instance method of [Account](#Account) | Param | Type | | --- | --- | -| options | AttachMethodRelationshipOptions | +| options | DetachMethodRelationshipOptions | @@ -475,17 +475,6 @@ Returns the decrypted text. | cek_algorithm | [CekAlgorithm](#CekAlgorithm) | | fragment | string | - - -### account.detachMethodRelationships(options) ⇒ Promise.<void> -Detaches the given relationship from the given method, if the method exists. - -**Kind**: instance method of [Account](#Account) - -| Param | Type | -| --- | --- | -| options | DetachMethodRelationshipOptions | - ### account.deleteService(options) ⇒ Promise.<void> @@ -530,6 +519,17 @@ Deletes a verification method if the method exists. | --- | --- | | options | DeleteMethodOptions | + + +### account.createService(options) ⇒ Promise.<void> +Adds a new Service to the DID Document. + +**Kind**: instance method of [Account](#Account) + +| Param | Type | +| --- | --- | +| options | CreateServiceOptions | + ## AccountBuilder @@ -581,7 +581,6 @@ The identity is stored locally in the `Storage`. The DID network is automaticall by the [Client](#Client) used to publish it. **Kind**: instance method of [AccountBuilder](#AccountBuilder) -**See**: [IdentitySetup](IdentitySetup) to customize the identity creation. | Param | Type | | --- | --- | @@ -2065,7 +2064,7 @@ on the same document. This is intended for signing updates to a document where a capability invocation method is rotated or replaced entirely. NOTE: does not validate whether the private key of the given `key_pair` corresponds to the -verification method. See [Document.verifyDocument](Document.verifyDocument). +verification method. See [Document.verifyDocument](#Document+verifyDocument). **Kind**: instance method of [Document](#Document) @@ -2622,7 +2621,7 @@ Length in bytes of an Ed25519 signature. ### Ed25519.sign(message, privateKey) ⇒ Uint8Array Computes an EdDSA signature using an Ed25519 private key. -NOTE: this differs from [Document.signData](Document.signData) which uses JCS +NOTE: this differs from [Document.signData](#Document+signData) which uses JCS to canonicalize JSON messages. The private key must be a 32-byte seed in compliance with [RFC 8032](https://datatracker.ietf.org/doc/html/rfc8032#section-3.2). @@ -2640,7 +2639,7 @@ Other implementations often use another format. See [this blog post](https://blo ### Ed25519.verify(message, signature, publicKey) Verifies an EdDSA signature against an Ed25519 public key. -NOTE: this differs from [Document.verifyData](Document.verifyData) which uses JCS +NOTE: this differs from [Document.verifyData](#Document+verifyData) which uses JCS to canonicalize JSON messages. **Kind**: static method of [Ed25519](#Ed25519) @@ -4984,15 +4983,15 @@ Return all errors that occur during validation. ## FirstError Return after the first error occurs. -**Kind**: global variable - - -## KeyType **Kind**: global variable ## MethodRelationship **Kind**: global variable + + +## KeyType +**Kind**: global variable ## start() diff --git a/bindings/wasm/src/account/wasm_account/account_builder.rs b/bindings/wasm/src/account/wasm_account/account_builder.rs index 010ea7694a..3132593985 100644 --- a/bindings/wasm/src/account/wasm_account/account_builder.rs +++ b/bindings/wasm/src/account/wasm_account/account_builder.rs @@ -90,8 +90,6 @@ impl WasmAccountBuilder { /// /// The identity is stored locally in the `Storage`. The DID network is automatically determined /// by the {@link Client} used to publish it. - /// - /// @See {@link IdentitySetup} to customize the identity creation. #[wasm_bindgen(js_name = createIdentity)] pub fn create_identity(&mut self, identity_setup: Option) -> Result { let setup: IdentitySetup = identity_setup.map(IdentitySetup::from).unwrap_or_default(); diff --git a/bindings/wasm/src/crypto/wasm_ed25519.rs b/bindings/wasm/src/crypto/wasm_ed25519.rs index 175a153e1f..0021add624 100644 --- a/bindings/wasm/src/crypto/wasm_ed25519.rs +++ b/bindings/wasm/src/crypto/wasm_ed25519.rs @@ -36,7 +36,7 @@ impl WasmEd25519 { /// Computes an EdDSA signature using an Ed25519 private key. /// - /// NOTE: this differs from {@link Document.signData} which uses JCS + /// NOTE: this differs from [Document.signData](#Document+signData) which uses JCS /// to canonicalize JSON messages. /// /// The private key must be a 32-byte seed in compliance with [RFC 8032](https://datatracker.ietf.org/doc/html/rfc8032#section-3.2). @@ -52,7 +52,7 @@ impl WasmEd25519 { /// Verifies an EdDSA signature against an Ed25519 public key. /// - /// NOTE: this differs from {@link Document.verifyData} which uses JCS + /// NOTE: this differs from [Document.verifyData](#Document+verifyData) which uses JCS /// to canonicalize JSON messages. #[allow(non_snake_case)] #[wasm_bindgen] diff --git a/bindings/wasm/src/did/wasm_document.rs b/bindings/wasm/src/did/wasm_document.rs index 5f95d3e10e..1231781cd6 100644 --- a/bindings/wasm/src/did/wasm_document.rs +++ b/bindings/wasm/src/did/wasm_document.rs @@ -366,7 +366,7 @@ impl WasmDocument { /// capability invocation method is rotated or replaced entirely. /// /// NOTE: does not validate whether the private key of the given `key_pair` corresponds to the - /// verification method. See {@link Document.verifyDocument}. + /// verification method. See [Document.verifyDocument](#Document+verifyDocument). #[wasm_bindgen(js_name = signDocument)] pub fn sign_document( &self, From 6657c1ec37006f8bc2849241755ebbde2cf6f6b5 Mon Sep 17 00:00:00 2001 From: cycraig Date: Mon, 11 Jul 2022 14:02:27 +0200 Subject: [PATCH 19/89] Add Rust documentation check to GitHub Actions (#937) * Add Rust documentation check to GitHub Actions * Break intra-doc link to test CI * Revert "Break intra-doc link to test CI" * Use docsrs build configuration * Correct step name --- .github/workflows/test-docs-build.yml | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-docs-build.yml b/.github/workflows/test-docs-build.yml index 57f917f16c..17a969ab94 100644 --- a/.github/workflows/test-docs-build.yml +++ b/.github/workflows/test-docs-build.yml @@ -12,7 +12,7 @@ on: - epic/* - support/* -concurrency: +concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true @@ -21,8 +21,25 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Test Build + + - name: Test Wiki Build working-directory: documentation run: | yarn install --immutable yarn build + + - name: Install Rust nightly toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + override: true + + - name: Test Rust Documentation + uses: actions-rs/cargo@v1 + env: + RUSTDOCFLAGS: "-D warnings --cfg docsrs" + with: + command: doc + toolchain: nightly + args: --all-features --no-deps --workspace + From 324efebf0136d8579643e028e2eab358497c3c46 Mon Sep 17 00:00:00 2001 From: cycraig Date: Tue, 12 Jul 2022 12:19:07 +0200 Subject: [PATCH 20/89] Stardust DID Method Proof-of-Concept (#940) * Stardust Alias Output DID Method proof-of-concept * Add helper functions * Add resolution proof-of-concept, use state metadata instead * Simplify builder usage * Use state index as sentinel value * Add TODO * Rename identity-stardust to identity_stardust * Update to latest iota-client commit, remove unused dependencies * Fix toml formatting * Add identity_stardust to GitHub Actions CI * Fix build-and-test-stardust run condition --- .github/workflows/build-and-test.yml | 53 +++++- .github/workflows/clippy.yml | 9 +- .github/workflows/format.yml | 6 + .github/workflows/test-docs-build.yml | 8 + identity_stardust/Cargo.toml | 40 +++++ identity_stardust/README.md | 5 + identity_stardust/examples/create_did.rs | 177 +++++++++++++++++++++ identity_stardust/src/error.rs | 21 +++ identity_stardust/src/lib.rs | 13 ++ identity_stardust/src/stardust_document.rs | 172 ++++++++++++++++++++ 10 files changed, 501 insertions(+), 3 deletions(-) create mode 100644 identity_stardust/Cargo.toml create mode 100644 identity_stardust/README.md create mode 100644 identity_stardust/examples/create_did.rs create mode 100644 identity_stardust/src/error.rs create mode 100644 identity_stardust/src/lib.rs create mode 100644 identity_stardust/src/stardust_document.rs diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 1632cff2ab..5e9f6034ff 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -6,7 +6,7 @@ on: - main - dev pull_request: - types: [opened, synchronize, reopened, ready_for_review] + types: [ opened, synchronize, reopened, ready_for_review ] branches: - main - dev @@ -63,7 +63,7 @@ jobs: build-and-test: runs-on: ${{ matrix.os }} - needs: [check-for-run-condition, check-for-modification] + needs: [ check-for-run-condition, check-for-modification ] if: ${{ needs.check-for-run-condition.outputs.should-run == 'true' && needs.check-for-modification.outputs.core-modified == 'true' }} strategy: fail-fast: false @@ -123,6 +123,55 @@ jobs: with: os: ${{matrix.os}} + build-and-test-stardust: + needs: [ check-for-run-condition, check-for-modification ] + if: ${{ needs.check-for-run-condition.outputs.should-run == 'true' && needs.check-for-modification.outputs.core-modified == 'true' }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + os: [ ubuntu-latest ] + include: + - os: ubuntu-latest + sccache-path: /home/runner/.cache/sccache + env: + SCCACHE_DIR: ${{ matrix.sccache-path }} + RUSTC_WRAPPER: sccache + + steps: + - uses: actions/checkout@v2 + + - name: Setup Rust and cache + uses: './.github/actions/rust/rust-setup' + with: + os: ${{ runner.os }} + job: ${{ github.job }} + target-cache-enabled: false + sccache-enabled: true + sccache-path: ${{ matrix.sccache-path }} + + - name: Setup sccache + uses: './.github/actions/rust/sccache/setup-sccache' + with: + os: ${{matrix.os}} + + - name: Build Stardust + uses: actions-rs/cargo@v1 + with: + command: build + args: --manifest-path ./identity_stardust/Cargo.toml --workspace --tests --examples --release + + - name: Run Stardust tests + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path ./identity_stardust/Cargo.toml --all --all-features --release + + - name: Stop sccache + uses: './.github/actions/rust/sccache/stop-sccache' + with: + os: ${{matrix.os}} + build-and-test-libjose: needs: check-for-run-condition if: ${{ needs.check-for-run-condition.outputs.should-run == 'true' }} diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 4f0d1e2e3a..ce08221e1a 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -43,7 +43,14 @@ jobs: args: --all-targets --all-features -- -D warnings name: core - - name: wasm clippy check + - name: Stardust clippy check + uses: actions-rs/clippy-check@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + args: --manifest-path ./identity_stardust/Cargo.toml --all-targets --all-features -- -D warnings + name: stardust + + - name: Wasm clippy check uses: actions-rs/clippy-check@v1 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index 9e8894b595..ae910b33a3 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -41,6 +41,12 @@ jobs: command: fmt args: --all -- --check + - name: Stardust fmt check + uses: actions-rs/cargo@v1 + with: + command: fmt + args: --manifest-path ./identity_stardust/Cargo.toml --all -- --check + - name: wasm fmt check uses: actions-rs/cargo@v1 with: diff --git a/.github/workflows/test-docs-build.yml b/.github/workflows/test-docs-build.yml index 17a969ab94..a93ae0d687 100644 --- a/.github/workflows/test-docs-build.yml +++ b/.github/workflows/test-docs-build.yml @@ -43,3 +43,11 @@ jobs: toolchain: nightly args: --all-features --no-deps --workspace + - name: Test Stardust Rust Documentation + uses: actions-rs/cargo@v1 + env: + RUSTDOCFLAGS: "-D warnings --cfg docsrs" + with: + command: doc + toolchain: nightly + args: --manifest-path ./identity_stardust/Cargo.toml --all-features --no-deps --workspace diff --git a/identity_stardust/Cargo.toml b/identity_stardust/Cargo.toml new file mode 100644 index 0000000000..212548aa76 --- /dev/null +++ b/identity_stardust/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "identity_stardust" +version = "0.6.0" +authors = ["IOTA Stiftung"] +edition = "2021" +homepage = "https://www.iota.org" +keywords = ["iota", "tangle", "stardust", "identity"] +license = "Apache-2.0" +readme = "../README.md" +repository = "https://github.com/iotaledger/identity.rs" +description = "An IOTA Ledger integration for the identity.rs library." + +[workspace] + +[dependencies] +identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } +identity_credential = { version = "=0.6.0", path = "../identity_credential", default-features = false } +identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } +lazy_static = { version = "1.4", default-features = false } +serde = { version = "1.0", default-features = false, features = ["std", "derive"] } +strum = { version = "0.21", features = ["derive"] } +thiserror = { version = "1.0", default-features = false } + +[dependencies.iota-client] +git = "https://github.com/iotaledger/iota.rs" +rev = "4e7db070a05321c4bd7579acdcc74436865235c0" # develop branch, 2022-07-11 +features = ["tls"] +default-features = false + +[dev-dependencies] +anyhow = { version = "1.0.57" } +iota-crypto = { version = "0.11.0", default-features = false, features = ["bip39", "bip39-en"] } +proptest = { version = "1.0.0", default-features = false, features = ["std"] } +tokio = { version = "1.17.0", default-features = false, features = ["macros"] } + +[package.metadata.docs.rs] +# To build locally: +# RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features --no-deps --workspace --open +all-features = true +rustdoc-args = ["--cfg", "docsrs"] diff --git a/identity_stardust/README.md b/identity_stardust/README.md new file mode 100644 index 0000000000..6363517c21 --- /dev/null +++ b/identity_stardust/README.md @@ -0,0 +1,5 @@ +# IOTA Stardust Identity Library + +This is a work-in-progress intended to replace the `did:iota` DID Method. + +`cargo run --example create_did` diff --git a/identity_stardust/examples/create_did.rs b/identity_stardust/examples/create_did.rs new file mode 100644 index 0000000000..4c4b978d16 --- /dev/null +++ b/identity_stardust/examples/create_did.rs @@ -0,0 +1,177 @@ +use identity_core::convert::ToJson; +use iota_client::bee_block::output::feature::IssuerFeature; +use iota_client::bee_block::output::feature::MetadataFeature; +use iota_client::bee_block::output::feature::SenderFeature; +use iota_client::bee_block::output::unlock_condition::GovernorAddressUnlockCondition; +use iota_client::bee_block::output::unlock_condition::StateControllerAddressUnlockCondition; +use iota_client::bee_block::output::unlock_condition::UnlockCondition; +use iota_client::bee_block::output::AliasId; +use iota_client::bee_block::output::AliasOutputBuilder; +use iota_client::bee_block::output::ByteCostConfig; +use iota_client::bee_block::output::Feature; +use iota_client::bee_block::output::Output; +use iota_client::constants::SHIMMER_TESTNET_BECH32_HRP; +use iota_client::secret::mnemonic::MnemonicSecretManager; +use iota_client::secret::SecretManager; +use iota_client::Client; + +use identity_stardust::StardustDocument; + +// PROBLEMS SO FAR: +// 1) Alias Id is inferred from the block, so we have to use a placeholder DID for creation. +// 2) Cannot get an Output Id back from an Alias Id (hash of Output Id), need to use Indexer API. +// 3) The Output response from the Indexer is an Output, not a Block, so cannot infer Alias ID from it (fine since we +// use the ID to retrieve the Output in the first place). The OutputDto conversion is annoying too. +// 4) The pieces needed to publish an update are fragmented (Output ID for input, amount, document), bit annoying to +// reconstruct. Use a holder struct like Holder { AliasOutput, StardustDocument } with convenience functions? +// 5) Inferred fields such as the controller and governor need to reflect in the (JSON) Document but excluded from the +// StardustDocument serialization when published. Handle with a separate `pack` function like before? + +/// Demonstrate how to embed a DID Document in an Alias Output. +/// +/// iota.rs alias example: +/// https://github.com/iotaledger/iota.rs/blob/f945ccf326829a418334942ae9cf53b8fab3dbe5/examples/outputs/alias.rs +/// +/// iota.js mint-nft example: +/// https://github.com/iotaledger/iota.js/blob/79a71d3a2ad03be5bd6148689d083947f3b98476/packages/iota/examples/mint-nft/src/index.ts +#[tokio::main] +async fn main() -> anyhow::Result<()> { + // let endpoint = "http://localhost:14265"; + let endpoint = "https://api.alphanet.iotaledger.net"; + let faucet_manual = "https://faucet.alphanet.iotaledger.net"; + + // =========================================================================== + // Step 1: Create or load your wallet. + // =========================================================================== + + // let keypair = identity_core::crypto::KeyPair::new(identity_core::crypto::KeyType::Ed25519).unwrap(); + // println!("PrivateKey: {}", keypair.private().to_string()); + // let mnemonic = + // iota_client::crypto::keys::bip39::wordlist::encode(keypair.private().as_ref(),&bip39::wordlist::ENGLISH).unwrap(); + + // NOTE: this is just a randomly generated mnemonic, REMOVE THIS, never actually commit your seed or mnemonic. + let mnemonic = "veteran provide abstract express quick another fee dragon trend extend cotton tail dog truly angle napkin lunch dinosaur shrimp odor gain bag media mountain"; + println!("Mnemonic: {}", mnemonic); + let secret_manager = SecretManager::Mnemonic(MnemonicSecretManager::try_from_mnemonic(mnemonic)?); + + // Create a client instance. + let client = Client::builder() + .with_node(endpoint)? + .with_node_sync_disabled() + .finish() + .await?; + + let address = client.get_addresses(&secret_manager).with_range(0..1).get_raw().await?[0]; + let address_bech32 = address.to_bech32(SHIMMER_TESTNET_BECH32_HRP); + println!("Wallet address: {address_bech32}"); + + println!("INTERACTION REQUIRED: request faucet funds to the above wallet from {faucet_manual}"); + // let faucet_auto = format!("{endpoint}/api/plugins/faucet/v1/enqueue"); + // iota_client::request_funds_from_faucet(&faucet_auto, &address_bech32).await?; + // tokio::time::sleep(std::time::Duration::from_secs(15)).await; + + // =========================================================================== + // Step 2: Create and publish a DID Document in an Alias Output. + // =========================================================================== + + // Create an empty DID Document. + // All new Stardust DID Documents initially use a placeholder DID, + // "did:stardust:0x00000000000000000000000000000000". + let document: StardustDocument = StardustDocument::new(); + println!("DID Document {:#}", document); + + // Create a new Alias Output with the DID Document as state metadata. + let byte_cost_config: ByteCostConfig = client.get_byte_cost_config().await?; + let alias_output: Output = AliasOutputBuilder::new_with_minimum_storage_deposit(byte_cost_config, AliasId::null())? + .with_state_index(0) + .with_foundry_counter(0) + .with_state_metadata(document.to_json_vec()?) + .add_feature(Feature::Sender(SenderFeature::new(address))) + .add_feature(Feature::Metadata(MetadataFeature::new(vec![1, 2, 3])?)) + .add_immutable_feature(Feature::Issuer(IssuerFeature::new(address))) + .add_unlock_condition(UnlockCondition::StateControllerAddress( + StateControllerAddressUnlockCondition::new(address), + )) + .add_unlock_condition(UnlockCondition::GovernorAddress(GovernorAddressUnlockCondition::new( + address, + ))) + .finish_output()?; + println!("Deposit amount: {}", alias_output.amount()); + + // Publish to the Tangle ledger. + let block = client + .block() + .with_secret_manager(&secret_manager) + .with_outputs(vec![alias_output])? + .finish() + .await?; + println!( + "Transaction with new alias output sent: {endpoint}/api/v2/blocks/{}", + block.id() + ); + let _ = client.retry_until_included(&block.id(), None, None).await?; + + // Infer DID from Alias Output block. + let did = StardustDocument::did_from_block(&block)?; + println!("DID: {did}"); + + // =========================================================================== + // Step 3: Resolve a DID Document. + // =========================================================================== + // iota.rs indexer example: + // https://github.com/iotaledger/iota.rs/blob/f945ccf326829a418334942ae9cf53b8fab3dbe5/examples/indexer.rs + + // Extract Alias ID from DID. + let alias_id: AliasId = StardustDocument::did_to_alias_id(&did)?; + println!("Alias ID: {alias_id}"); + + // Query Indexer INX Plugin for the Output of the Alias ID. + let output_id = client.alias_output_id(alias_id).await?; + println!("Output ID: {output_id}"); + let response = client.get_output(&output_id).await?; + let output = Output::try_from(&response.output)?; + println!("Output: {output:?}"); + + // The resolved DID Document replaces the placeholder DID with the correct one. + let resolved_document = StardustDocument::deserialize_from_output(&alias_id, &output)?; + println!("Resolved Document: {resolved_document:#}"); + + let alias_output = match output { + Output::Alias(output) => Ok(output), + _ => Err(anyhow::anyhow!("not an alias output")), + }?; + + // =========================================================================== + // Step 4: Publish an updated Alias ID. (optional) + // =========================================================================== + // TODO: we could always publish twice on creation to populate the DID (could fail), + // or just infer the DID during resolution (safer). + + // Update the Alias Output to contain an explicit ID and DID. + let updated_alias_output = AliasOutputBuilder::from(&alias_output) // Not adding any content, previous amount will cover the deposit. + // Set the explicit Alias ID. + .with_alias_id(alias_id) + // Update the DID Document content to replace the placeholder DID. + .with_state_metadata(resolved_document.to_json_vec()?) + // State controller updates increment the state index. + .with_state_index(alias_output.state_index() + 1) + .finish_output()?; + + println!("Updated output: {updated_alias_output:?}"); + + let block = client + .block() + .with_secret_manager(&secret_manager) + .with_input(output_id.into())? + .with_outputs(vec![updated_alias_output])? + .finish() + .await?; + + println!( + "Transaction with alias id set sent: {endpoint}/api/v2/blocks/{}", + block.id() + ); + let _ = client.retry_until_included(&block.id(), None, None).await?; + + Ok(()) +} diff --git a/identity_stardust/src/error.rs b/identity_stardust/src/error.rs new file mode 100644 index 0000000000..0561931837 --- /dev/null +++ b/identity_stardust/src/error.rs @@ -0,0 +1,21 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +pub type Result = core::result::Result; + +// TODO: replace all variants with specific errors? +#[derive(Debug, thiserror::Error, strum::IntoStaticStr)] +pub enum Error { + #[error("{0}")] + CoreError(#[from] identity_core::Error), + #[error("{0}")] + CredError(#[from] identity_credential::Error), + #[error("{0}")] + InvalidDID(#[from] identity_did::did::DIDError), + #[error("{0}")] + InvalidDoc(#[from] identity_did::Error), + #[error("{0}")] + ClientError(#[from] iota_client::error::Error), + #[error("{0}")] + BeeError(#[from] iota_client::bee_block::Error), +} diff --git a/identity_stardust/src/lib.rs b/identity_stardust/src/lib.rs new file mode 100644 index 0000000000..50a7bc1c99 --- /dev/null +++ b/identity_stardust/src/lib.rs @@ -0,0 +1,13 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +#![forbid(unsafe_code)] +#![allow(clippy::upper_case_acronyms)] + +pub use self::error::Error; +pub use self::error::Result; + +pub use stardust_document::StardustDocument; + +mod error; +mod stardust_document; diff --git a/identity_stardust/src/stardust_document.rs b/identity_stardust/src/stardust_document.rs new file mode 100644 index 0000000000..35ff25dc6e --- /dev/null +++ b/identity_stardust/src/stardust_document.rs @@ -0,0 +1,172 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use core::fmt::Debug; +use core::fmt::Display; +use core::fmt::Formatter; +use core::fmt::Result as FmtResult; +use std::str::FromStr; + +use identity_core::common::Object; +use identity_core::convert::FmtJson; +use identity_core::convert::FromJson; +use identity_core::utils::Base; +use identity_core::utils::BaseEncoding; +use identity_did::did::CoreDID; +use identity_did::did::DID; +use identity_did::document::CoreDocument; +use iota_client::bee_block::output::AliasId; +use iota_client::bee_block::output::Output; +use iota_client::bee_block::output::OutputId; +use iota_client::bee_block::payload::transaction::TransactionEssence; +use iota_client::bee_block::payload::Payload; +use iota_client::bee_block::Block; +use lazy_static::lazy_static; +use serde::Deserialize; +use serde::Serialize; + +use crate::error::Result; + +/// An IOTA DID document resolved from the Tangle. Represents an integration chain message possibly +/// merged with one or more diff messages. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct StardustDocument(CoreDocument); + +// Tag is 64-bytes long, matching the hex-encoding of the Alias ID (without 0x prefix). +// TODO: should we just keep the 0x prefix in the tag? Other DID methods like did:ethr do... +lazy_static! { + static ref PLACEHOLDER_DID: CoreDID = + CoreDID::parse("did:stardust:0000000000000000000000000000000000000000000000000000000000000000").unwrap(); +} + +impl StardustDocument { + /// Constructs an empty DID Document with a [`StardustDocument::placeholder_did`] identifier. + pub fn new() -> StardustDocument { + Self( + // PANIC: constructing an empty DID Document is infallible, caught by tests otherwise. + CoreDocument::builder(Object::default()) + .id(Self::placeholder_did().clone()) + .build() + .expect("empty StardustDocument constructor failed"), + ) + } + + /// Returns the placeholder DID of newly constructed DID Documents, + /// `"did:stardust:0000000000000000000000000000000000000000000000000000000000000000"`. + // TODO: generalise to take network name? + pub fn placeholder_did() -> &'static CoreDID { + &PLACEHOLDER_DID + } + + /// Constructs a DID from an Alias ID. + /// + /// Uses the hex-encoding of the Alias ID as the DID tag. + pub fn alias_id_to_did(id: &AliasId) -> Result { + // Manually encode to hex to avoid 0x prefix. + let hex: String = BaseEncoding::encode(id.as_slice(), Base::Base16Lower); + CoreDID::parse(format!("did:stardust:{hex}")).map_err(Into::into) + } + + pub fn did_to_alias_id(did: &CoreDID) -> Result { + // TODO: just use 0x in the tag as well? + // Prepend 0x manually. + AliasId::from_str(&format!("0x{}", did.method_id())).map_err(Into::into) + } + + // TODO: can hopefully remove if the publishing logic is wrapped. + pub fn did_from_block(block: &Block) -> Result { + let id: AliasId = AliasId::from(get_alias_output_id_from_payload(block.payload().unwrap())); + Self::alias_id_to_did(&id) + } + + fn parse_block(block: &Block) -> (AliasId, &[u8], bool) { + match block.payload().unwrap() { + Payload::Transaction(tx_payload) => { + let TransactionEssence::Regular(regular) = tx_payload.essence(); + for (index, output) in regular.outputs().iter().enumerate() { + if let Output::Alias(alias_output) = output { + let alias_id = alias_output + .alias_id() + .or_from_output_id(OutputId::new(tx_payload.id(), index.try_into().unwrap()).unwrap()); + let document = alias_output.state_metadata(); + let first = alias_output.state_index() == 0; + return (alias_id, document, first); + } + } + panic!("No alias output in transaction essence") + } + _ => panic!("No tx payload"), + }; + } + + /// Deserializes a JSON-encoded `StardustDocument` from an Alias Output block. + /// + /// NOTE: [`AliasId`] is required since it cannot be inferred from the [`Output`] alone + /// for the first time an Alias Output is published, the transaction payload is required. + pub fn deserialize_from_output(alias_id: &AliasId, output: &Output) -> Result { + let (document, first): (&[u8], bool) = match output { + Output::Alias(alias_output) => (alias_output.state_metadata(), alias_output.alias_id().is_null()), + _ => panic!("not an alias output"), + }; + Self::deserialize_inner(alias_id, document, first) + } + + /// Deserializes a JSON-encoded `StardustDocument` from an Alias Output block. + pub fn deserialize_from_block(block: &Block) -> Result { + let (alias_id, document, first) = Self::parse_block(block); + Self::deserialize_inner(&alias_id, document, first) + } + + pub fn deserialize_inner(alias_id: &AliasId, document: &[u8], first: bool) -> Result { + let did: CoreDID = Self::alias_id_to_did(alias_id)?; + + // Replace the placeholder DID in the Document content for the first Alias Output block. + // TODO: maybe _always_ do this replacement in case developers forget to replace it? + if first { + let json = String::from_utf8(document.to_vec()).unwrap(); + let replaced = json.replace(Self::placeholder_did().as_str(), did.as_str()); + StardustDocument::from_json(&replaced).map_err(Into::into) + } else { + StardustDocument::from_json_slice(document).map_err(Into::into) + } + } +} + +impl Default for StardustDocument { + fn default() -> Self { + Self::new() + } +} + +impl Display for StardustDocument { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + self.fmt_json(f) + } +} + +// helper function to get the output id for the first alias output +fn get_alias_output_id_from_payload(payload: &Payload) -> OutputId { + match payload { + Payload::Transaction(tx_payload) => { + let TransactionEssence::Regular(regular) = tx_payload.essence(); + for (index, output) in regular.outputs().iter().enumerate() { + if let Output::Alias(_alias_output) = output { + return OutputId::new(tx_payload.id(), index.try_into().unwrap()).unwrap(); + } + } + panic!("No alias output in transaction essence") + } + _ => panic!("No tx payload"), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_new() { + let document: StardustDocument = StardustDocument::new(); + assert_eq!(document.0.id(), StardustDocument::placeholder_did()); + } +} From 7aae8b6bf1c27b8567d2c26a30a9d43edc7172a5 Mon Sep 17 00:00:00 2001 From: "Oliver E. Anderson" Date: Tue, 12 Jul 2022 14:54:01 +0200 Subject: [PATCH 21/89] Specify deviation from the JcsEd25519Signature2020 specification. (#941) --- .../docs/specs/did/iota_did_method_spec.md | 4 +++- identity_core/src/crypto/proof/jcs_ed25519.rs | 13 +++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/documentation/docs/specs/did/iota_did_method_spec.md b/documentation/docs/specs/did/iota_did_method_spec.md index 820b52f906..e44287eb92 100644 --- a/documentation/docs/specs/did/iota_did_method_spec.md +++ b/documentation/docs/specs/did/iota_did_method_spec.md @@ -192,7 +192,9 @@ The proof object is an embedded proof that contains all information to be verifi The proof object MUST include a `type` property. This property references a verification method type's signature algorithm, as standardized in the [DID spec registries](https://www.w3.org/TR/did-spec-registries/#verification-method-types) or standardized via the method specification. The IOTA Identity implementation currently supports: -- `JcsEd25519Signature2020` +- `JcsEd25519Signature2020` + +It must be emphasized that the IOTA Identity implementation of `JcsEd25519Signature2020` follows the [official specification](https://identity.foundation/JcsEd25519Signature2020/) with the single exception that the SHA-256 pre-hash of the input, the third step of the [proof generation algorithm](https://identity.foundation/JcsEd25519Signature2020/#ProofGeneration), is skipped. See the following two GitHub issues [#22](https://github.com/decentralized-identity/JcsEd25519Signature2020/issues/22) and [#26](https://github.com/decentralized-identity/JcsEd25519Signature2020/issues/26) for more context on why this deviation is deemed necessary. **Verification Method** diff --git a/identity_core/src/crypto/proof/jcs_ed25519.rs b/identity_core/src/crypto/proof/jcs_ed25519.rs index 272fb39dac..ef8a53b98b 100644 --- a/identity_core/src/crypto/proof/jcs_ed25519.rs +++ b/identity_core/src/crypto/proof/jcs_ed25519.rs @@ -20,11 +20,20 @@ use crate::utils::BaseEncoding; // TODO: Marker trait for Ed25519 implementations (?) /// An implementation of the [JCS Ed25519 Signature 2020][SPEC1] signature suite -/// for [Linked Data Proofs][SPEC2]. +/// for [Linked Data Proofs][SPEC2] modified to pass the official test vectors. /// /// Users should use the [`Sign`]/[`Verify`] traits to access /// this implementation. /// +/// ## Deviation from the [JCS Ed25519 Signature 2020 specification][SPEC1] +/// This implementation follows the specification with the single exception that the SHA-256 pre-hash of the input, the third step of the [proof generation algorithm](https://identity.foundation/JcsEd25519Signature2020/#ProofGeneration), is skipped. +/// +/// We deviate from the specification to satisfy the [official test vectors](https://github.com/decentralized-identity/JcsEd25519Signature2020/tree/master/signature-suite-impls/test-vectors), as well as to achieve compatibility with the official reference implementations for [Go](https://github.com/decentralized-identity/JcsEd25519Signature2020/tree/master/signature-suite-impls/golang) +/// and [Java](https://github.com/decentralized-identity/JcsEd25519Signature2020/tree/master/signature-suite-impls/java). +/// +/// See [this GitHub issue](https://github.com/decentralized-identity/JcsEd25519Signature2020/issues/22) for further discussions. +/// +/// /// [SPEC1]: https://identity.foundation/JcsEd25519Signature2020/ /// [SPEC2]: https://w3c-ccg.github.io/ld-proofs/ pub struct JcsEd25519(PhantomData); @@ -105,7 +114,7 @@ mod tests { } const TV_1_BYTES: &[u8] = include_bytes!("../../../tests/fixtures/jcs_ed25519/test_vector_1.json"); - const TV_2_BYTES: &[u8] = include_bytes!("../../../tests/fixtures/jcs_ed25519/test_vector_1.json"); + const TV_2_BYTES: &[u8] = include_bytes!("../../../tests/fixtures/jcs_ed25519/test_vector_2.json"); const TEST_VECTOR_BYTES: [&[u8]; 2] = [TV_1_BYTES, TV_2_BYTES]; for tv_bytes in TEST_VECTOR_BYTES { From fd86d58e3e8b9ad4fdaebcbfcb68c4c72c046f23 Mon Sep 17 00:00:00 2001 From: cycraig Date: Wed, 13 Jul 2022 10:49:32 +0200 Subject: [PATCH 22/89] Change `Service` `type` field to allow sets (#944) * Change Service type to OneOrSet * Update Account to allow Service type sets * Update Wasm bindings --- bindings/wasm/docs/api-reference.md | 80 +++++++++---------- .../wasm_account/update/create_service.rs | 10 +-- bindings/wasm/src/did/wasm_document.rs | 5 +- bindings/wasm/src/did/wasm_service.rs | 29 ++++--- bindings/wasm/tests/account.ts | 38 ++++++++- bindings/wasm/tests/document.ts | 50 ++++++++++++ identity_account/src/tests/updates.rs | 4 +- identity_account/src/updates/update.rs | 21 +++-- identity_core/src/common/key_comparable.rs | 34 ++++++++ identity_core/src/common/one_or_set.rs | 16 ++-- identity_core/src/common/ordered_set.rs | 18 +---- .../src/validator/credential_validator.rs | 5 +- .../src/validator/validator_document.rs | 17 +++- identity_did/src/diff/diff_service.rs | 61 ++++++++++---- identity_did/src/revocation/bitmap.rs | 2 +- identity_did/src/service/builder.rs | 31 ++++--- identity_did/src/service/service.rs | 62 +++++++++++--- 17 files changed, 344 insertions(+), 139 deletions(-) create mode 100644 bindings/wasm/tests/document.ts diff --git a/bindings/wasm/docs/api-reference.md b/bindings/wasm/docs/api-reference.md index 02103b8c15..4d351c7796 100644 --- a/bindings/wasm/docs/api-reference.md +++ b/bindings/wasm/docs/api-reference.md @@ -197,10 +197,10 @@ This variant is the default used if no other variant is specified when construct
FirstError

Return after the first error occurs.

-
MethodRelationship
-
KeyType
+
MethodRelationship
+
## Functions @@ -222,9 +222,8 @@ publishing to the Tangle. **Kind**: global class * [Account](#Account) - * [.attachMethodRelationships(options)](#Account+attachMethodRelationships) ⇒ Promise.<void> + * [.createService(options)](#Account+createService) ⇒ Promise.<void> * [.createMethod(options)](#Account+createMethod) ⇒ Promise.<void> - * [.detachMethodRelationships(options)](#Account+detachMethodRelationships) ⇒ Promise.<void> * [.did()](#Account+did) ⇒ [DID](#DID) * [.autopublish()](#Account+autopublish) ⇒ boolean * [.autosave()](#Account+autosave) ⇒ [AutoSave](#AutoSave) @@ -242,25 +241,23 @@ publishing to the Tangle. * [.unrevokeCredentials(fragment, credentialIndices)](#Account+unrevokeCredentials) ⇒ Promise.<void> * [.encryptData(plaintext, associated_data, encryption_algorithm, cek_algorithm, public_key)](#Account+encryptData) ⇒ [Promise.<EncryptedData>](#EncryptedData) * [.decryptData(data, encryption_algorithm, cek_algorithm, fragment)](#Account+decryptData) ⇒ Promise.<Uint8Array> + * [.attachMethodRelationships(options)](#Account+attachMethodRelationships) ⇒ Promise.<void> + * [.detachMethodRelationships(options)](#Account+detachMethodRelationships) ⇒ Promise.<void> * [.deleteService(options)](#Account+deleteService) ⇒ Promise.<void> * [.setAlsoKnownAs(options)](#Account+setAlsoKnownAs) ⇒ Promise.<void> * [.setController(options)](#Account+setController) ⇒ Promise.<void> * [.deleteMethod(options)](#Account+deleteMethod) ⇒ Promise.<void> - * [.createService(options)](#Account+createService) ⇒ Promise.<void> - - -### account.attachMethodRelationships(options) ⇒ Promise.<void> -Attach one or more verification relationships to a method. + -Note: the method must exist and be in the set of verification methods; -it cannot be an embedded method. +### account.createService(options) ⇒ Promise.<void> +Adds a new Service to the DID Document. **Kind**: instance method of [Account](#Account) | Param | Type | | --- | --- | -| options | AttachMethodRelationshipOptions | +| options | CreateServiceOptions | @@ -273,17 +270,6 @@ Adds a new verification method to the DID document. | --- | --- | | options | CreateMethodOptions | - - -### account.detachMethodRelationships(options) ⇒ Promise.<void> -Detaches the given relationship from the given method, if the method exists. - -**Kind**: instance method of [Account](#Account) - -| Param | Type | -| --- | --- | -| options | DetachMethodRelationshipOptions | - ### account.did() ⇒ [DID](#DID) @@ -475,6 +461,31 @@ Returns the decrypted text. | cek_algorithm | [CekAlgorithm](#CekAlgorithm) | | fragment | string | + + +### account.attachMethodRelationships(options) ⇒ Promise.<void> +Attach one or more verification relationships to a method. + +Note: the method must exist and be in the set of verification methods; +it cannot be an embedded method. + +**Kind**: instance method of [Account](#Account) + +| Param | Type | +| --- | --- | +| options | AttachMethodRelationshipOptions | + + + +### account.detachMethodRelationships(options) ⇒ Promise.<void> +Detaches the given relationship from the given method, if the method exists. + +**Kind**: instance method of [Account](#Account) + +| Param | Type | +| --- | --- | +| options | DetachMethodRelationshipOptions | + ### account.deleteService(options) ⇒ Promise.<void> @@ -519,17 +530,6 @@ Deletes a verification method if the method exists. | --- | --- | | options | DeleteMethodOptions | - - -### account.createService(options) ⇒ Promise.<void> -Adds a new Service to the DID Document. - -**Kind**: instance method of [Account](#Account) - -| Param | Type | -| --- | --- | -| options | CreateServiceOptions | - ## AccountBuilder @@ -4392,7 +4392,7 @@ See: https://www.w3.org/TR/did-core/#services * [new Service(service)](#new_Service_new) * _instance_ * [.id()](#Service+id) ⇒ [DIDUrl](#DIDUrl) - * [.type()](#Service+type) ⇒ string + * [.type()](#Service+type) ⇒ Array.<string> * [.serviceEndpoint()](#Service+serviceEndpoint) ⇒ string \| Array.<string> \| Map.<string, Array.<string>> * [.properties()](#Service+properties) ⇒ Map.<string, any> * [.toJSON()](#Service+toJSON) ⇒ any @@ -4416,7 +4416,7 @@ Returns a copy of the `Service` id. **Kind**: instance method of [Service](#Service) -### service.type() ⇒ string +### service.type() ⇒ Array.<string> Returns a copy of the `Service` type. **Kind**: instance method of [Service](#Service) @@ -4983,15 +4983,15 @@ Return all errors that occur during validation. ## FirstError Return after the first error occurs. -**Kind**: global variable - - -## MethodRelationship **Kind**: global variable ## KeyType **Kind**: global variable + + +## MethodRelationship +**Kind**: global variable ## start() diff --git a/bindings/wasm/src/account/wasm_account/update/create_service.rs b/bindings/wasm/src/account/wasm_account/update/create_service.rs index ca01cb764f..b1a2eafe5f 100644 --- a/bindings/wasm/src/account/wasm_account/update/create_service.rs +++ b/bindings/wasm/src/account/wasm_account/update/create_service.rs @@ -10,6 +10,7 @@ use identity_iota::account::IdentityUpdater; use identity_iota::account::UpdateError::MissingRequiredField; use identity_iota::client::Client; use identity_iota::core::Object; +use identity_iota::core::OneOrSet; use identity_iota::did::ServiceEndpoint; use js_sys::Promise; use wasm_bindgen::prelude::*; @@ -28,12 +29,11 @@ impl WasmAccount { /// Adds a new Service to the DID Document. #[wasm_bindgen(js_name = createService)] pub fn create_service(&mut self, options: &CreateServiceOptions) -> Result { - let service_type: String = options.type_().ok_or(MissingRequiredField("type")).wasm_result()?; - let fragment: String = options .fragment() .ok_or(MissingRequiredField("fragment")) .wasm_result()?; + let service_types: OneOrSet = options.type_().into_serde().wasm_result()?; let endpoint: ServiceEndpoint = deserialize_map_or_any(&options.endpoint())?; let properties: Option = deserialize_map_or_any(&options.properties())?; @@ -44,7 +44,7 @@ impl WasmAccount { let mut create_service: CreateServiceBuilder<'_, Rc> = updater .create_service() .fragment(fragment) - .type_(service_type) + .types(service_types) .endpoint(endpoint); if let Some(properties) = properties { @@ -67,7 +67,7 @@ extern "C" { pub fn fragment(this: &CreateServiceOptions) -> Option; #[wasm_bindgen(getter, method, js_name = type)] - pub fn type_(this: &CreateServiceOptions) -> Option; + pub fn type_(this: &CreateServiceOptions) -> JsValue; #[wasm_bindgen(getter, method)] pub fn endpoint(this: &CreateServiceOptions) -> JsValue; @@ -90,7 +90,7 @@ export type CreateServiceOptions = { /** * The type of the service. */ - type: string; + type: string | string[]; /** * The `ServiceEndpoint` of the service. diff --git a/bindings/wasm/src/did/wasm_document.rs b/bindings/wasm/src/did/wasm_document.rs index 1231781cd6..1c8b037743 100644 --- a/bindings/wasm/src/did/wasm_document.rs +++ b/bindings/wasm/src/did/wasm_document.rs @@ -24,6 +24,7 @@ use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; use crate::account::wasm_account::UOneOrManyNumber; +use crate::common::ArrayString; use crate::common::MapStringAny; use crate::common::WasmTimestamp; use crate::credential::WasmCredential; @@ -701,7 +702,6 @@ impl From for IotaDocument { } } -/// Duck-typed union to pass either a string or WasmDIDUrl as a parameter. #[wasm_bindgen] extern "C" { #[wasm_bindgen(typescript_type = "DIDUrl | string")] @@ -719,9 +719,6 @@ extern "C" { #[wasm_bindgen(typescript_type = "Service[]")] pub type ArrayService; - #[wasm_bindgen(typescript_type = "Array")] - pub type ArrayString; - #[wasm_bindgen(typescript_type = "VerificationMethod[]")] pub type ArrayVerificationMethods; diff --git a/bindings/wasm/src/did/wasm_service.rs b/bindings/wasm/src/did/wasm_service.rs index 49119d6372..63a90b7f1b 100644 --- a/bindings/wasm/src/did/wasm_service.rs +++ b/bindings/wasm/src/did/wasm_service.rs @@ -1,14 +1,16 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use crate::common::deserialize_map_or_any; -use crate::common::MapStringAny; +use identity_iota::core::OneOrMany; use identity_iota::did::ServiceEndpoint; use identity_iota::iota_core::IotaDIDUrl; use identity_iota::iota_core::IotaService; use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; +use crate::common::deserialize_map_or_any; +use crate::common::ArrayString; +use crate::common::MapStringAny; use crate::did::WasmDIDUrl; use crate::error::Result; use crate::error::WasmResult; @@ -25,13 +27,13 @@ impl WasmService { #[wasm_bindgen(constructor)] pub fn new(service: IService) -> Result { let id: IotaDIDUrl = service.id().into_serde().wasm_result()?; - let type_: String = service.type_(); + let types: OneOrMany = service.type_().into_serde().wasm_result()?; let service_endpoint: ServiceEndpoint = deserialize_map_or_any(&service.service_endpoint())?; let properties: Option = deserialize_map_or_any(&service.properties())?; IotaService::builder(properties.unwrap_or_default()) .id(id) - .type_(type_) + .types(types) .service_endpoint(service_endpoint) .build() .map(WasmService::from) @@ -46,8 +48,15 @@ impl WasmService { /// Returns a copy of the `Service` type. #[wasm_bindgen(js_name = type)] - pub fn type_(&self) -> String { - self.0.type_().to_owned() + pub fn type_(&self) -> ArrayString { + self + .0 + .type_() + .iter() + .cloned() + .map(JsValue::from) + .collect::() + .unchecked_into::() } /// Returns a copy of the `Service` endpoint. @@ -87,13 +96,13 @@ pub(crate) fn service_endpoint_to_js_value(endpoint: &ServiceEndpoint) -> UServi match endpoint { // string ServiceEndpoint::One(url) => JsValue::from_str(url.as_str()).unchecked_into::(), - // [string] + // string[] ServiceEndpoint::Set(set) => set .iter() .map(|url| JsValue::from_str(url.as_str())) .collect::() .unchecked_into::(), - // Map + // Map ServiceEndpoint::Map(map) => { let js_map: js_sys::Map = js_sys::Map::new(); for (key, urls) in map.into_iter() { @@ -125,7 +134,7 @@ extern "C" { pub fn id(this: &IService) -> JsValue; #[wasm_bindgen(method, getter, js_name = type)] - pub fn type_(this: &IService) -> String; + pub fn type_(this: &IService) -> JsValue; #[wasm_bindgen(method, getter, js_name = serviceEndpoint)] pub fn service_endpoint(this: &IService) -> JsValue; @@ -152,7 +161,7 @@ interface IService { * * E.g. "LinkedDomains" or "DIDCommMessaging". */ - readonly type: string; + readonly type: string | string[]; /** * A URL, set of URLs, or map of URL sets. diff --git a/bindings/wasm/tests/account.ts b/bindings/wasm/tests/account.ts index 75699054b5..6e30c04b9c 100644 --- a/bindings/wasm/tests/account.ts +++ b/bindings/wasm/tests/account.ts @@ -4,11 +4,11 @@ const assert = require('assert'); const { AccountBuilder, AutoSave, - MethodScope, - MethodContent, KeyType, - MethodType, KeyPair, + MethodContent, + MethodScope, + MethodType, } = require("../node"); function setupAccountBuilder() { @@ -98,4 +98,36 @@ describe('Account', function () { assert.equal(method2.data().tryDecode().toString(), keypair.public().toString()); }); }); + describe('#createService()', function () { + it('should take one type and endpoint', async () => { + const account = await setupAccount(); + + // Test single type & endpoint. + const fragment1 = "new-service-1"; + await account.createService({ + fragment: fragment1, + type: "LinkedDomains", + endpoint: "https://example.com/" + }); + const service = account.document().resolveService(fragment1); + assert.deepStrictEqual(service.id().fragment(), fragment1); + assert.deepStrictEqual(service.type(), ["LinkedDomains"]); + assert.deepStrictEqual(service.serviceEndpoint(), "https://example.com/"); + }); + it('should take multiple types and endpoints', async () => { + const account = await setupAccount(); + + // Test multiple types & endpoints. + const fragment1 = "new-service-1"; + await account.createService({ + fragment: fragment1, + type: ["LinkedDomains", "ExampleType"], + endpoint: ["https://example.com/", "https://iota.org/"] + }); + const service = account.document().resolveService(fragment1); + assert.deepStrictEqual(service.id().fragment(), fragment1); + assert.deepStrictEqual(service.type(), ["LinkedDomains", "ExampleType"]); + assert.deepStrictEqual(service.serviceEndpoint(), ["https://example.com/", "https://iota.org/"]); + }); + }); }); diff --git a/bindings/wasm/tests/document.ts b/bindings/wasm/tests/document.ts new file mode 100644 index 0000000000..0593b6abef --- /dev/null +++ b/bindings/wasm/tests/document.ts @@ -0,0 +1,50 @@ +export {}; + +const assert = require('assert'); +const { + Document, + KeyType, + KeyPair, + Service, +} = require("../node"); + +describe('Document', function () { + describe('#insertService()', function () { + it('should take one type', async () => { + const keypair = new KeyPair(KeyType.Ed25519); + const doc = new Document(keypair); + + // Test single type. + const fragment1 = "new-service-1"; + doc.insertService(new Service({ + id: doc.id().toUrl().join('#' + fragment1), + type: "LinkedDomains", + serviceEndpoint: { + "origins": ["https://iota.org/", "https://example.com/"] + }, + })); + const service = doc.resolveService(fragment1); + assert.deepStrictEqual(service.id().fragment(), fragment1); + assert.deepStrictEqual(service.type(), ["LinkedDomains"]); + assert.deepStrictEqual(service.serviceEndpoint(), new Map([ + ["origins", ["https://iota.org/", "https://example.com/"]], + ])); + }); + it('should take multiple types', async () => { + const keypair = new KeyPair(KeyType.Ed25519); + const doc = new Document(keypair); + + // Test multiple types. + const fragment1 = "new-service-1"; + doc.insertService(new Service({ + id: doc.id().toUrl().join('#' + fragment1), + type: ["LinkedDomains", "ExampleType"], + serviceEndpoint: ["https://example.com/", "https://iota.org/"], + })); + const service = doc.resolveService(fragment1); + assert.deepStrictEqual(service.id().fragment(), fragment1); + assert.deepStrictEqual(service.type(), ["LinkedDomains", "ExampleType"]); + assert.deepStrictEqual(service.serviceEndpoint(), ["https://example.com/", "https://iota.org/"]); + }); + }); +}); diff --git a/identity_account/src/tests/updates.rs b/identity_account/src/tests/updates.rs index 7565791f46..3ac3b0a893 100644 --- a/identity_account/src/tests/updates.rs +++ b/identity_account/src/tests/updates.rs @@ -621,7 +621,7 @@ async fn test_insert_service() -> Result<()> { let update: Update = Update::CreateService { fragment: fragment.clone(), - type_: "LinkedDomains".to_owned(), + types: vec!["LinkedDomains".to_owned()], endpoint: ServiceEndpoint::One(Url::parse("https://iota.org").unwrap()), properties: None, }; @@ -652,7 +652,7 @@ async fn test_remove_service() -> Result<()> { let update: Update = Update::CreateService { fragment: fragment.clone(), - type_: "LinkedDomains".to_owned(), + types: vec!["LinkedDomains".to_owned()], endpoint: ServiceEndpoint::One(Url::parse("https://iota.org").unwrap()), properties: None, }; diff --git a/identity_account/src/updates/update.rs b/identity_account/src/updates/update.rs index 00da8a535e..0cb2a3bdc1 100644 --- a/identity_account/src/updates/update.rs +++ b/identity_account/src/updates/update.rs @@ -82,7 +82,7 @@ pub(crate) enum Update { }, CreateService { fragment: String, - type_: String, + types: Vec, endpoint: ServiceEndpoint, properties: Option, }, @@ -198,7 +198,7 @@ impl Update { } Self::CreateService { fragment, - type_, + types, endpoint, properties, } => { @@ -214,7 +214,7 @@ impl Update { let service: IotaService = Service::builder(properties.unwrap_or_default()) .id(did_url) .service_endpoint(endpoint) - .type_(type_) + .types(types) .build()?; document.insert_service(service); @@ -343,17 +343,28 @@ impl_update_builder!( /// Create a new service on an identity. /// /// # Parameters -/// - `type_`: the type of the service, e.g. `"LinkedDomains"`, required. +/// - `types`: the type/s of the service, e.g. `"LinkedDomains"`, required. /// - `fragment`: the identifier of the service in the document, required. /// - `endpoint`: the `ServiceEndpoint` of the service, required. /// - `properties`: additional properties of the service, optional. CreateService { @required fragment String, - @required type_ String, + @required types Vec, @required endpoint ServiceEndpoint, @optional properties Object, }); +impl<'account, C> CreateServiceBuilder<'account, C> +where + C: SharedPtr, +{ + #[must_use] + pub fn type_(mut self, value: impl Into) -> Self { + self.types.get_or_insert_with(Default::default).push(value.into()); + self + } +} + impl_update_builder!( /// Delete a service on an identity. /// diff --git a/identity_core/src/common/key_comparable.rs b/identity_core/src/common/key_comparable.rs index ce058939c9..cfe8973ed4 100644 --- a/identity_core/src/common/key_comparable.rs +++ b/identity_core/src/common/key_comparable.rs @@ -9,3 +9,37 @@ pub trait KeyComparable { /// Returns a reference to the key. fn key(&self) -> &Self::Key; } + +/// Macro to implement the `KeyComparable` trait for primitive types. +/// +/// This approach is used to avoid conflicts from a blanket implementation where the type is the +/// key itself. +macro_rules! impl_key_comparable { + ($($t:ty)*) => ($( + impl KeyComparable for $t { + type Key = $t; + #[inline] + fn key(&self) -> &Self::Key { self } + } + )*) +} + +impl_key_comparable! { + str bool char usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 +} + +impl KeyComparable for &str { + type Key = str; + + fn key(&self) -> &Self::Key { + self + } +} + +impl KeyComparable for String { + type Key = str; + + fn key(&self) -> &Self::Key { + self + } +} diff --git a/identity_core/src/common/one_or_set.rs b/identity_core/src/common/one_or_set.rs index f7ffd95f5e..c4ee406c75 100644 --- a/identity_core/src/common/one_or_set.rs +++ b/identity_core/src/common/one_or_set.rs @@ -151,7 +151,7 @@ where pub fn contains(&self, item: &U) -> bool where T: KeyComparable, - U: KeyComparable, + U: KeyComparable + ?Sized, { match &self.0 { OneOrSetInner::One(inner) => inner.key() == item.key(), @@ -439,16 +439,16 @@ mod tests { fn test_contains() { // One. let one: OneOrSet = OneOrSet::new_one(MockKeyU8(1)); - assert!(one.contains(&1)); - assert!(!one.contains(&2)); - assert!(!one.contains(&3)); + assert!(one.contains(&1u8)); + assert!(!one.contains(&2u8)); + assert!(!one.contains(&3u8)); // Set. let set: OneOrSet = OneOrSet::new_set((1..=3).map(MockKeyU8).collect()).unwrap(); - assert!(set.contains(&1)); - assert!(set.contains(&2)); - assert!(set.contains(&3)); - assert!(!set.contains(&4)); + assert!(set.contains(&1u8)); + assert!(set.contains(&2u8)); + assert!(set.contains(&3u8)); + assert!(!set.contains(&4u8)); } #[test] diff --git a/identity_core/src/common/ordered_set.rs b/identity_core/src/common/ordered_set.rs index 1acc41e419..9cccca1288 100644 --- a/identity_core/src/common/ordered_set.rs +++ b/identity_core/src/common/ordered_set.rs @@ -118,7 +118,7 @@ impl OrderedSet { pub fn contains(&self, item: &U) -> bool where T: KeyComparable, - U: KeyComparable, + U: KeyComparable + ?Sized, { self.0.iter().any(|other| other.key() == item.key()) } @@ -313,22 +313,6 @@ where mod tests { use super::*; - impl KeyComparable for &str { - type Key = str; - - fn key(&self) -> &Self::Key { - self - } - } - - impl KeyComparable for u8 { - type Key = u8; - - fn key(&self) -> &Self::Key { - self - } - } - #[test] fn test_ordered_set_works() { let mut set = OrderedSet::new(); diff --git a/identity_credential/src/validator/credential_validator.rs b/identity_credential/src/validator/credential_validator.rs index 81cb80eca9..59c999f51f 100644 --- a/identity_credential/src/validator/credential_validator.rs +++ b/identity_credential/src/validator/credential_validator.rs @@ -9,7 +9,6 @@ use identity_core::common::OneOrMany; use identity_core::common::Timestamp; use identity_core::common::Url; use identity_did::did::CoreDID; -use identity_did::did::CoreDIDUrl; use identity_did::did::DID; #[cfg(feature = "revocation-bitmap")] use identity_did::revocation::RevocationBitmap; @@ -208,7 +207,7 @@ impl CredentialValidator { issuer: &DOC, status: RevocationBitmapStatus, ) -> ValidationUnitResult { - let issuer_service_url: CoreDIDUrl = status.id().map_err(ValidationError::InvalidStatus)?; + let issuer_service_url: identity_did::did::CoreDIDUrl = status.id().map_err(ValidationError::InvalidStatus)?; // Check whether index is revoked. let revocation_bitmap: RevocationBitmap = issuer @@ -762,7 +761,7 @@ mod tests { } // Add a RevocationBitmap status to the credential. - let service_url: CoreDIDUrl = issuer_doc.id().to_url().join("#revocation-service").unwrap(); + let service_url: identity_did::did::CoreDIDUrl = issuer_doc.id().to_url().join("#revocation-service").unwrap(); let index: u32 = 42; credential.credential_status = Some(RevocationBitmapStatus::new(service_url.clone(), index).into()); diff --git a/identity_credential/src/validator/validator_document.rs b/identity_credential/src/validator/validator_document.rs index 118466d9bc..01ff3d4abe 100644 --- a/identity_credential/src/validator/validator_document.rs +++ b/identity_credential/src/validator/validator_document.rs @@ -4,8 +4,8 @@ use identity_core::crypto::GetSignature; use identity_did::did::DID; use identity_did::document::Document; +#[cfg(feature = "revocation-bitmap")] use identity_did::revocation::RevocationBitmap; -use identity_did::utils::DIDUrlQuery; use identity_did::verifiable::VerifierOptions; use self::private::Sealed; @@ -45,7 +45,10 @@ pub trait ValidatorDocument: Sealed { /// Fails if the referenced service is not found, or is not a /// valid `RevocationBitmap2022` service. #[cfg(feature = "revocation-bitmap")] - fn resolve_revocation_bitmap(&self, query: DIDUrlQuery<'_>) -> identity_did::Result; + fn resolve_revocation_bitmap( + &self, + query: identity_did::utils::DIDUrlQuery<'_>, + ) -> identity_did::Result; } mod private { @@ -82,7 +85,10 @@ impl ValidatorDocument for &dyn ValidatorDocument { } #[cfg(feature = "revocation-bitmap")] - fn resolve_revocation_bitmap(&self, query: DIDUrlQuery<'_>) -> identity_did::Result { + fn resolve_revocation_bitmap( + &self, + query: identity_did::utils::DIDUrlQuery<'_>, + ) -> identity_did::Result { (*self).resolve_revocation_bitmap(query) } } @@ -100,7 +106,10 @@ where } #[cfg(feature = "revocation-bitmap")] - fn resolve_revocation_bitmap(&self, query: DIDUrlQuery<'_>) -> identity_did::Result { + fn resolve_revocation_bitmap( + &self, + query: identity_did::utils::DIDUrlQuery<'_>, + ) -> identity_did::Result { self .resolve_service(query) .ok_or(identity_did::Error::InvalidService( diff --git a/identity_did/src/diff/diff_service.rs b/identity_did/src/diff/diff_service.rs index cbcf5eccfb..4e2d78861a 100644 --- a/identity_did/src/diff/diff_service.rs +++ b/identity_did/src/diff/diff_service.rs @@ -5,8 +5,8 @@ use serde::Deserialize; use serde::Serialize; use identity_core::common::Object; +use identity_core::common::OneOrSet; use identity_core::diff::Diff; -use identity_core::diff::DiffString; use identity_core::diff::Error; use identity_core::diff::Result; @@ -26,7 +26,7 @@ where #[serde(skip_serializing_if = "Option::is_none")] id: Option< as Diff>::Type>, #[serde(skip_serializing_if = "Option::is_none")] - type_: Option, + type_: Option< as Diff>::Type>, #[serde(skip_serializing_if = "Option::is_none")] service_endpoint: Option<::Type>, #[serde(skip_serializing_if = "Option::is_none")] @@ -50,7 +50,7 @@ where type_: if self.type_() == other.type_() { None } else { - Some(self.type_().to_string().diff(&other.type_().to_string())?) + Some(self.type_().diff(other.type_())?) }, service_endpoint: if self.service_endpoint() == other.service_endpoint() { None @@ -72,11 +72,11 @@ where .transpose()? .unwrap_or_else(|| self.id().clone()); - let type_: String = diff + let type_: OneOrSet = diff .type_ - .map(|value| self.type_().to_string().merge(value)) + .map(|value| self.type_().merge(value)) .transpose()? - .unwrap_or_else(|| self.type_().to_string()); + .unwrap_or_else(|| self.type_().clone()); let service_endpoint: ServiceEndpoint = diff .service_endpoint @@ -93,7 +93,7 @@ where // Use builder to enforce invariants. ServiceBuilder::new(properties) .id(id) - .type_(type_) + .types(type_) .service_endpoint(service_endpoint) .build() .map_err(Error::merge) @@ -106,7 +106,7 @@ where .transpose()? .ok_or_else(|| Error::convert("Missing field `service.id`"))?; - let type_: String = diff + let type_: OneOrSet = diff .type_ .map(Diff::from_diff) .transpose()? @@ -123,7 +123,7 @@ where // Use builder to enforce invariants. ServiceBuilder::new(properties) .id(id) - .type_(type_) + .types(type_) .service_endpoint(service_endpoint) .build() .map_err(Error::convert) @@ -181,6 +181,7 @@ mod test { use identity_core::common::Url; use identity_core::convert::FromJson; use identity_core::convert::ToJson; + use identity_core::diff::DiffString; use identity_core::diff::DiffVec; use super::*; @@ -193,7 +194,7 @@ mod test { let controller = controller(); Service::builder(Object::default()) .id(controller) - .service_endpoint(Url::parse("did:service:1234").unwrap().into()) + .service_endpoint(Url::parse("did:service:1234").unwrap()) .type_("test_service") .build() .unwrap() @@ -215,20 +216,50 @@ mod test { } #[test] - fn test_type() { + fn test_type_one() { let service = service(); let mut new = service.clone(); - *new.type_mut() = "test_service_2".parse().unwrap(); + *new.type_mut() = OneOrSet::new_one("test_service_2".to_owned()); let diff = service.diff(&new).unwrap(); assert!(diff.properties.is_none()); assert!(diff.service_endpoint.is_none()); assert!(diff.id.is_none()); - assert_eq!(diff.type_, Some(DiffString(Some("test_service_2".to_string())))); + assert!(diff.type_.is_some()); let merge = service.merge(diff).unwrap(); assert_eq!(merge, new); } + #[test] + fn test_type_set() { + let service = service(); + + // One to set. + let mut service_set = service.clone(); + *service_set.type_mut() = + OneOrSet::try_from(vec!["test_service_set_1".to_owned(), "test_service_set_2".to_owned()]).unwrap(); + + let diff_set = service.diff(&service_set).unwrap(); + assert!(diff_set.properties.is_none()); + assert!(diff_set.service_endpoint.is_none()); + assert!(diff_set.id.is_none()); + assert!(diff_set.type_.is_some()); + let merge = service.merge(diff_set).unwrap(); + assert_eq!(merge, service_set); + + // Set to one. + let mut service_one = service_set.clone(); + *service_one.type_mut() = OneOrSet::new_one("one_service_type".to_owned()); + + let diff_one = service_set.diff(&service_one).unwrap(); + assert!(diff_one.properties.is_none()); + assert!(diff_one.service_endpoint.is_none()); + assert!(diff_one.id.is_none()); + assert!(diff_one.type_.is_some()); + let merge = service_set.merge(diff_one).unwrap(); + assert_eq!(merge, service_one); + } + #[test] fn test_service_endpoint_one() { let service = service(); @@ -357,7 +388,7 @@ mod test { { let mut updated: Service = service.clone(); updated.id = CoreDIDUrl::parse("did:test:serde").unwrap(); - updated.type_ = "TestSerde".into(); + updated.type_ = OneOrSet::new_one("TestSerde".to_owned()); updated.service_endpoint = ServiceEndpoint::One(Url::parse("https://test.serde/").unwrap()); updated.properties.insert("a".into(), 42.into()); let diff: DiffService = Diff::diff(&service, &updated).unwrap(); @@ -370,7 +401,7 @@ mod test { #[test] fn test_ordered_set_service_diff_serde() { let mut service = service(); - service.type_ = "".to_string(); + service.type_ = OneOrSet::from("".to_string()); let set0: OrderedSet = OrderedSet::new(); let set1: OrderedSet = OrderedSet::try_from(vec![service]).unwrap(); diff --git a/identity_did/src/revocation/bitmap.rs b/identity_did/src/revocation/bitmap.rs index 70045a844a..eab516955a 100644 --- a/identity_did/src/revocation/bitmap.rs +++ b/identity_did/src/revocation/bitmap.rs @@ -145,7 +145,7 @@ impl TryFrom<&Service> for RevocationBitmap { type Error = Error; fn try_from(service: &Service) -> Result { - if service.type_() != Self::TYPE { + if !service.type_().contains(Self::TYPE) { return Err(Error::InvalidService("invalid type - expected `RevocationBitmap2022`")); } diff --git a/identity_did/src/service/builder.rs b/identity_did/src/service/builder.rs index 0620b60ee8..70ea63cc4e 100644 --- a/identity_did/src/service/builder.rs +++ b/identity_did/src/service/builder.rs @@ -17,7 +17,7 @@ where D: DID, { pub(crate) id: Option>, - pub(crate) type_: Option, + pub(crate) type_: Vec, pub(crate) service_endpoint: Option, pub(crate) properties: T, } @@ -30,7 +30,7 @@ where pub fn new(properties: T) -> Self { Self { id: None, - type_: None, + type_: Vec::new(), service_endpoint: None, properties, } @@ -43,17 +43,26 @@ where self } - /// Sets the `type` value of the generated `Service`. + /// Appends a `type` value to the generated `Service`. #[must_use] pub fn type_(mut self, value: impl Into) -> Self { - self.type_ = Some(value.into()); + self.type_.push(value.into()); + self + } + + /// Sets the `type` value of the generated `Service`, overwriting any previous `type` entries. + /// + /// NOTE: the entries must be unique. + #[must_use] + pub fn types(mut self, values: impl Into>) -> Self { + self.type_ = values.into(); self } /// Sets the `serviceEndpoint` value of the generated `Service`. #[must_use] - pub fn service_endpoint(mut self, value: ServiceEndpoint) -> Self { - self.service_endpoint = Some(value); + pub fn service_endpoint(mut self, value: impl Into) -> Self { + self.service_endpoint = Some(value.into()); self } @@ -71,7 +80,7 @@ where fn default() -> Self { Self { id: None, - type_: None, + type_: Vec::default(), service_endpoint: None, properties: T::default(), } @@ -90,7 +99,7 @@ mod tests { let _: Service = ServiceBuilder::default() .id("did:example:123#service".parse().unwrap()) .type_("ServiceType") - .service_endpoint(Url::parse("https://example.com").unwrap().into()) + .service_endpoint(Url::parse("https://example.com").unwrap()) .build() .unwrap(); } @@ -99,7 +108,7 @@ mod tests { fn test_missing_id() { let result: Result = ServiceBuilder::default() .type_("ServiceType") - .service_endpoint(Url::parse("https://example.com").unwrap().into()) + .service_endpoint(Url::parse("https://example.com").unwrap()) .build(); assert!(matches!(result.unwrap_err(), Error::InvalidService(_))); } @@ -109,7 +118,7 @@ mod tests { let result: Result = ServiceBuilder::default() .id("did:example:123".parse().unwrap()) .type_("ServiceType") - .service_endpoint(Url::parse("https://example.com").unwrap().into()) + .service_endpoint(Url::parse("https://example.com").unwrap()) .build(); assert!(matches!(result.unwrap_err(), Error::InvalidService(_))); } @@ -118,7 +127,7 @@ mod tests { fn test_missing_type_() { let result: Result = ServiceBuilder::default() .id("did:example:123#service".parse().unwrap()) - .service_endpoint(Url::parse("https://example.com").unwrap().into()) + .service_endpoint(Url::parse("https://example.com").unwrap()) .build(); assert!(matches!(result.unwrap_err(), Error::InvalidService(_))); } diff --git a/identity_did/src/service/service.rs b/identity_did/src/service/service.rs index 2817090bfa..aa41b9b1c8 100644 --- a/identity_did/src/service/service.rs +++ b/identity_did/src/service/service.rs @@ -10,6 +10,7 @@ use serde::Serialize; use identity_core::common::KeyComparable; use identity_core::common::Object; +use identity_core::common::OneOrSet; use identity_core::convert::FmtJson; use crate::did::CoreDID; @@ -32,7 +33,7 @@ where #[serde(deserialize_with = "deserialize_id_with_fragment")] pub(crate) id: DIDUrl, #[serde(rename = "type")] - pub(crate) type_: String, + pub(crate) type_: OneOrSet, #[serde(rename = "serviceEndpoint")] pub(crate) service_endpoint: ServiceEndpoint, #[serde(flatten)] @@ -72,7 +73,7 @@ where Ok(Self { id, - type_: builder.type_.ok_or(Error::InvalidService("missing type"))?, + type_: OneOrSet::try_from(builder.type_).map_err(|_| Error::InvalidService("invalid type"))?, service_endpoint: builder .service_endpoint .ok_or(Error::InvalidService("missing endpoint"))?, @@ -98,12 +99,12 @@ where } /// Returns a reference to the `Service` type. - pub fn type_(&self) -> &str { - &*self.type_ + pub fn type_(&self) -> &OneOrSet { + &self.type_ } /// Returns a mutable reference to the `Service` type. - pub fn type_mut(&mut self) -> &mut String { + pub fn type_mut(&mut self) -> &mut OneOrSet { &mut self.type_ } @@ -198,13 +199,52 @@ mod tests { use identity_core::convert::ToJson; #[test] - fn test_service_serde() { - // Single endpoint + fn test_service_types_serde() { + // Single type. + let service1: Service = Service::builder(Object::new()) + .id(CoreDIDUrl::parse("did:example:123#service").unwrap()) + .type_("LinkedDomains") + .service_endpoint(Url::parse("https://iota.org/").unwrap()) + .build() + .unwrap(); + assert_eq!(service1.type_, OneOrSet::new_one("LinkedDomains".to_owned())); + assert!(service1.type_.contains("LinkedDomains")); + assert!(!service1.type_.contains("NotEntry")); + assert!(!service1.type_.contains("")); + + let expected1 = r#"{"id":"did:example:123#service","type":"LinkedDomains","serviceEndpoint":"https://iota.org/"}"#; + assert_eq!(service1.to_json().unwrap(), expected1); + assert_eq!(Service::from_json(expected1).unwrap(), service1); + + // Set of types. + let service2: Service = Service::builder(Object::new()) + .id(CoreDIDUrl::parse("did:example:123#service").unwrap()) + .types(["LinkedDomains".to_owned(), "OtherService2022".to_owned()]) + .service_endpoint(Url::parse("https://iota.org/").unwrap()) + .build() + .unwrap(); + assert_eq!( + service2.type_, + OneOrSet::try_from(vec!["LinkedDomains".to_owned(), "OtherService2022".to_owned()]).unwrap() + ); + assert!(service2.type_.contains("LinkedDomains")); + assert!(service2.type_.contains("OtherService2022")); + assert!(!service2.type_.contains("NotEntry")); + assert!(!service2.type_.contains("")); + + let expected2 = r#"{"id":"did:example:123#service","type":["LinkedDomains","OtherService2022"],"serviceEndpoint":"https://iota.org/"}"#; + assert_eq!(service2.to_json().unwrap(), expected2); + assert_eq!(Service::from_json(expected2).unwrap(), service2) + } + + #[test] + fn test_service_endpoint_serde() { + // Single endpoint. { let service: Service = Service::builder(Object::new()) .id(CoreDIDUrl::parse("did:example:123#service").unwrap()) - .type_("LinkedDomains".to_owned()) - .service_endpoint(Url::parse("https://iota.org/").unwrap().into()) + .type_("LinkedDomains") + .service_endpoint(Url::parse("https://iota.org/").unwrap()) .build() .unwrap(); let expected = r#"{"id":"did:example:123#service","type":"LinkedDomains","serviceEndpoint":"https://iota.org/"}"#; @@ -212,7 +252,7 @@ mod tests { assert_eq!(Service::from_json(expected).unwrap(), service); } - // Set of endpoints + // Set of endpoints. { let endpoint: ServiceEndpoint = ServiceEndpoint::Set( OrderedSet::try_from(vec![ @@ -224,7 +264,7 @@ mod tests { ); let service: Service = Service::builder(Object::new()) .id(CoreDIDUrl::parse("did:example:123#service").unwrap()) - .type_("LinkedDomains".to_owned()) + .type_("LinkedDomains") .service_endpoint(endpoint) .build() .unwrap(); From cfe789de0cf602049922564781604dfa83ed35dd Mon Sep 17 00:00:00 2001 From: Philipp Date: Mon, 18 Jul 2022 08:17:40 +0300 Subject: [PATCH 23/89] Remove >::eq` (#946) --- identity_did/src/did/did.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/identity_did/src/did/did.rs b/identity_did/src/did/did.rs index c1637d2cd0..c4f2bc4360 100644 --- a/identity_did/src/did/did.rs +++ b/identity_did/src/did/did.rs @@ -277,12 +277,6 @@ impl PartialEq<&'_ str> for CoreDID { } } -impl PartialEq<&CoreDID> for CoreDID { - fn eq(&self, other: &&CoreDID) -> bool { - self == other - } -} - impl KeyComparable for CoreDID { type Key = CoreDID; From 7e95e255bdfd0c2ff53d9c07452fb74750ff18bc Mon Sep 17 00:00:00 2001 From: Philipp Date: Tue, 19 Jul 2022 09:40:24 +0300 Subject: [PATCH 24/89] State metadata serialization for the stardust DID method (#947) * Implement state metadata document * Update example * Implement roundtrip transformation * Use StateMetadataDoc for (de)serialization * Update example with extended document * Rm needless clone; improve test naming * Impl draining iterator for `OrderedSet` * Make document conversion more efficient * Polish docs * Simplify transformation * Add version, encoding and `DID` marker * Use stardust did in tests * Move `DID` marker to front; improve errors --- identity_stardust/Cargo.toml | 4 +- identity_stardust/examples/create_did.rs | 7 +- identity_stardust/src/error.rs | 2 + identity_stardust/src/lib.rs | 2 + identity_stardust/src/stardust_document.rs | 106 +++++-- .../src/state_metadata/document.rs | 292 ++++++++++++++++++ .../src/state_metadata/encoding.rs | 20 ++ identity_stardust/src/state_metadata/mod.rs | 10 + .../src/state_metadata/version.rs | 24 ++ 9 files changed, 432 insertions(+), 35 deletions(-) create mode 100644 identity_stardust/src/state_metadata/document.rs create mode 100644 identity_stardust/src/state_metadata/encoding.rs create mode 100644 identity_stardust/src/state_metadata/mod.rs create mode 100644 identity_stardust/src/state_metadata/version.rs diff --git a/identity_stardust/Cargo.toml b/identity_stardust/Cargo.toml index 212548aa76..a7167c8356 100644 --- a/identity_stardust/Cargo.toml +++ b/identity_stardust/Cargo.toml @@ -16,7 +16,9 @@ description = "An IOTA Ledger integration for the identity.rs library." identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } identity_credential = { version = "=0.6.0", path = "../identity_credential", default-features = false } identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } -lazy_static = { version = "1.4", default-features = false } +num-derive = { version = "0.3", default-features = false } +num-traits = { version = "0.2", default-features = false, features = ["std"] } +once_cell = { version = "1.13", default-features = false } serde = { version = "1.0", default-features = false, features = ["std", "derive"] } strum = { version = "0.21", features = ["derive"] } thiserror = { version = "1.0", default-features = false } diff --git a/identity_stardust/examples/create_did.rs b/identity_stardust/examples/create_did.rs index 4c4b978d16..9b99e1cd8a 100644 --- a/identity_stardust/examples/create_did.rs +++ b/identity_stardust/examples/create_did.rs @@ -1,4 +1,3 @@ -use identity_core::convert::ToJson; use iota_client::bee_block::output::feature::IssuerFeature; use iota_client::bee_block::output::feature::MetadataFeature; use iota_client::bee_block::output::feature::SenderFeature; @@ -77,7 +76,9 @@ async fn main() -> anyhow::Result<()> { // Create an empty DID Document. // All new Stardust DID Documents initially use a placeholder DID, // "did:stardust:0x00000000000000000000000000000000". + let document: StardustDocument = StardustDocument::new(); + println!("DID Document {:#}", document); // Create a new Alias Output with the DID Document as state metadata. @@ -85,7 +86,7 @@ async fn main() -> anyhow::Result<()> { let alias_output: Output = AliasOutputBuilder::new_with_minimum_storage_deposit(byte_cost_config, AliasId::null())? .with_state_index(0) .with_foundry_counter(0) - .with_state_metadata(document.to_json_vec()?) + .with_state_metadata(document.pack()?) .add_feature(Feature::Sender(SenderFeature::new(address))) .add_feature(Feature::Metadata(MetadataFeature::new(vec![1, 2, 3])?)) .add_immutable_feature(Feature::Issuer(IssuerFeature::new(address))) @@ -151,8 +152,6 @@ async fn main() -> anyhow::Result<()> { let updated_alias_output = AliasOutputBuilder::from(&alias_output) // Not adding any content, previous amount will cover the deposit. // Set the explicit Alias ID. .with_alias_id(alias_id) - // Update the DID Document content to replace the placeholder DID. - .with_state_metadata(resolved_document.to_json_vec()?) // State controller updates increment the state index. .with_state_index(alias_output.state_index() + 1) .finish_output()?; diff --git a/identity_stardust/src/error.rs b/identity_stardust/src/error.rs index 0561931837..560f038bbd 100644 --- a/identity_stardust/src/error.rs +++ b/identity_stardust/src/error.rs @@ -18,4 +18,6 @@ pub enum Error { ClientError(#[from] iota_client::error::Error), #[error("{0}")] BeeError(#[from] iota_client::bee_block::Error), + #[error("invalid state metadata {0}")] + InvalidStateMetadata(&'static str), } diff --git a/identity_stardust/src/lib.rs b/identity_stardust/src/lib.rs index 50a7bc1c99..39cdc3998f 100644 --- a/identity_stardust/src/lib.rs +++ b/identity_stardust/src/lib.rs @@ -8,6 +8,8 @@ pub use self::error::Error; pub use self::error::Result; pub use stardust_document::StardustDocument; +pub use state_metadata::*; mod error; mod stardust_document; +mod state_metadata; diff --git a/identity_stardust/src/stardust_document.rs b/identity_stardust/src/stardust_document.rs index 35ff25dc6e..9fa632bc62 100644 --- a/identity_stardust/src/stardust_document.rs +++ b/identity_stardust/src/stardust_document.rs @@ -9,35 +9,34 @@ use std::str::FromStr; use identity_core::common::Object; use identity_core::convert::FmtJson; -use identity_core::convert::FromJson; +use identity_core::crypto::KeyPair; use identity_core::utils::Base; use identity_core::utils::BaseEncoding; use identity_did::did::CoreDID; use identity_did::did::DID; use identity_did::document::CoreDocument; +use identity_did::service::Service; +use identity_did::service::ServiceEndpoint; +use identity_did::verification::MethodScope; +use identity_did::verification::VerificationMethod; use iota_client::bee_block::output::AliasId; use iota_client::bee_block::output::Output; use iota_client::bee_block::output::OutputId; use iota_client::bee_block::payload::transaction::TransactionEssence; use iota_client::bee_block::payload::Payload; use iota_client::bee_block::Block; -use lazy_static::lazy_static; use serde::Deserialize; use serde::Serialize; use crate::error::Result; +use crate::state_metadata::StateMetadataEncoding; +use crate::state_metadata::PLACEHOLDER_DID; +use crate::StateMetadataDocument; /// An IOTA DID document resolved from the Tangle. Represents an integration chain message possibly /// merged with one or more diff messages. #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] -pub struct StardustDocument(CoreDocument); - -// Tag is 64-bytes long, matching the hex-encoding of the Alias ID (without 0x prefix). -// TODO: should we just keep the 0x prefix in the tag? Other DID methods like did:ethr do... -lazy_static! { - static ref PLACEHOLDER_DID: CoreDID = - CoreDID::parse("did:stardust:0000000000000000000000000000000000000000000000000000000000000000").unwrap(); -} +pub struct StardustDocument(pub(crate) CoreDocument); impl StardustDocument { /// Constructs an empty DID Document with a [`StardustDocument::placeholder_did`] identifier. @@ -51,8 +50,57 @@ impl StardustDocument { ) } + /// Temporary testing implementation. + pub fn tmp_add_verification_method( + &mut self, + id: CoreDID, + keypair: &KeyPair, + fragment: &str, + scope: MethodScope, + ) -> Result<()> { + let method: VerificationMethod = VerificationMethod::new(id, keypair.type_(), keypair.public(), fragment)?; + + self.0.insert_method(method, scope)?; + + Ok(()) + } + + /// Temporary testing implementation. + pub fn tmp_add_service(&mut self, id: CoreDID, fragment: &str, type_: &str, endpoint: ServiceEndpoint) -> Result<()> { + let service = Service::builder(Object::new()) + .type_(type_) + .id(id.join(fragment).unwrap()) + .service_endpoint(endpoint) + .build()?; + + self.0.service_mut().append(service); + + Ok(()) + } + + /// Temporary testing implementation. + pub fn tmp_set_id(&mut self, mut id: CoreDID) { + std::mem::swap(self.0.id_mut(), &mut id); + } + + /// Temporary testing implementation. + pub fn tmp_id(&self) -> &CoreDID { + self.0.id() + } + + /// Serializes the document for inclusion in an alias output's state metadata + /// with the default encoding. + pub fn pack(self) -> Result> { + self.pack_with_encoding(StateMetadataEncoding::Json) + } + + /// Serializes the document for inclusion in an alias output's state metadata. + pub fn pack_with_encoding(self, encoding: StateMetadataEncoding) -> Result> { + StateMetadataDocument::from(self).pack(encoding) + } + /// Returns the placeholder DID of newly constructed DID Documents, - /// `"did:stardust:0000000000000000000000000000000000000000000000000000000000000000"`. + /// `"did:0:0"`. // TODO: generalise to take network name? pub fn placeholder_did() -> &'static CoreDID { &PLACEHOLDER_DID @@ -79,7 +127,7 @@ impl StardustDocument { Self::alias_id_to_did(&id) } - fn parse_block(block: &Block) -> (AliasId, &[u8], bool) { + fn parse_block(block: &Block) -> (AliasId, &[u8]) { match block.payload().unwrap() { Payload::Transaction(tx_payload) => { let TransactionEssence::Regular(regular) = tx_payload.essence(); @@ -89,8 +137,7 @@ impl StardustDocument { .alias_id() .or_from_output_id(OutputId::new(tx_payload.id(), index.try_into().unwrap()).unwrap()); let document = alias_output.state_metadata(); - let first = alias_output.state_index() == 0; - return (alias_id, document, first); + return (alias_id, document); } } panic!("No alias output in transaction essence") @@ -104,31 +151,22 @@ impl StardustDocument { /// NOTE: [`AliasId`] is required since it cannot be inferred from the [`Output`] alone /// for the first time an Alias Output is published, the transaction payload is required. pub fn deserialize_from_output(alias_id: &AliasId, output: &Output) -> Result { - let (document, first): (&[u8], bool) = match output { - Output::Alias(alias_output) => (alias_output.state_metadata(), alias_output.alias_id().is_null()), + let document: &[u8] = match output { + Output::Alias(alias_output) => alias_output.state_metadata(), _ => panic!("not an alias output"), }; - Self::deserialize_inner(alias_id, document, first) + Self::deserialize_inner(alias_id, document) } /// Deserializes a JSON-encoded `StardustDocument` from an Alias Output block. pub fn deserialize_from_block(block: &Block) -> Result { - let (alias_id, document, first) = Self::parse_block(block); - Self::deserialize_inner(&alias_id, document, first) + let (alias_id, document) = Self::parse_block(block); + Self::deserialize_inner(&alias_id, document) } - pub fn deserialize_inner(alias_id: &AliasId, document: &[u8], first: bool) -> Result { + pub fn deserialize_inner(alias_id: &AliasId, document: &[u8]) -> Result { let did: CoreDID = Self::alias_id_to_did(alias_id)?; - - // Replace the placeholder DID in the Document content for the first Alias Output block. - // TODO: maybe _always_ do this replacement in case developers forget to replace it? - if first { - let json = String::from_utf8(document.to_vec()).unwrap(); - let replaced = json.replace(Self::placeholder_did().as_str(), did.as_str()); - StardustDocument::from_json(&replaced).map_err(Into::into) - } else { - StardustDocument::from_json_slice(document).map_err(Into::into) - } + StateMetadataDocument::unpack(document).map(|doc| doc.into_stardust_document(&did)) } } @@ -160,6 +198,14 @@ fn get_alias_output_id_from_payload(payload: &Payload) -> OutputId { } } +impl From for CoreDocument { + fn from(document: StardustDocument) -> Self { + // TODO: convert any additional properties (that cannot be inferred from the Output) into + // JSON Object or an alternative struct in CoreDocument. + document.0 + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/identity_stardust/src/state_metadata/document.rs b/identity_stardust/src/state_metadata/document.rs new file mode 100644 index 0000000000..0fc530bd37 --- /dev/null +++ b/identity_stardust/src/state_metadata/document.rs @@ -0,0 +1,292 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use identity_core::convert::FromJson; +use identity_core::convert::ToJson; +use identity_did::did::CoreDID; +use identity_did::document::CoreDocument; +use once_cell::sync::Lazy; +use serde::Deserialize; +use serde::Serialize; + +use crate::error::Result; +use crate::Error; +use crate::StardustDocument; + +use super::StateMetadataEncoding; +use super::StateMetadataVersion; + +pub(crate) static PLACEHOLDER_DID: Lazy = Lazy::new(|| CoreDID::parse("did:0:0").unwrap()); + +/// Magic bytes used to mark DID documents. +const DID_MARKER: &[u8] = b"DID"; + +/// The DID document as it is contained in the state metadata of an alias output. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub(crate) struct StateMetadataDocument(CoreDocument); + +impl StateMetadataDocument { + /// Transforms the document into a [`StardustDocument`] by replacing all placeholders with `original_did`. + pub fn into_stardust_document(self, original_did: &CoreDID) -> StardustDocument { + let core_doc: CoreDocument = self.0; + let core_document: CoreDocument = core_doc.map( + // Replace placeholder identifiers. + |did| { + if did == PLACEHOLDER_DID.as_ref() { + original_did.clone() + } else { + did + } + }, + // Do not modify properties. + |o| o, + ); + StardustDocument(core_document) + } + + /// Pack a [`StateMetadataDocument`] into bytes, suitable for inclusion in + /// an alias output's state metadata, according to the given `encoding`. + pub fn pack(self, encoding: StateMetadataEncoding) -> Result> { + let encoded_message_data: Vec = match encoding { + StateMetadataEncoding::Json => self.to_json_vec()?, + }; + + // Prepend flags. + let encoded_message_data_with_flags = + add_flags_to_message(encoded_message_data, StateMetadataVersion::CURRENT, encoding); + Ok(encoded_message_data_with_flags) + } + + /// Unpack bytes into a [`StateMetadataDocument`]. + pub fn unpack(data: &[u8]) -> Result { + // Check marker. + let marker: &[u8] = data.get(0..3).ok_or(identity_did::Error::InvalidDocument( + "expected data to have at least length 3", + None, + ))?; + if marker != DID_MARKER { + return Err(Error::InvalidStateMetadata("missing `DID` marker")); + } + + // Check version. + let version: StateMetadataVersion = StateMetadataVersion::try_from(*data.get(3).ok_or( + identity_did::Error::InvalidDocument("expected data to have at least length 4", None), + )?)?; + if version != StateMetadataVersion::V1 { + return Err(Error::InvalidStateMetadata("unsupported version")); + } + + // Decode data. + let encoding: StateMetadataEncoding = StateMetadataEncoding::try_from(*data.get(4).ok_or( + identity_did::Error::InvalidDocument("expected data to have at least length 5", None), + )?)?; + + let inner: &[u8] = data.get(5..).ok_or(identity_did::Error::InvalidDocument( + "expected data to have at least length 6", + None, + ))?; + + match encoding { + StateMetadataEncoding::Json => StateMetadataDocument::from_json_slice(inner).map_err(Into::into), + } + } +} + +/// Prepends the message flags and marker magic bytes to the data in the following order: +/// `[marker, version, encoding, data]`. +fn add_flags_to_message(mut data: Vec, version: StateMetadataVersion, encoding: StateMetadataEncoding) -> Vec { + let mut buffer: Vec = Vec::with_capacity(1 + DID_MARKER.len() + 1 + data.len()); + buffer.extend_from_slice(DID_MARKER); + buffer.push(version as u8); + buffer.push(encoding as u8); + buffer.append(&mut data); + buffer +} + +impl From for StateMetadataDocument { + /// Transforms a [`StardustDocument`] into its state metadata representation by replacing all + /// occurrences of its did with a placeholder. + fn from(document: StardustDocument) -> Self { + let core_document: CoreDocument = CoreDocument::from(document); + let id: CoreDID = core_document.id().clone(); + let core_doc: CoreDocument = core_document.map( + // Replace self-referential identifiers with a placeholder, but not others. + |did| { + if did == id { + PLACEHOLDER_DID.clone() + } else { + did + } + }, + // Do not modify properties. + |o| o, + ); + StateMetadataDocument(core_doc) + } +} + +#[cfg(test)] +mod tests { + use identity_core::common::OneOrSet; + use identity_core::common::Url; + use identity_core::crypto::KeyPair; + use identity_core::crypto::KeyType; + use identity_did::did::CoreDID; + use identity_did::verification::MethodScope; + + use crate::state_metadata::PLACEHOLDER_DID; + use crate::StardustDocument; + use crate::StateMetadataDocument; + use crate::StateMetadataEncoding; + + struct TestSetup { + document: StardustDocument, + original_did: CoreDID, + other_did: CoreDID, + } + + fn test_document() -> TestSetup { + let original_did = + CoreDID::parse("did:stardust:8036235b6b5939435a45d68bcea7890eef399209a669c8c263fac7f5089b2ec6").unwrap(); + let other_did = + CoreDID::parse("did:stardust:71b709dff439f1ac9dd2b9c2e28db0807156b378e13bfa3605ce665aa0d0fdca").unwrap(); + + let mut document: StardustDocument = StardustDocument::new(); + document.tmp_set_id(original_did.clone()); + + let keypair: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); + document + .tmp_add_verification_method( + document.tmp_id().clone(), + &keypair, + "#did-self", + MethodScope::VerificationMethod, + ) + .unwrap(); + + let keypair: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); + document + .tmp_add_verification_method( + other_did.clone(), + &keypair, + "#did-foreign", + MethodScope::authentication(), + ) + .unwrap(); + + document + .tmp_add_service( + document.tmp_id().clone(), + "#my-service", + "RevocationList2030", + identity_did::service::ServiceEndpoint::One(Url::parse("https://example.com/xyzabc").unwrap()), + ) + .unwrap(); + + document + .tmp_add_service( + other_did.clone(), + "#my-foreign-service", + "RevocationList2030", + identity_did::service::ServiceEndpoint::One(Url::parse("https://example.com/0xf4c42e9da").unwrap()), + ) + .unwrap(); + + document + .0 + .also_known_as_mut() + .append(Url::parse("did:example:abc").unwrap()); + + document + .0 + .also_known_as_mut() + .append(Url::parse("did:example:xyz").unwrap()); + + let mut controllers = OneOrSet::new_one(other_did.clone()); + (&mut controllers).append(original_did.clone()); + let mut controllers = Some(controllers); + std::mem::swap(&mut controllers, document.0.controller_mut()); + + TestSetup { + document, + original_did, + other_did, + } + } + + #[test] + fn test_transformation_roundtrip() { + let TestSetup { + document, + original_did, + other_did, + } = test_document(); + + let state_metadata_doc: StateMetadataDocument = StateMetadataDocument::from(document.clone()); + + assert_eq!( + state_metadata_doc + .0 + .resolve_method("#did-self", None) + .unwrap() + .id() + .did(), + PLACEHOLDER_DID.as_ref() + ); + + assert_eq!( + state_metadata_doc + .0 + .resolve_method("#did-foreign", None) + .unwrap() + .id() + .did(), + &other_did + ); + + assert_eq!( + state_metadata_doc + .0 + .service() + .iter() + .find(|service| service.id().fragment().unwrap() == "my-foreign-service") + .unwrap() + .id() + .did(), + &other_did + ); + + assert_eq!( + state_metadata_doc + .0 + .service() + .iter() + .find(|service| service.id().fragment().unwrap() == "my-service") + .unwrap() + .id() + .did(), + PLACEHOLDER_DID.as_ref() + ); + + let controllers = state_metadata_doc.0.controller().unwrap(); + assert_eq!(controllers.get(0).unwrap(), &other_did); + assert_eq!(controllers.get(1).unwrap(), PLACEHOLDER_DID.as_ref()); + + let stardust_document = state_metadata_doc.into_stardust_document(&original_did); + + assert_eq!(stardust_document, document); + } + + #[test] + fn test_packing_roundtrip() { + let TestSetup { document, .. } = test_document(); + + let state_metadata_doc: StateMetadataDocument = StateMetadataDocument::from(document); + + let packed_bytes: Vec = state_metadata_doc.clone().pack(StateMetadataEncoding::Json).unwrap(); + + let unpacked_doc = StateMetadataDocument::unpack(&packed_bytes).unwrap(); + + assert_eq!(state_metadata_doc, unpacked_doc); + } +} diff --git a/identity_stardust/src/state_metadata/encoding.rs b/identity_stardust/src/state_metadata/encoding.rs new file mode 100644 index 0000000000..80e5c8f6b1 --- /dev/null +++ b/identity_stardust/src/state_metadata/encoding.rs @@ -0,0 +1,20 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use num_traits::FromPrimitive; + +use crate::Error; + +/// Indicates the encoding of a DID document in state metadata. +#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, num_derive::FromPrimitive)] +pub enum StateMetadataEncoding { + Json = 0, +} + +impl TryFrom for StateMetadataEncoding { + type Error = Error; + + fn try_from(value: u8) -> Result { + FromPrimitive::from_u8(value).ok_or(Error::InvalidStateMetadata("unsupported encoding")) + } +} diff --git a/identity_stardust/src/state_metadata/mod.rs b/identity_stardust/src/state_metadata/mod.rs new file mode 100644 index 0000000000..78aefda493 --- /dev/null +++ b/identity_stardust/src/state_metadata/mod.rs @@ -0,0 +1,10 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +mod document; +mod encoding; +mod version; + +pub(crate) use document::*; +pub use encoding::*; +pub(crate) use version::*; diff --git a/identity_stardust/src/state_metadata/version.rs b/identity_stardust/src/state_metadata/version.rs new file mode 100644 index 0000000000..82ddc02407 --- /dev/null +++ b/identity_stardust/src/state_metadata/version.rs @@ -0,0 +1,24 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use num_traits::FromPrimitive; + +use crate::Error; + +/// Indicates the version of a DID document in state metadata. +#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, num_derive::FromPrimitive)] +pub(crate) enum StateMetadataVersion { + V1 = 1, +} + +impl StateMetadataVersion { + pub const CURRENT: Self = Self::V1; +} + +impl TryFrom for StateMetadataVersion { + type Error = Error; + + fn try_from(value: u8) -> Result { + FromPrimitive::from_u8(value).ok_or(Error::InvalidStateMetadata("unsupported version number")) + } +} From 5e9adf192bab070563a2536274bd1ab622c92b08 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Jul 2022 14:25:36 +0200 Subject: [PATCH 25/89] Bump terser from 5.7.2 to 5.14.2 in /bindings/wasm (#952) Bumps [terser](https://github.com/terser/terser) from 5.7.2 to 5.14.2. - [Release notes](https://github.com/terser/terser/releases) - [Changelog](https://github.com/terser/terser/blob/master/CHANGELOG.md) - [Commits](https://github.com/terser/terser/commits) --- updated-dependencies: - dependency-name: terser dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- bindings/wasm/package-lock.json | 170 ++++++++++++++++++++++++-------- 1 file changed, 131 insertions(+), 39 deletions(-) diff --git a/bindings/wasm/package-lock.json b/bindings/wasm/package-lock.json index 66c824f904..862294593a 100644 --- a/bindings/wasm/package-lock.json +++ b/bindings/wasm/package-lock.json @@ -147,6 +147,64 @@ "node": ">=10.0.0" } }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.14", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", + "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -532,9 +590,9 @@ "dev": true }, "node_modules/acorn": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.4.1.tgz", - "integrity": "sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA==", + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", + "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -5882,9 +5940,9 @@ } }, "node_modules/source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "dependencies": { "buffer-from": "^1.0.0", @@ -6100,14 +6158,15 @@ "dev": true }, "node_modules/terser": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.7.2.tgz", - "integrity": "sha512-0Omye+RD4X7X69O0eql3lC4Heh/5iLj3ggxR/B5ketZLOtLiOqukUgjw3q4PDnNQbsrkKr3UMypqStQG3XKRvw==", + "version": "5.14.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", + "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", "dev": true, "dependencies": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", "commander": "^2.20.0", - "source-map": "~0.7.2", - "source-map-support": "~0.5.19" + "source-map-support": "~0.5.20" }, "bin": { "terser": "bin/terser" @@ -6149,15 +6208,6 @@ "randombytes": "^2.1.0" } }, - "node_modules/terser/node_modules/source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, "node_modules/test-value": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/test-value/-/test-value-3.0.0.tgz", @@ -7266,6 +7316,55 @@ "integrity": "sha512-Fxt+AfXgjMoin2maPIYzFZnQjAXjAL0PHscM5pRTtatFqB+vZxAM9tLp2Optnuw3QOQC40jTNeGYFOMvyf7v9g==", "dev": true }, + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true + }, + "@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true + }, + "@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.14", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", + "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -7629,9 +7728,9 @@ "dev": true }, "acorn": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.4.1.tgz", - "integrity": "sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA==", + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", + "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", "dev": true }, "acorn-import-assertions": { @@ -11506,9 +11605,9 @@ "dev": true }, "source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -11677,22 +11776,15 @@ "dev": true }, "terser": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.7.2.tgz", - "integrity": "sha512-0Omye+RD4X7X69O0eql3lC4Heh/5iLj3ggxR/B5ketZLOtLiOqukUgjw3q4PDnNQbsrkKr3UMypqStQG3XKRvw==", + "version": "5.14.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", + "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", "dev": true, "requires": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", "commander": "^2.20.0", - "source-map": "~0.7.2", - "source-map-support": "~0.5.19" - }, - "dependencies": { - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true - } + "source-map-support": "~0.5.20" } }, "terser-webpack-plugin": { From 1f7911e3ccf3e709e500cde05cb92e770631d55e Mon Sep 17 00:00:00 2001 From: cycraig Date: Thu, 21 Jul 2022 16:08:29 +0200 Subject: [PATCH 26/89] Add `StardustDocumentMetadata`, implement `StardustDocument` methods (#951) * Update iota-crypto, iota-client * Add StardustDocumentMetadata, implement Document methods * Rename `(un)revoke_credentials` to `(un)revoke_indices` * Update Stardust example for increasing the amount and destroying it * Update Wasm bindings * Fix Rust docs * Revert rename to `(un)revoke_credentials` * Fix formatting --- bindings/wasm/docs/api-reference.md | 32 +- .../wasm/src/account/wasm_account/account.rs | 16 +- bindings/wasm/src/did/wasm_document.rs | 26 +- identity_account/src/account/account.rs | 12 +- identity_account_storage/Cargo.toml | 2 +- .../src/storage/memstore.rs | 4 +- .../src/storage/stronghold.rs | 2 +- .../types/encryption/encryption_algorithm.rs | 2 +- identity_core/Cargo.toml | 2 +- identity_did/src/document/core_document.rs | 112 ++- identity_iota_client/Cargo.toml | 2 +- identity_iota_core/Cargo.toml | 2 +- .../src/document/iota_document.rs | 70 +- identity_stardust/Cargo.toml | 9 +- identity_stardust/examples/create_did.rs | 117 ++- identity_stardust/src/document/mod.rs | 12 + .../src/document/stardust_document.rs | 753 ++++++++++++++++++ .../document/stardust_document_metadata.rs | 49 ++ identity_stardust/src/error.rs | 5 +- identity_stardust/src/lib.rs | 4 +- identity_stardust/src/stardust_document.rs | 218 ----- .../src/state_metadata/document.rs | 136 ++-- 22 files changed, 1123 insertions(+), 464 deletions(-) create mode 100644 identity_stardust/src/document/mod.rs create mode 100644 identity_stardust/src/document/stardust_document.rs create mode 100644 identity_stardust/src/document/stardust_document_metadata.rs delete mode 100644 identity_stardust/src/stardust_document.rs diff --git a/bindings/wasm/docs/api-reference.md b/bindings/wasm/docs/api-reference.md index 4d351c7796..f1c6031ddd 100644 --- a/bindings/wasm/docs/api-reference.md +++ b/bindings/wasm/docs/api-reference.md @@ -237,8 +237,8 @@ publishing to the Tangle. * [.createSignedData(fragment, data, options)](#Account+createSignedData) ⇒ Promise.<any> * [.updateDocumentUnchecked(document)](#Account+updateDocumentUnchecked) ⇒ Promise.<void> * [.fetchDocument()](#Account+fetchDocument) ⇒ Promise.<void> - * [.revokeCredentials(fragment, credentialIndices)](#Account+revokeCredentials) ⇒ Promise.<void> - * [.unrevokeCredentials(fragment, credentialIndices)](#Account+unrevokeCredentials) ⇒ Promise.<void> + * [.revokeCredentials(fragment, indices)](#Account+revokeCredentials) ⇒ Promise.<void> + * [.unrevokeCredentials(fragment, indices)](#Account+unrevokeCredentials) ⇒ Promise.<void> * [.encryptData(plaintext, associated_data, encryption_algorithm, cek_algorithm, public_key)](#Account+encryptData) ⇒ [Promise.<EncryptedData>](#EncryptedData) * [.decryptData(data, encryption_algorithm, cek_algorithm, fragment)](#Account+decryptData) ⇒ Promise.<Uint8Array> * [.attachMethodRelationships(options)](#Account+attachMethodRelationships) ⇒ Promise.<void> @@ -403,29 +403,29 @@ to the identity, to avoid publishing updates that would be ignored. **Kind**: instance method of [Account](#Account) -### account.revokeCredentials(fragment, credentialIndices) ⇒ Promise.<void> +### account.revokeCredentials(fragment, indices) ⇒ Promise.<void> If the document has a `RevocationBitmap` service identified by `fragment`, -revoke all credentials with a `revocationBitmapIndex` in `credentialIndices`. +revoke all specified `indices`. **Kind**: instance method of [Account](#Account) | Param | Type | | --- | --- | | fragment | string | -| credentialIndices | number \| Array.<number> | +| indices | number \| Array.<number> | -### account.unrevokeCredentials(fragment, credentialIndices) ⇒ Promise.<void> +### account.unrevokeCredentials(fragment, indices) ⇒ Promise.<void> If the document has a `RevocationBitmap` service identified by `fragment`, -unrevoke all credentials with a `revocationBitmapIndex` in `credentialIndices`. +unrevoke all specified `indices`. **Kind**: instance method of [Account](#Account) | Param | Type | | --- | --- | | fragment | string | -| credentialIndices | number \| Array.<number> | +| indices | number \| Array.<number> | @@ -1798,8 +1798,8 @@ Deserializes a `DiffMessage` from a JSON object. * [.metadataPreviousMessageId()](#Document+metadataPreviousMessageId) ⇒ string * [.setMetadataPreviousMessageId(value)](#Document+setMetadataPreviousMessageId) * [.proof()](#Document+proof) ⇒ [Proof](#Proof) \| undefined - * [.revokeCredentials(serviceQuery, credentialIndices)](#Document+revokeCredentials) - * [.unrevokeCredentials(serviceQuery, credentialIndices)](#Document+unrevokeCredentials) + * [.revokeCredentials(serviceQuery, indices)](#Document+revokeCredentials) + * [.unrevokeCredentials(serviceQuery, indices)](#Document+unrevokeCredentials) * [.toJSON()](#Document+toJSON) ⇒ any * [.clone()](#Document+clone) ⇒ [Document](#Document) * _static_ @@ -2280,29 +2280,29 @@ Returns a copy of the proof. **Kind**: instance method of [Document](#Document) -### document.revokeCredentials(serviceQuery, credentialIndices) +### document.revokeCredentials(serviceQuery, indices) If the document has a `RevocationBitmap` service identified by `serviceQuery`, -revoke all credentials with a revocationBitmapIndex in `credentialIndices`. +revoke all specified `indices`. **Kind**: instance method of [Document](#Document) | Param | Type | | --- | --- | | serviceQuery | [DIDUrl](#DIDUrl) \| string | -| credentialIndices | number \| Array.<number> | +| indices | number \| Array.<number> | -### document.unrevokeCredentials(serviceQuery, credentialIndices) +### document.unrevokeCredentials(serviceQuery, indices) If the document has a `RevocationBitmap` service identified by `serviceQuery`, -unrevoke all credentials with a revocationBitmapIndex in `credentialIndices`. +unrevoke all specified `indices`. **Kind**: instance method of [Document](#Document) | Param | Type | | --- | --- | | serviceQuery | [DIDUrl](#DIDUrl) \| string | -| credentialIndices | number \| Array.<number> | +| indices | number \| Array.<number> | diff --git a/bindings/wasm/src/account/wasm_account/account.rs b/bindings/wasm/src/account/wasm_account/account.rs index 023bf1dac2..e212c20d81 100644 --- a/bindings/wasm/src/account/wasm_account/account.rs +++ b/bindings/wasm/src/account/wasm_account/account.rs @@ -276,18 +276,18 @@ impl WasmAccount { } /// If the document has a `RevocationBitmap` service identified by `fragment`, - /// revoke all credentials with a `revocationBitmapIndex` in `credentialIndices`. + /// revoke all specified `indices`. #[wasm_bindgen(js_name = revokeCredentials)] #[allow(non_snake_case)] - pub fn revoke_credentials(&mut self, fragment: String, credentialIndices: UOneOrManyNumber) -> PromiseVoid { + pub fn revoke_credentials(&mut self, fragment: String, indices: UOneOrManyNumber) -> PromiseVoid { let account = self.0.clone(); future_to_promise(async move { - let credentials_indices: OneOrMany = credentialIndices.into_serde().wasm_result()?; + let indices: OneOrMany = indices.into_serde().wasm_result()?; account .as_ref() .borrow_mut() - .revoke_credentials(&fragment, credentials_indices.as_slice()) + .revoke_credentials(&fragment, indices.as_slice()) .await .map(|_| JsValue::undefined()) .wasm_result() @@ -296,18 +296,18 @@ impl WasmAccount { } /// If the document has a `RevocationBitmap` service identified by `fragment`, - /// unrevoke all credentials with a `revocationBitmapIndex` in `credentialIndices`. + /// unrevoke all specified `indices`. #[wasm_bindgen(js_name = unrevokeCredentials)] #[allow(non_snake_case)] - pub fn unrevoke_credentials(&mut self, fragment: String, credentialIndices: UOneOrManyNumber) -> PromiseVoid { + pub fn unrevoke_credentials(&mut self, fragment: String, indices: UOneOrManyNumber) -> PromiseVoid { let account = self.0.clone(); future_to_promise(async move { - let credentials_indices: OneOrMany = credentialIndices.into_serde().wasm_result()?; + let indices: OneOrMany = indices.into_serde().wasm_result()?; account .as_ref() .borrow_mut() - .unrevoke_credentials(&fragment, credentials_indices.as_slice()) + .unrevoke_credentials(&fragment, indices.as_slice()) .await .map(|_| JsValue::undefined()) .wasm_result() diff --git a/bindings/wasm/src/did/wasm_document.rs b/bindings/wasm/src/did/wasm_document.rs index 1c8b037743..a93b35a60b 100644 --- a/bindings/wasm/src/did/wasm_document.rs +++ b/bindings/wasm/src/did/wasm_document.rs @@ -640,35 +640,25 @@ impl WasmDocument { } /// If the document has a `RevocationBitmap` service identified by `serviceQuery`, - /// revoke all credentials with a revocationBitmapIndex in `credentialIndices`. + /// revoke all specified `indices`. #[wasm_bindgen(js_name = revokeCredentials)] #[allow(non_snake_case)] - pub fn revoke_credentials(&mut self, serviceQuery: &UDIDUrlQuery, credentialIndices: UOneOrManyNumber) -> Result<()> { + pub fn revoke_credentials(&mut self, serviceQuery: &UDIDUrlQuery, indices: UOneOrManyNumber) -> Result<()> { let query: String = serviceQuery.into_serde().wasm_result()?; - let credentials_indices: OneOrMany = credentialIndices.into_serde().wasm_result()?; + let indices: OneOrMany = indices.into_serde().wasm_result()?; - self - .0 - .revoke_credentials(&query, credentials_indices.as_slice()) - .wasm_result() + self.0.revoke_credentials(&query, indices.as_slice()).wasm_result() } /// If the document has a `RevocationBitmap` service identified by `serviceQuery`, - /// unrevoke all credentials with a revocationBitmapIndex in `credentialIndices`. + /// unrevoke all specified `indices`. #[wasm_bindgen(js_name = unrevokeCredentials)] #[allow(non_snake_case)] - pub fn unrevoke_credentials( - &mut self, - serviceQuery: &UDIDUrlQuery, - credentialIndices: UOneOrManyNumber, - ) -> Result<()> { + pub fn unrevoke_credentials(&mut self, serviceQuery: &UDIDUrlQuery, indices: UOneOrManyNumber) -> Result<()> { let query: String = serviceQuery.into_serde().wasm_result()?; - let credentials_indices: OneOrMany = credentialIndices.into_serde().wasm_result()?; + let indices: OneOrMany = indices.into_serde().wasm_result()?; - self - .0 - .unrevoke_credentials(&query, credentials_indices.as_slice()) - .wasm_result() + self.0.unrevoke_credentials(&query, indices.as_slice()).wasm_result() } // =========================================================================== diff --git a/identity_account/src/account/account.rs b/identity_account/src/account/account.rs index 5f4a0f595b..5365a783bc 100644 --- a/identity_account/src/account/account.rs +++ b/identity_account/src/account/account.rs @@ -566,13 +566,13 @@ mod account_revocation { C: SharedPtr, { /// If the document has a [`RevocationBitmap`][identity_did::revocation::RevocationBitmap] service identified by - /// `fragment`, revoke all credentials with a `revocationBitmapIndex` in `credential_indices`. - pub async fn revoke_credentials(&mut self, fragment: &str, credential_indices: &[u32]) -> Result<()> { + /// `fragment`, revoke all specified `indices`. + pub async fn revoke_credentials(&mut self, fragment: &str, indices: &[u32]) -> Result<()> { // Find the service to be updated. let mut service_id: IotaDIDUrl = self.did().to_url(); service_id.set_fragment(Some(fragment))?; - self.document.revoke_credentials(&service_id, credential_indices)?; + self.document.revoke_credentials(&service_id, indices)?; self.increment_actions(); self.publish_internal(false, PublishOptions::default()).await?; @@ -580,13 +580,13 @@ mod account_revocation { } /// If the document has a [`RevocationBitmap`][identity_did::revocation::RevocationBitmap] service identified by - /// `fragment`, unrevoke all credentials with a `revocationBitmapIndex` in `credential_indices`. - pub async fn unrevoke_credentials(&mut self, fragment: &str, credential_indices: &[u32]) -> Result<()> { + /// `fragment`, unrevoke all specified `indices`. + pub async fn unrevoke_credentials(&mut self, fragment: &str, indices: &[u32]) -> Result<()> { // Find the service to be updated. let mut service_id: IotaDIDUrl = self.did().to_url(); service_id.set_fragment(Some(fragment))?; - self.document.unrevoke_credentials(&service_id, credential_indices)?; + self.document.unrevoke_credentials(&service_id, indices)?; self.increment_actions(); self.publish_internal(false, PublishOptions::default()).await?; diff --git a/identity_account_storage/Cargo.toml b/identity_account_storage/Cargo.toml index bb07c6d839..ebdf6211e8 100644 --- a/identity_account_storage/Cargo.toml +++ b/identity_account_storage/Cargo.toml @@ -19,7 +19,7 @@ hashbrown = { version = "0.11", features = ["serde"] } identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } identity_iota_core = { version = "=0.6.0", path = "../identity_iota_core", default-features = false } -iota-crypto = { version = ">=0.7, <0.10", default-features = false, features = ["hmac", "pbkdf", "sha", "std", "aes", "aes-kw"] } +iota-crypto = { version = "0.12.1", default-features = false, features = ["hmac", "pbkdf", "sha", "std", "aes-gcm", "aes-kw"] } iota_stronghold = { version = "0.6.4", default-features = false, features = ["std"], optional = true } once_cell = { version = "1.7", default-features = false, features = ["std"], optional = true } parking_lot = { version = "0.12" } diff --git a/identity_account_storage/src/storage/memstore.rs b/identity_account_storage/src/storage/memstore.rs index 48303a220d..ef27a591d2 100644 --- a/identity_account_storage/src/storage/memstore.rs +++ b/identity_account_storage/src/storage/memstore.rs @@ -6,7 +6,7 @@ use core::fmt::Formatter; use async_trait::async_trait; #[cfg(feature = "encryption")] -use crypto::ciphers::aes::Aes256Gcm; +use crypto::ciphers::aes_gcm::Aes256Gcm; #[cfg(feature = "encryption")] use crypto::ciphers::aes_kw::Aes256Kw; #[cfg(feature = "encryption")] @@ -419,7 +419,7 @@ mod memstore_encryption { use crate::types::EncryptionAlgorithm; use crate::Error; use crate::Result; - use crypto::ciphers::aes::Aes256Gcm; + use crypto::ciphers::aes_gcm::Aes256Gcm; use crypto::ciphers::traits::Aead; use crypto::hashes::sha::Sha256; use crypto::hashes::Digest; diff --git a/identity_account_storage/src/storage/stronghold.rs b/identity_account_storage/src/storage/stronghold.rs index 715c11d4f1..3a9762dedd 100644 --- a/identity_account_storage/src/storage/stronghold.rs +++ b/identity_account_storage/src/storage/stronghold.rs @@ -4,7 +4,7 @@ use std::collections::BTreeSet; use async_trait::async_trait; -use crypto::ciphers::aes::Aes256Gcm; +use crypto::ciphers::aes_gcm::Aes256Gcm; use crypto::ciphers::traits::Aead; use futures::executor; use identity_core::convert::FromJson; diff --git a/identity_account_storage/src/types/encryption/encryption_algorithm.rs b/identity_account_storage/src/types/encryption/encryption_algorithm.rs index 71343ccfd2..fe6ee02fe3 100644 --- a/identity_account_storage/src/types/encryption/encryption_algorithm.rs +++ b/identity_account_storage/src/types/encryption/encryption_algorithm.rs @@ -1,7 +1,7 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use crypto::ciphers::aes::Aes256Gcm; +use crypto::ciphers::aes_gcm::Aes256Gcm; use crypto::ciphers::traits::Aead; use serde::Deserialize; use serde::Serialize; diff --git a/identity_core/Cargo.toml b/identity_core/Cargo.toml index 0abdf7fbc3..a76819477c 100644 --- a/identity_core/Cargo.toml +++ b/identity_core/Cargo.toml @@ -23,7 +23,7 @@ url = { version = "2.2", default-features = false, features = ["serde"] } zeroize = { version = "1.4", default-features = false } [dependencies.iota-crypto] -version = ">=0.7, <0.10" +version = "0.12.1" default-features = false features = ["ed25519", "random", "sha", "x25519"] diff --git a/identity_did/src/document/core_document.rs b/identity_did/src/document/core_document.rs index b3a855f147..2a90e6afbb 100644 --- a/identity_did/src/document/core_document.rs +++ b/identity_did/src/document/core_document.rs @@ -665,7 +665,8 @@ impl CoreDocument where D: DID + KeyComparable, { - /// Verifies the signature of the provided data. + /// Verifies the signature of the provided `data` was created using a verification method + /// in this DID Document. /// /// # Errors /// @@ -811,32 +812,26 @@ mod core_document_revocation { D: DID + KeyComparable, { /// If the document has a [`RevocationBitmap`] service identified by `service_query`, - /// revoke all credentials with a `revocationBitmapIndex` in `credential_indices`. - pub fn revoke_credentials<'query, 'me, Q>(&mut self, service_query: Q, credential_indices: &[u32]) -> Result<()> + /// revoke all specified `indices`. + pub fn revoke_credentials<'query, 'me, Q>(&mut self, service_query: Q, indices: &[u32]) -> Result<()> where Q: Into>, { self.update_revocation_bitmap(service_query, |revocation_bitmap| { - // Revoke all given credential indices. - for credential in credential_indices { + for credential in indices { revocation_bitmap.revoke(*credential); } }) } /// If the document has a [`RevocationBitmap`] service identified by `service_query`, - /// unrevoke all credentials with a `revocationBitmapIndex` in `credential_indices`. - pub fn unrevoke_credentials<'query, 'me, Q>( - &'me mut self, - service_query: Q, - credential_indices: &[u32], - ) -> Result<()> + /// unrevoke all specified `indices`. + pub fn unrevoke_credentials<'query, 'me, Q>(&'me mut self, service_query: Q, indices: &[u32]) -> Result<()> where Q: Into>, { self.update_revocation_bitmap(service_query, |revocation_bitmap| { - // Unrevoke all given credential indices. - for credential in credential_indices { + for credential in indices { revocation_bitmap.unrevoke(*credential); } }) @@ -898,15 +893,9 @@ where #[cfg(test)] mod tests { - use crate::did::CoreDID; - use crate::did::DID; - use crate::document::CoreDocument; - use crate::utils::Queryable; use crate::verification::MethodData; - use crate::verification::MethodRelationship; - use crate::verification::MethodScope; - use crate::verification::MethodType; - use crate::verification::VerificationMethod; + + use super::*; fn controller() -> CoreDID { "did:example:1234".parse().unwrap() @@ -937,6 +926,35 @@ mod tests { .unwrap() } + #[test] + fn test_controller() { + // One controller. + { + let mut document: CoreDocument = document(); + let expected: CoreDID = CoreDID::parse("did:example:one1234").unwrap(); + *document.controller_mut() = Some(OneOrSet::new_one(expected.clone())); + assert_eq!(document.controller().unwrap().as_slice(), &[expected]); + // Unset. + *document.controller_mut() = None; + assert!(document.controller().is_none()); + } + + // Many controllers. + { + let mut document: CoreDocument = document(); + let expected_controllers: Vec = vec![ + CoreDID::parse("did:example:many1234").unwrap(), + CoreDID::parse("did:example:many4567").unwrap(), + CoreDID::parse("did:example:many8910").unwrap(), + ]; + *document.controller_mut() = Some(expected_controllers.clone().try_into().unwrap()); + assert_eq!(document.controller().unwrap().as_slice(), &expected_controllers); + // Unset. + *document.controller_mut() = None; + assert!(document.controller().is_none()); + } + } + #[rustfmt::skip] #[test] fn test_resolve_method() { @@ -1189,4 +1207,56 @@ mod tests { assert!(document.capability_delegation().query(method3.id()).is_none()); assert!(document.verification_method().query(method3.id()).is_none()); } + + #[cfg(feature = "revocation-bitmap")] + #[test] + fn test_revocation() { + let mut document: CoreDocument = document(); + let indices_1 = [3, 9, 254, 65536]; + let indices_2 = [2, 15, 1337, 1000]; + + let service_id = document.id().to_url().join("#revocation-service").unwrap(); + + // The methods error if the service doesn't exist. + assert!(document.revoke_credentials(&service_id, &indices_2).is_err()); + assert!(document.unrevoke_credentials(&service_id, &indices_2).is_err()); + + // Add service with indices_1 already revoked. + let mut bitmap: crate::revocation::RevocationBitmap = crate::revocation::RevocationBitmap::new(); + for index in indices_1.iter() { + bitmap.revoke(*index); + } + assert!(document.service_mut().append( + Service::builder(Object::new()) + .id(service_id.clone()) + .type_(crate::revocation::RevocationBitmap::TYPE) + .service_endpoint(bitmap.to_endpoint().unwrap()) + .build() + .unwrap() + )); + + // Revoke indices_2. + document.revoke_credentials(&service_id, &indices_2).unwrap(); + let service: &Service = document.resolve_service(&service_id).unwrap(); + let decoded_bitmap: crate::revocation::RevocationBitmap = service.try_into().unwrap(); + + // We expect all indices to be revoked now. + for index in indices_1.iter().chain(indices_2.iter()) { + assert!(decoded_bitmap.is_revoked(*index)); + } + + // Unrevoke indices_1. + document.unrevoke_credentials(&service_id, &indices_1).unwrap(); + + let service: &Service = document.resolve_service(&service_id).unwrap(); + let decoded_bitmap: crate::revocation::RevocationBitmap = service.try_into().unwrap(); + + // Expect indices_2 to be revoked, but not indices_1. + for index in indices_2 { + assert!(decoded_bitmap.is_revoked(index)); + } + for index in indices_1 { + assert!(!decoded_bitmap.is_revoked(index)); + } + } } diff --git a/identity_iota_client/Cargo.toml b/identity_iota_client/Cargo.toml index c69ac99918..5ad3e1292e 100644 --- a/identity_iota_client/Cargo.toml +++ b/identity_iota_client/Cargo.toml @@ -40,7 +40,7 @@ default-features = false features = ["wasm"] [dependencies.iota-crypto] -version = ">=0.7, <0.10" +version = "0.12.1" default-features = false features = ["blake2b"] diff --git a/identity_iota_core/Cargo.toml b/identity_iota_core/Cargo.toml index 55d0db3571..cc53afa939 100644 --- a/identity_iota_core/Cargo.toml +++ b/identity_iota_core/Cargo.toml @@ -20,7 +20,7 @@ strum = { version = "0.24.0", default-features = false, features = ["std", "deri thiserror = { version = "1.0", default-features = false } [dependencies.iota-crypto] -version = ">=0.7, <0.10" +version = "0.12.1" default-features = false features = ["blake2b"] diff --git a/identity_iota_core/src/document/iota_document.rs b/identity_iota_core/src/document/iota_document.rs index 9ed8315efa..db47e19ed6 100644 --- a/identity_iota_core/src/document/iota_document.rs +++ b/identity_iota_core/src/document/iota_document.rs @@ -446,7 +446,7 @@ impl IotaDocument { /// /// # Errors /// - /// Fails if an unsupported verification method is used, document + /// Fails if an unsupported verification method is used, data /// serialization fails, or the verification operation fails. pub fn verify_data(&self, data: &X, options: &VerifierOptions) -> Result<()> where @@ -667,8 +667,7 @@ mod iota_document_revocation { impl IotaDocument { /// If the document has a [`RevocationBitmap`](identity_did::revocation::RevocationBitmap) - /// service identified by `service_query`, revoke all credentials with a - /// `revocationBitmapIndex` in `credential_indices`. + /// service identified by `service_query`, revoke all specified `indices`. pub fn revoke_credentials<'query, 'me, Q>(&mut self, service_query: Q, credential_indices: &[u32]) -> Result<()> where Q: Into>, @@ -680,8 +679,7 @@ mod iota_document_revocation { } /// If the document has a [`RevocationBitmap`](identity_did::revocation::RevocationBitmap) - /// service with an id by `service_query`, unrevoke all credentials with a - /// `revocationBitmapIndex` in `credential_indices`. + /// service with an id by `service_query`, unrevoke all specified `indices`. pub fn unrevoke_credentials<'query, 'me, Q>( &'me mut self, service_query: Q, @@ -760,7 +758,6 @@ mod tests { use identity_core::crypto::KeyType; use identity_core::utils::BaseEncoding; use identity_did::did::DID; - use identity_did::revocation::RevocationBitmap; use identity_did::verifiable::VerifiableProperties; use identity_did::verification::MethodData; @@ -1680,65 +1677,4 @@ mod tests { assert!(insertion_result.is_err()); assert_eq!(doc1, doc2); } - - #[test] - fn test_revocation() { - let keypair: KeyPair = generate_testkey(); - let mut document: IotaDocument = IotaDocument::new(&keypair).unwrap(); - let indices_1 = [3, 9, 254, 65536]; - let indices_2 = [2, 15, 1337, 1000]; - - let service_id: IotaDIDUrl = document.id().to_url().join("#revocation-service").unwrap(); - - // The method errors if the service doesn't exist. - assert!(document.revoke_credentials(&service_id, &indices_2).is_err()); - - let mut bitmap: RevocationBitmap = RevocationBitmap::new(); - for index in indices_1.iter() { - bitmap.revoke(*index); - } - - document.insert_service( - IotaService::builder(Object::new()) - .id(service_id.clone()) - .type_(RevocationBitmap::TYPE) - .service_endpoint(bitmap.to_endpoint().unwrap()) - .build() - .unwrap(), - ); - - document.revoke_credentials(&service_id, &indices_2).unwrap(); - - let service: &IotaService = document - .service() - .iter() - .find(|service| service.id() == &service_id) - .unwrap(); - - let decoded_bitmap: RevocationBitmap = service.try_into().unwrap(); - - // We expect all indices to be revoked now. - for index in indices_1.iter().chain(indices_2.iter()) { - assert!(decoded_bitmap.is_revoked(*index)); - } - - document.unrevoke_credentials(&service_id, &indices_1).unwrap(); - - let service: &IotaService = document - .service() - .iter() - .find(|service| service.id() == &service_id) - .unwrap(); - - let decoded_bitmap: RevocationBitmap = service.try_into().unwrap(); - - // We expect indices_2 to be revoked, but not indices_1. - for index in indices_2 { - assert!(decoded_bitmap.is_revoked(index)); - } - - for index in indices_1 { - assert!(!decoded_bitmap.is_revoked(index)); - } - } } diff --git a/identity_stardust/Cargo.toml b/identity_stardust/Cargo.toml index a7167c8356..a431737f50 100644 --- a/identity_stardust/Cargo.toml +++ b/identity_stardust/Cargo.toml @@ -25,13 +25,13 @@ thiserror = { version = "1.0", default-features = false } [dependencies.iota-client] git = "https://github.com/iotaledger/iota.rs" -rev = "4e7db070a05321c4bd7579acdcc74436865235c0" # develop branch, 2022-07-11 +rev = "2a35c7affe15034fd1b82884966269882bffb23b" # develop branch, 2022-07-18 features = ["tls"] default-features = false [dev-dependencies] anyhow = { version = "1.0.57" } -iota-crypto = { version = "0.11.0", default-features = false, features = ["bip39", "bip39-en"] } +iota-crypto = { version = "0.12.1", default-features = false, features = ["bip39", "bip39-en"] } proptest = { version = "1.0.0", default-features = false, features = ["std"] } tokio = { version = "1.17.0", default-features = false, features = ["macros"] } @@ -40,3 +40,8 @@ tokio = { version = "1.17.0", default-features = false, features = ["macros"] } # RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features --no-deps --workspace --open all-features = true rustdoc-args = ["--cfg", "docsrs"] + +[features] +default = ["revocation-bitmap"] +# Enables revocation with `RevocationBitmap2022`. +revocation-bitmap = ["identity_did/revocation-bitmap"] diff --git a/identity_stardust/examples/create_did.rs b/identity_stardust/examples/create_did.rs index 9b99e1cd8a..4e18ecf0b7 100644 --- a/identity_stardust/examples/create_did.rs +++ b/identity_stardust/examples/create_did.rs @@ -1,20 +1,26 @@ -use iota_client::bee_block::output::feature::IssuerFeature; -use iota_client::bee_block::output::feature::MetadataFeature; -use iota_client::bee_block::output::feature::SenderFeature; -use iota_client::bee_block::output::unlock_condition::GovernorAddressUnlockCondition; -use iota_client::bee_block::output::unlock_condition::StateControllerAddressUnlockCondition; -use iota_client::bee_block::output::unlock_condition::UnlockCondition; -use iota_client::bee_block::output::AliasId; -use iota_client::bee_block::output::AliasOutputBuilder; -use iota_client::bee_block::output::ByteCostConfig; -use iota_client::bee_block::output::Feature; -use iota_client::bee_block::output::Output; +use identity_core::crypto::KeyPair; +use identity_core::crypto::KeyType; +use identity_did::verification::MethodScope; +use iota_client::block::output::feature::IssuerFeature; +use iota_client::block::output::feature::MetadataFeature; +use iota_client::block::output::feature::SenderFeature; +use iota_client::block::output::unlock_condition::AddressUnlockCondition; +use iota_client::block::output::unlock_condition::GovernorAddressUnlockCondition; +use iota_client::block::output::unlock_condition::StateControllerAddressUnlockCondition; +use iota_client::block::output::unlock_condition::UnlockCondition; +use iota_client::block::output::AliasId; +use iota_client::block::output::AliasOutputBuilder; +use iota_client::block::output::BasicOutputBuilder; +use iota_client::block::output::ByteCostConfig; +use iota_client::block::output::Feature; +use iota_client::block::output::Output; use iota_client::constants::SHIMMER_TESTNET_BECH32_HRP; use iota_client::secret::mnemonic::MnemonicSecretManager; use iota_client::secret::SecretManager; use iota_client::Client; use identity_stardust::StardustDocument; +use identity_stardust::StardustVerificationMethod; // PROBLEMS SO FAR: // 1) Alias Id is inferred from the block, so we have to use a placeholder DID for creation. @@ -57,8 +63,7 @@ async fn main() -> anyhow::Result<()> { let client = Client::builder() .with_node(endpoint)? .with_node_sync_disabled() - .finish() - .await?; + .finish()?; let address = client.get_addresses(&secret_manager).with_range(0..1).get_raw().await?[0]; let address_bech32 = address.to_bech32(SHIMMER_TESTNET_BECH32_HRP); @@ -100,7 +105,7 @@ async fn main() -> anyhow::Result<()> { println!("Deposit amount: {}", alias_output.amount()); // Publish to the Tangle ledger. - let block = client + let block1 = client .block() .with_secret_manager(&secret_manager) .with_outputs(vec![alias_output])? @@ -108,12 +113,12 @@ async fn main() -> anyhow::Result<()> { .await?; println!( "Transaction with new alias output sent: {endpoint}/api/v2/blocks/{}", - block.id() + block1.id() ); - let _ = client.retry_until_included(&block.id(), None, None).await?; + let _ = client.retry_until_included(&block1.id(), None, None).await?; - // Infer DID from Alias Output block. - let did = StardustDocument::did_from_block(&block)?; + // Infer DID from the Alias Output block. + let did = StardustDocument::did_from_block(&block1)?; println!("DID: {did}"); // =========================================================================== @@ -127,14 +132,14 @@ async fn main() -> anyhow::Result<()> { println!("Alias ID: {alias_id}"); // Query Indexer INX Plugin for the Output of the Alias ID. - let output_id = client.alias_output_id(alias_id).await?; - println!("Output ID: {output_id}"); - let response = client.get_output(&output_id).await?; + let alias_output_id = client.alias_output_id(alias_id).await?; + println!("Output ID: {alias_output_id}"); + let response = client.get_output(&alias_output_id).await?; let output = Output::try_from(&response.output)?; println!("Output: {output:?}"); // The resolved DID Document replaces the placeholder DID with the correct one. - let resolved_document = StardustDocument::deserialize_from_output(&alias_id, &output)?; + let resolved_document = StardustDocument::deserialize_from_output(&did, &output)?; println!("Resolved Document: {resolved_document:#}"); let alias_output = match output { @@ -143,34 +148,80 @@ async fn main() -> anyhow::Result<()> { }?; // =========================================================================== - // Step 4: Publish an updated Alias ID. (optional) + // Step 4: Publish an updated Alias Output. (optional) // =========================================================================== - // TODO: we could always publish twice on creation to populate the DID (could fail), - // or just infer the DID during resolution (safer). - // Update the Alias Output to contain an explicit ID and DID. - let updated_alias_output = AliasOutputBuilder::from(&alias_output) // Not adding any content, previous amount will cover the deposit. + // Add a new Ed25519 verification method to the DID Document for authentication. (optional) + let mut updated_document = resolved_document.clone(); + let keypair: KeyPair = KeyPair::new(KeyType::Ed25519)?; + let method = StardustVerificationMethod::new( + updated_document.id().clone(), + keypair.type_(), + keypair.public(), + "#key-0", + )?; + updated_document.insert_method(method, MethodScope::authentication())?; + + // Update the Alias Output to contain an explicit Alias ID, and the updated DID Document. + let byte_cost_config: ByteCostConfig = client.get_byte_cost_config().await?; + let updated_alias_output = AliasOutputBuilder::from(&alias_output) + // Set the deposit to the new minimum covering the increased size of the DID Document. + .with_minimum_storage_deposit(byte_cost_config) // Set the explicit Alias ID. .with_alias_id(alias_id) - // State controller updates increment the state index. + // Set the updated DID Document. + .with_state_metadata(updated_document.pack()?) + // State controller updates must increment the state index. .with_state_index(alias_output.state_index() + 1) .finish_output()?; println!("Updated output: {updated_alias_output:?}"); - let block = client + let block2 = client .block() .with_secret_manager(&secret_manager) - .with_input(output_id.into())? + // Omit inputs so it automatically selects a Basic Output to cover the increased amount. + // .with_input(alias_output_id.into())? .with_outputs(vec![updated_alias_output])? .finish() .await?; println!( - "Transaction with alias id set sent: {endpoint}/api/v2/blocks/{}", - block.id() + "Transaction with updated Alias Output sent: {endpoint}/api/v2/blocks/{}", + block2.id() ); - let _ = client.retry_until_included(&block.id(), None, None).await?; + let _ = client.retry_until_included(&block2.id(), None, None).await?; + + // =========================================================================== + // Step 5: Destroy Alias Output. (optional) + // =========================================================================== + + // Query Indexer INX Plugin for the latest Output of the Alias ID. + let alias_output_id = client.alias_output_id(alias_id).await?; + let response = client.get_output(&alias_output_id).await?; + let alias_output = Output::try_from(&response.output)?; + + // Consume the Alias Output containing the DID Document, sending its tokens to a new Basic Output. + // WARNING: this destroys the DID Document and renders it permanently unrecoverable. + let basic_output = BasicOutputBuilder::new_with_amount(alias_output.amount())? + .add_unlock_condition(UnlockCondition::Address(AddressUnlockCondition::new(address))) + .finish_output()?; + let block3 = client + .block() + .with_secret_manager(&secret_manager) + .with_input(alias_output_id.into())? + .with_outputs(vec![basic_output])? + .finish() + .await?; + + println!( + "Transaction destroying Alias Output sent: {endpoint}/api/v2/blocks/{}", + block3.id() + ); + let _ = client.retry_until_included(&block3.id(), None, None).await?; + + // Consolidate amounts in separate Basic Outputs into a single one. + client.consolidate_funds(&secret_manager, 0, 0..1).await?; Ok(()) } diff --git a/identity_stardust/src/document/mod.rs b/identity_stardust/src/document/mod.rs new file mode 100644 index 0000000000..2a03721617 --- /dev/null +++ b/identity_stardust/src/document/mod.rs @@ -0,0 +1,12 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +pub use stardust_document::StardustCoreDocument; +pub use stardust_document::StardustDIDUrl; +pub use stardust_document::StardustDocument; +pub use stardust_document::StardustService; +pub use stardust_document::StardustVerificationMethod; +pub use stardust_document_metadata::StardustDocumentMetadata; + +mod stardust_document; +mod stardust_document_metadata; diff --git a/identity_stardust/src/document/stardust_document.rs b/identity_stardust/src/document/stardust_document.rs new file mode 100644 index 0000000000..c7829ba193 --- /dev/null +++ b/identity_stardust/src/document/stardust_document.rs @@ -0,0 +1,753 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use core::fmt; +use core::fmt::Debug; +use core::fmt::Display; +use std::str::FromStr; + +use identity_core::common::Object; +use identity_core::common::OneOrSet; +use identity_core::common::OrderedSet; +use identity_core::common::Url; +use identity_core::convert::FmtJson; +use identity_core::crypto::GetSignature; +use identity_core::crypto::PrivateKey; +use identity_core::crypto::ProofOptions; +use identity_core::crypto::SetSignature; +use identity_core::utils::Base; +use identity_core::utils::BaseEncoding; +use identity_did::did::CoreDID; +use identity_did::did::DIDUrl; +use identity_did::did::DID; +use identity_did::document::CoreDocument; +use identity_did::document::Document; +use identity_did::service::Service; +use identity_did::utils::DIDUrlQuery; +use identity_did::verifiable::DocumentSigner; +use identity_did::verifiable::VerifierOptions; +use identity_did::verification::MethodRelationship; +use identity_did::verification::MethodScope; +use identity_did::verification::MethodUriType; +use identity_did::verification::TryMethod; +use identity_did::verification::VerificationMethod; +use iota_client::block::output::AliasId; +use iota_client::block::output::Output; +use iota_client::block::output::OutputId; +use iota_client::block::payload::transaction::TransactionEssence; +use iota_client::block::payload::Payload; +use iota_client::block::Block; +use serde::Deserialize; +use serde::Serialize; + +use crate::document::stardust_document_metadata::StardustDocumentMetadata; +use crate::error::Result; +use crate::state_metadata::StateMetadataEncoding; +use crate::state_metadata::PLACEHOLDER_DID; +use crate::StateMetadataDocument; + +// TODO: replace with StardustDID struct. +type StardustDID = CoreDID; + +/// A DID URL conforming to the IOTA DID method specification. +/// +/// See [`DIDUrl`]. +// TODO: move to `did` module. +pub type StardustDIDUrl = DIDUrl; + +/// A [`VerificationMethod`] adhering to the IOTA DID method specification. +pub type StardustVerificationMethod = VerificationMethod; + +/// A [`Service`] adhering to the IOTA DID method specification. +pub type StardustService = Service; + +/// A [`CoreDocument`] whose fields adhere to the IOTA DID method specification. +pub type StardustCoreDocument = CoreDocument; + +/// A DID Document adhering to the IOTA DID method specification. +/// +/// This extends [`CoreDocument`]. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct StardustDocument { + #[serde(rename = "doc")] + pub(crate) document: StardustCoreDocument, + #[serde(rename = "meta")] + pub metadata: StardustDocumentMetadata, +} + +impl StardustDocument { + // =========================================================================== + // Constructors + // =========================================================================== + + /// Constructs an empty DID Document with a [`StardustDocument::placeholder_did`] identifier. + // TODO: always take Option or `new_with_options` for a particular network? + pub fn new() -> Self { + Self::new_with_id(Self::placeholder_did().clone()) + } + + /// Constructs an empty DID Document with the given identifier. + pub fn new_with_id(id: StardustDID) -> Self { + // PANIC: constructing an empty DID Document is infallible, caught by tests otherwise. + let document: StardustCoreDocument = CoreDocument::builder(Object::default()) + .id(id) + .build() + .expect("empty StardustDocument constructor failed"); + let metadata: StardustDocumentMetadata = StardustDocumentMetadata::new(); + Self { document, metadata } + } + + // =========================================================================== + // Properties + // =========================================================================== + + /// Returns the placeholder DID of newly constructed DID Documents, + /// `"did:0:0"`. + // TODO: generalise to take network name? + pub fn placeholder_did() -> &'static StardustDID { + &PLACEHOLDER_DID + } + + /// Returns the DID document identifier. + pub fn id(&self) -> &StardustDID { + self.document.id() + } + + /// Returns a reference to the DID controllers. + /// + /// NOTE: controllers are determined by the `state_controller` unlock condition of the output + /// during resolution and are omitted when publishing. + pub fn controller(&self) -> Option<&OneOrSet> { + self.document.controller() + } + + /// Returns a reference to the `alsoKnownAs` set. + pub fn also_known_as(&self) -> &OrderedSet { + self.document.also_known_as() + } + + /// Returns a mutable reference to the `alsoKnownAs` set. + pub fn also_known_as_mut(&mut self) -> &mut OrderedSet { + self.document.also_known_as_mut() + } + + /// Returns a reference to the underlying [`StardustCoreDocument`]. + pub fn core_document(&self) -> &StardustCoreDocument { + &self.document + } + + /// Returns a mutable reference to the underlying [`StardustCoreDocument`]. + /// + /// WARNING: mutating the inner document directly bypasses restrictions and + /// may have undesired consequences. + pub fn core_document_mut(&mut self) -> &mut StardustCoreDocument { + &mut self.document + } + + /// Returns a reference to the custom DID Document properties. + pub fn properties(&self) -> &Object { + self.document.properties() + } + + /// Returns a mutable reference to the custom DID Document properties. + pub fn properties_mut(&mut self) -> &mut Object { + self.document.properties_mut() + } + + // =========================================================================== + // Services + // =========================================================================== + + /// Return a set of all [`StardustService`]s in the document. + pub fn service(&self) -> &OrderedSet { + self.document.service() + } + + /// Add a new [`StardustService`] to the document. + /// + /// Returns `true` if the service was added. + pub fn insert_service(&mut self, service: StardustService) -> bool { + if service.id().fragment().is_none() { + false + } else { + self.core_document_mut().service_mut().append(service) + } + } + + /// Remove a [`StardustService`] identified by the given [`StardustDIDUrl`] from the document. + /// + /// Returns `true` if a service was removed. + pub fn remove_service(&mut self, did_url: &StardustDIDUrl) -> bool { + self.core_document_mut().service_mut().remove(did_url) + } + + // =========================================================================== + // Verification Methods + // =========================================================================== + + /// Returns an iterator over all [`StardustVerificationMethod`] in the DID Document. + pub fn methods(&self) -> impl Iterator { + self.document.methods() + } + + /// Adds a new [`StardustVerificationMethod`] to the document in the given [`MethodScope`]. + /// + /// # Errors + /// + /// Returns an error if a method with the same fragment already exists. + pub fn insert_method(&mut self, method: StardustVerificationMethod, scope: MethodScope) -> Result<()> { + Ok(self.core_document_mut().insert_method(method, scope)?) + } + + /// Removes all references to the specified [`StardustVerificationMethod`]. + /// + /// # Errors + /// + /// Returns an error if the method does not exist. + pub fn remove_method(&mut self, did_url: &StardustDIDUrl) -> Result<()> { + Ok(self.core_document_mut().remove_method(did_url)?) + } + + /// Attaches the relationship to the given method, if the method exists. + /// + /// Note: The method needs to be in the set of verification methods, + /// so it cannot be an embedded one. + pub fn attach_method_relationship( + &mut self, + did_url: &StardustDIDUrl, + relationship: MethodRelationship, + ) -> Result { + Ok( + self + .core_document_mut() + .attach_method_relationship(did_url, relationship)?, + ) + } + + /// Detaches the given relationship from the given method, if the method exists. + pub fn detach_method_relationship( + &mut self, + did_url: &StardustDIDUrl, + relationship: MethodRelationship, + ) -> Result { + Ok( + self + .core_document_mut() + .detach_method_relationship(did_url, relationship)?, + ) + } + + /// Returns the first [`StardustVerificationMethod`] with an `id` property matching the + /// provided `query` and the verification relationship specified by `scope` if present. + /// + /// WARNING: improper usage of this allows violating the uniqueness of the verification method + /// sets. + pub fn resolve_method_mut<'query, Q>( + &mut self, + query: Q, + scope: Option, + ) -> Option<&mut StardustVerificationMethod> + where + Q: Into>, + { + self.document.resolve_method_mut(query, scope) + } + + // =========================================================================== + // Signatures + // =========================================================================== + + /// Creates a new [`DocumentSigner`] that can be used to create digital signatures + /// from verification methods in this DID Document. + pub fn signer<'base>(&'base self, private_key: &'base PrivateKey) -> DocumentSigner<'base, '_, StardustDID> { + self.document.signer(private_key) + } + + /// Signs the provided `data` with the verification method specified by `method_query`. + /// See [`StardustDocument::signer`] for creating signatures with a builder pattern. + /// + /// NOTE: does not validate whether `private_key` corresponds to the verification method. + /// See [`StardustDocument::verify_data`]. + /// + /// # Errors + /// + /// Fails if an unsupported verification method is used, data + /// serialization fails, or the signature operation fails. + pub fn sign_data<'query, 'this: 'query, X, Q>( + &'this self, + data: &mut X, + private_key: &'this PrivateKey, + method_query: Q, + options: ProofOptions, + ) -> Result<()> + where + X: Serialize + SetSignature + TryMethod, + Q: Into>, + { + self + .signer(private_key) + .method(method_query) + .options(options) + .sign(data) + .map_err(Into::into) + } + + // =========================================================================== + // Publishing + // =========================================================================== + // TODO: clean up and feature-gate certain methods to avoid hard dependency on iota-client? + + /// Serializes the document for inclusion in an Alias Output's state metadata + /// with the default [`StateMetadataEncoding`]. + pub fn pack(self) -> Result> { + self.pack_with_encoding(StateMetadataEncoding::Json) + } + + /// Serializes the document for inclusion in an Alias Output's state metadata. + pub fn pack_with_encoding(self, encoding: StateMetadataEncoding) -> Result> { + StateMetadataDocument::from(self).pack(encoding) + } + + /// Deserializes the document from the state metadata bytes of an Alias Output. + /// + /// NOTE: `did` is required since it is omitted from the serialized DID Document and + /// cannot be inferred from the [`Output`]. It also indicates the network, which is not + /// encoded in the `AliasId` alone. + pub fn unpack(did: &StardustDID, state_metadata: &[u8]) -> Result { + StateMetadataDocument::unpack(state_metadata).map(|doc| doc.into_stardust_document(did)) + } + + /// Constructs a DID from an Alias ID. + /// + /// Uses the hex-encoding of the Alias ID as the DID tag. + // TODO: pass in optional network? Feature-gate to avoid iota-client hard dependency? + pub fn alias_id_to_did(id: &AliasId) -> Result { + // Manually encode to hex to avoid 0x prefix. + let hex: String = BaseEncoding::encode(id.as_slice(), Base::Base16Lower); + StardustDID::parse(format!("did:stardust:{hex}")).map_err(Into::into) + } + + pub fn did_to_alias_id(did: &StardustDID) -> Result { + // TODO: just use 0x in the tag as well? + // Prepend 0x manually. + AliasId::from_str(&format!("0x{}", did.method_id())).map_err(Into::into) + } + + // TODO: can hopefully remove if the publishing logic is wrapped. + pub fn did_from_block(block: &Block) -> Result { + let id: AliasId = AliasId::from(get_alias_output_id_from_payload(block.payload().unwrap())); + Self::alias_id_to_did(&id) + } + + /// Deserializes a JSON-encoded `StardustDocument` from an Alias Output block. + /// + /// NOTE: `did` is required since it is omitted from the serialized DID Document and + /// cannot be inferred from the [`Output`]. It also indicates the network, which is not + /// encoded in the `AliasId` alone. + // TODO: remove? Is `unpack` sufficient? + pub fn deserialize_from_output(did: &StardustDID, output: &Output) -> Result { + let document: &[u8] = match output { + Output::Alias(alias_output) => alias_output.state_metadata(), + _ => panic!("not an alias output"), + }; + Self::unpack(did, document) + } +} + +// helper function to get the output id for the first alias output +// TODO: error handling +fn get_alias_output_id_from_payload(payload: &Payload) -> OutputId { + match payload { + Payload::Transaction(tx_payload) => { + let TransactionEssence::Regular(regular) = tx_payload.essence(); + for (index, output) in regular.outputs().iter().enumerate() { + if let Output::Alias(_alias_output) = output { + return OutputId::new(tx_payload.id(), index.try_into().unwrap()).unwrap(); + } + } + panic!("No alias output in transaction essence") + } + _ => panic!("No tx payload"), + } +} + +impl Document for StardustDocument { + type D = StardustDID; + type U = Object; + type V = Object; + + fn id(&self) -> &Self::D { + StardustDocument::id(self) + } + + fn resolve_service<'query, 'me, Q>(&'me self, query: Q) -> Option<&Service> + where + Q: Into>, + { + self.document.resolve_service(query) + } + + fn resolve_method<'query, 'me, Q>( + &'me self, + query: Q, + scope: Option, + ) -> Option<&VerificationMethod> + where + Q: Into>, + { + self.document.resolve_method(query, scope) + } + + fn verify_data(&self, data: &X, options: &VerifierOptions) -> identity_did::Result<()> + where + X: Serialize + GetSignature + ?Sized, + { + self.document.verify_data(data, options) + } +} + +#[cfg(feature = "revocation-bitmap")] +mod iota_document_revocation { + use identity_did::utils::DIDUrlQuery; + + use crate::Error; + use crate::Result; + + use super::StardustDocument; + + impl StardustDocument { + /// If the document has a [`RevocationBitmap`](identity_did::revocation::RevocationBitmap) + /// service identified by `service_query`, revoke all specified `indices`. + pub fn revoke_credentials<'query, 'me, Q>(&mut self, service_query: Q, indices: &[u32]) -> Result<()> + where + Q: Into>, + { + self + .core_document_mut() + .revoke_credentials(service_query, indices) + .map_err(Error::RevocationError) + } + + /// If the document has a [`RevocationBitmap`](identity_did::revocation::RevocationBitmap) + /// service with an id by `service_query`, unrevoke all specified `indices`. + pub fn unrevoke_credentials<'query, 'me, Q>(&'me mut self, service_query: Q, indices: &[u32]) -> Result<()> + where + Q: Into>, + { + self + .core_document_mut() + .unrevoke_credentials(service_query, indices) + .map_err(Error::RevocationError) + } + } +} + +impl From for StardustCoreDocument { + fn from(document: StardustDocument) -> Self { + document.document + } +} + +impl From<(StardustCoreDocument, StardustDocumentMetadata)> for StardustDocument { + fn from((document, metadata): (StardustCoreDocument, StardustDocumentMetadata)) -> Self { + Self { document, metadata } + } +} + +impl Default for StardustDocument { + fn default() -> Self { + Self::new() + } +} + +impl Display for StardustDocument { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.fmt_json(f) + } +} + +impl TryMethod for StardustDocument { + const TYPE: MethodUriType = MethodUriType::Absolute; +} + +#[cfg(test)] +mod tests { + use super::*; + use identity_core::common::Timestamp; + use identity_core::convert::FromJson; + use identity_core::convert::ToJson; + use identity_core::crypto::KeyPair; + use identity_core::crypto::KeyType; + use identity_did::verifiable::VerifiableProperties; + use identity_did::verification::MethodData; + use identity_did::verification::MethodType; + + fn valid_did() -> StardustDID { + "did:stardust:0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + .parse() + .unwrap() + } + + fn generate_method(controller: &StardustDID, fragment: &str) -> StardustVerificationMethod { + VerificationMethod::builder(Default::default()) + .id(controller.to_url().join(fragment).unwrap()) + .controller(controller.clone()) + .type_(MethodType::Ed25519VerificationKey2018) + .data(MethodData::new_multibase(fragment.as_bytes())) + .build() + .unwrap() + } + + fn generate_document(id: &StardustDID) -> StardustDocument { + let mut metadata: StardustDocumentMetadata = StardustDocumentMetadata::new(); + metadata.created = Some(Timestamp::parse("2020-01-02T00:00:00Z").unwrap()); + metadata.updated = Some(Timestamp::parse("2020-01-02T00:00:00Z").unwrap()); + + let document: StardustCoreDocument = StardustCoreDocument::builder(Object::default()) + .id(id.clone()) + .controller(id.clone()) + .verification_method(generate_method(id, "#key-1")) + .verification_method(generate_method(id, "#key-2")) + .verification_method(generate_method(id, "#key-3")) + .authentication(generate_method(id, "#auth-key")) + .authentication(id.to_url().join("#key-3").unwrap()) + .build() + .unwrap(); + + StardustDocument::from((document, metadata)) + } + + #[test] + fn test_new() { + // VALID new(). + let doc1: StardustDocument = StardustDocument::new(); + assert_eq!(doc1.id(), StardustDocument::placeholder_did()); + assert_eq!(doc1.methods().count(), 0); + assert!(doc1.service().is_empty()); + + // VALID new_with_id(). + let did: StardustDID = valid_did(); + let doc2: StardustDocument = StardustDocument::new_with_id(did.clone()); + assert_eq!(doc2.id(), &did); + assert_eq!(doc2.methods().count(), 0); + assert!(doc2.service().is_empty()); + } + + #[test] + fn test_methods() { + let controller: StardustDID = valid_did(); + let document: StardustDocument = generate_document(&controller); + let expected: Vec = vec![ + generate_method(&controller, "#key-1"), + generate_method(&controller, "#key-2"), + generate_method(&controller, "#key-3"), + generate_method(&controller, "#auth-key"), + ]; + + let mut methods = document.methods(); + assert_eq!(methods.next(), Some(&expected[0])); + assert_eq!(methods.next(), Some(&expected[1])); + assert_eq!(methods.next(), Some(&expected[2])); + assert_eq!(methods.next(), Some(&expected[3])); + assert_eq!(methods.next(), None); + } + + #[test] + fn test_verify_data_with_scope() { + fn generate_data() -> VerifiableProperties { + use identity_core::json; + let mut properties: VerifiableProperties = VerifiableProperties::default(); + properties.properties.insert("int_key".to_owned(), json!(1)); + properties.properties.insert("str".to_owned(), json!("some value")); + properties + .properties + .insert("object".to_owned(), json!({ "inner": 42 })); + properties + } + + let mut document: StardustDocument = StardustDocument::new(); + + // Try sign using each type of verification relationship. + for scope in [ + MethodScope::assertion_method(), + MethodScope::authentication(), + MethodScope::capability_delegation(), + MethodScope::capability_invocation(), + MethodScope::key_agreement(), + MethodScope::VerificationMethod, + ] { + // Add a new method. + let key_new: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); + let method_fragment = format!("{}-1", scope.as_str().to_ascii_lowercase()); + let method_new: StardustVerificationMethod = StardustVerificationMethod::new( + document.id().clone(), + key_new.type_(), + key_new.public(), + method_fragment.as_str(), + ) + .unwrap(); + document.insert_method(method_new, scope).unwrap(); + + // Sign and verify data. + let mut data = generate_data(); + document + .sign_data( + &mut data, + key_new.private(), + method_fragment.as_str(), + ProofOptions::default(), + ) + .unwrap(); + // Signature should still be valid for every scope. + assert!(document.verify_data(&data, &VerifierOptions::default()).is_ok()); + + // Ensure only the correct scope is valid. + for scope_check in [ + MethodScope::assertion_method(), + MethodScope::authentication(), + MethodScope::capability_delegation(), + MethodScope::capability_invocation(), + MethodScope::key_agreement(), + MethodScope::VerificationMethod, + ] { + let result = document.verify_data(&data, &VerifierOptions::new().method_scope(scope_check)); + // Any other scope should fail validation. + if scope_check == scope { + assert!(result.is_ok()); + } else { + assert!(result.is_err()); + } + } + } + } + + #[test] + fn test_services() { + // VALID: add one service. + let mut document: StardustDocument = StardustDocument::new(); + let url1: StardustDIDUrl = document.id().to_url().join("#linked-domain").unwrap(); + let service1: StardustService = Service::from_json(&format!( + r#"{{ + "id":"{}", + "type": "LinkedDomains", + "serviceEndpoint": "https://bar.example.com" + }}"#, + url1 + )) + .unwrap(); + document.insert_service(service1.clone()); + assert_eq!(1, document.service().len()); + assert_eq!(document.resolve_service(&url1), Some(&service1)); + assert_eq!(document.resolve_service("#linked-domain"), Some(&service1)); + assert_eq!(document.resolve_service("linked-domain"), Some(&service1)); + assert_eq!(document.resolve_service(""), None); + assert_eq!(document.resolve_service("#other"), None); + + // VALID: add two services. + let url2: StardustDIDUrl = document.id().to_url().join("#revocation").unwrap(); + let service2: StardustService = Service::from_json(&format!( + r#"{{ + "id":"{}", + "type": "RevocationBitmap2022", + "serviceEndpoint": "data:,blah" + }}"#, + url2 + )) + .unwrap(); + document.insert_service(service2.clone()); + assert_eq!(2, document.service().len()); + assert_eq!(document.resolve_service(&url2), Some(&service2)); + assert_eq!(document.resolve_service("#revocation"), Some(&service2)); + assert_eq!(document.resolve_service("revocation"), Some(&service2)); + assert_eq!(document.resolve_service(""), None); + assert_eq!(document.resolve_service("#other"), None); + + // INVALID: insert service with duplicate fragment fails. + let duplicate: StardustService = Service::from_json(&format!( + r#"{{ + "id":"{}", + "type": "DuplicateService", + "serviceEndpoint": "data:,duplicate" + }}"#, + url1 + )) + .unwrap(); + assert!(!document.insert_service(duplicate.clone())); + assert_eq!(2, document.service().len()); + let resolved: &StardustService = document.resolve_service(&url1).unwrap(); + assert_eq!(resolved, &service1); + assert_ne!(resolved, &duplicate); + + // VALID: remove services. + assert!(document.remove_service(&url1)); + assert_eq!(1, document.service().len()); + let last_service: &StardustService = document.resolve_service(&url2).unwrap(); + assert_eq!(last_service, &service2); + + assert!(document.remove_service(&url2)); + assert_eq!(0, document.service().len()); + } + + #[test] + fn test_document_equality() { + let mut original_doc: StardustDocument = StardustDocument::new_with_id(valid_did()); + let keypair1: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); + let method1: StardustVerificationMethod = StardustVerificationMethod::new( + original_doc.id().to_owned(), + keypair1.type_(), + keypair1.public(), + "test-0", + ) + .unwrap(); + original_doc + .insert_method(method1, MethodScope::capability_invocation()) + .unwrap(); + + // Update the key material of the existing verification method #test-0. + let mut doc1 = original_doc.clone(); + let keypair2: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); + let method2: StardustVerificationMethod = + StardustVerificationMethod::new(doc1.id().to_owned(), keypair2.type_(), keypair2.public(), "test-0").unwrap(); + + doc1 + .remove_method(&doc1.id().to_url().join("#test-0").unwrap()) + .unwrap(); + doc1 + .insert_method(method2, MethodScope::capability_invocation()) + .unwrap(); + + // Even though the method fragment is the same, the key material has been updated + // so the two documents are expected to not be equal. + assert_ne!(original_doc, doc1); + + let mut doc2 = doc1.clone(); + let keypair3: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); + let method3: StardustVerificationMethod = + StardustVerificationMethod::new(doc1.id().to_owned(), keypair3.type_(), keypair3.public(), "test-0").unwrap(); + + let insertion_result = doc2.insert_method(method3, MethodScope::capability_invocation()); + + // Nothing was inserted, because a method with the same fragment already existed. + assert!(insertion_result.is_err()); + assert_eq!(doc1, doc2); + } + + #[test] + fn test_json_roundtrip() { + let document: StardustDocument = generate_document(&valid_did()); + + let ser: String = document.to_json().unwrap(); + let de: StardustDocument = StardustDocument::from_json(&ser).unwrap(); + assert_eq!(document, de); + } + + #[test] + fn test_json_fieldnames() { + let document: StardustDocument = StardustDocument::new(); + let serialization: String = document.to_json().unwrap(); + assert_eq!( + serialization, + format!("{{\"doc\":{},\"meta\":{}}}", document.document, document.metadata) + ); + } +} diff --git a/identity_stardust/src/document/stardust_document_metadata.rs b/identity_stardust/src/document/stardust_document_metadata.rs new file mode 100644 index 0000000000..6f014ac7c5 --- /dev/null +++ b/identity_stardust/src/document/stardust_document_metadata.rs @@ -0,0 +1,49 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use core::fmt::Debug; +use core::fmt::Display; +use core::fmt::Formatter; + +use identity_core::common::Object; +use identity_core::common::Timestamp; +use identity_core::convert::FmtJson; +use serde::Deserialize; +use serde::Serialize; + +/// Additional attributes related to a [`StardustDocument`][crate::StardustDocument]. +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +pub struct StardustDocumentMetadata { + // TODO: store created in the immutable metadata, if possible? + #[serde(skip_serializing_if = "Option::is_none")] + pub created: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub updated: Option, + #[serde(flatten)] + pub properties: Object, +} + +impl StardustDocumentMetadata { + /// Creates a new `StardustDocumentMetadata` with the current system datetime used for `created` + /// and `updated` timestamps. + pub fn new() -> Self { + let now: Timestamp = Timestamp::now_utc(); + Self { + created: Some(now), + updated: Some(now), + properties: Object::default(), + } + } +} + +impl Default for StardustDocumentMetadata { + fn default() -> Self { + Self::new() + } +} + +impl Display for StardustDocumentMetadata { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + self.fmt_json(f) + } +} diff --git a/identity_stardust/src/error.rs b/identity_stardust/src/error.rs index 560f038bbd..0eaedb3160 100644 --- a/identity_stardust/src/error.rs +++ b/identity_stardust/src/error.rs @@ -17,7 +17,10 @@ pub enum Error { #[error("{0}")] ClientError(#[from] iota_client::error::Error), #[error("{0}")] - BeeError(#[from] iota_client::bee_block::Error), + BeeError(#[from] iota_client::block::Error), + #[error("invalid state metadata {0}")] InvalidStateMetadata(&'static str), + #[error("credential revocation error")] + RevocationError(#[source] identity_did::Error), } diff --git a/identity_stardust/src/lib.rs b/identity_stardust/src/lib.rs index 39cdc3998f..d466fd22d8 100644 --- a/identity_stardust/src/lib.rs +++ b/identity_stardust/src/lib.rs @@ -7,9 +7,9 @@ pub use self::error::Error; pub use self::error::Result; -pub use stardust_document::StardustDocument; +pub use document::*; pub use state_metadata::*; +mod document; mod error; -mod stardust_document; mod state_metadata; diff --git a/identity_stardust/src/stardust_document.rs b/identity_stardust/src/stardust_document.rs deleted file mode 100644 index 9fa632bc62..0000000000 --- a/identity_stardust/src/stardust_document.rs +++ /dev/null @@ -1,218 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use core::fmt::Debug; -use core::fmt::Display; -use core::fmt::Formatter; -use core::fmt::Result as FmtResult; -use std::str::FromStr; - -use identity_core::common::Object; -use identity_core::convert::FmtJson; -use identity_core::crypto::KeyPair; -use identity_core::utils::Base; -use identity_core::utils::BaseEncoding; -use identity_did::did::CoreDID; -use identity_did::did::DID; -use identity_did::document::CoreDocument; -use identity_did::service::Service; -use identity_did::service::ServiceEndpoint; -use identity_did::verification::MethodScope; -use identity_did::verification::VerificationMethod; -use iota_client::bee_block::output::AliasId; -use iota_client::bee_block::output::Output; -use iota_client::bee_block::output::OutputId; -use iota_client::bee_block::payload::transaction::TransactionEssence; -use iota_client::bee_block::payload::Payload; -use iota_client::bee_block::Block; -use serde::Deserialize; -use serde::Serialize; - -use crate::error::Result; -use crate::state_metadata::StateMetadataEncoding; -use crate::state_metadata::PLACEHOLDER_DID; -use crate::StateMetadataDocument; - -/// An IOTA DID document resolved from the Tangle. Represents an integration chain message possibly -/// merged with one or more diff messages. -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] -pub struct StardustDocument(pub(crate) CoreDocument); - -impl StardustDocument { - /// Constructs an empty DID Document with a [`StardustDocument::placeholder_did`] identifier. - pub fn new() -> StardustDocument { - Self( - // PANIC: constructing an empty DID Document is infallible, caught by tests otherwise. - CoreDocument::builder(Object::default()) - .id(Self::placeholder_did().clone()) - .build() - .expect("empty StardustDocument constructor failed"), - ) - } - - /// Temporary testing implementation. - pub fn tmp_add_verification_method( - &mut self, - id: CoreDID, - keypair: &KeyPair, - fragment: &str, - scope: MethodScope, - ) -> Result<()> { - let method: VerificationMethod = VerificationMethod::new(id, keypair.type_(), keypair.public(), fragment)?; - - self.0.insert_method(method, scope)?; - - Ok(()) - } - - /// Temporary testing implementation. - pub fn tmp_add_service(&mut self, id: CoreDID, fragment: &str, type_: &str, endpoint: ServiceEndpoint) -> Result<()> { - let service = Service::builder(Object::new()) - .type_(type_) - .id(id.join(fragment).unwrap()) - .service_endpoint(endpoint) - .build()?; - - self.0.service_mut().append(service); - - Ok(()) - } - - /// Temporary testing implementation. - pub fn tmp_set_id(&mut self, mut id: CoreDID) { - std::mem::swap(self.0.id_mut(), &mut id); - } - - /// Temporary testing implementation. - pub fn tmp_id(&self) -> &CoreDID { - self.0.id() - } - - /// Serializes the document for inclusion in an alias output's state metadata - /// with the default encoding. - pub fn pack(self) -> Result> { - self.pack_with_encoding(StateMetadataEncoding::Json) - } - - /// Serializes the document for inclusion in an alias output's state metadata. - pub fn pack_with_encoding(self, encoding: StateMetadataEncoding) -> Result> { - StateMetadataDocument::from(self).pack(encoding) - } - - /// Returns the placeholder DID of newly constructed DID Documents, - /// `"did:0:0"`. - // TODO: generalise to take network name? - pub fn placeholder_did() -> &'static CoreDID { - &PLACEHOLDER_DID - } - - /// Constructs a DID from an Alias ID. - /// - /// Uses the hex-encoding of the Alias ID as the DID tag. - pub fn alias_id_to_did(id: &AliasId) -> Result { - // Manually encode to hex to avoid 0x prefix. - let hex: String = BaseEncoding::encode(id.as_slice(), Base::Base16Lower); - CoreDID::parse(format!("did:stardust:{hex}")).map_err(Into::into) - } - - pub fn did_to_alias_id(did: &CoreDID) -> Result { - // TODO: just use 0x in the tag as well? - // Prepend 0x manually. - AliasId::from_str(&format!("0x{}", did.method_id())).map_err(Into::into) - } - - // TODO: can hopefully remove if the publishing logic is wrapped. - pub fn did_from_block(block: &Block) -> Result { - let id: AliasId = AliasId::from(get_alias_output_id_from_payload(block.payload().unwrap())); - Self::alias_id_to_did(&id) - } - - fn parse_block(block: &Block) -> (AliasId, &[u8]) { - match block.payload().unwrap() { - Payload::Transaction(tx_payload) => { - let TransactionEssence::Regular(regular) = tx_payload.essence(); - for (index, output) in regular.outputs().iter().enumerate() { - if let Output::Alias(alias_output) = output { - let alias_id = alias_output - .alias_id() - .or_from_output_id(OutputId::new(tx_payload.id(), index.try_into().unwrap()).unwrap()); - let document = alias_output.state_metadata(); - return (alias_id, document); - } - } - panic!("No alias output in transaction essence") - } - _ => panic!("No tx payload"), - }; - } - - /// Deserializes a JSON-encoded `StardustDocument` from an Alias Output block. - /// - /// NOTE: [`AliasId`] is required since it cannot be inferred from the [`Output`] alone - /// for the first time an Alias Output is published, the transaction payload is required. - pub fn deserialize_from_output(alias_id: &AliasId, output: &Output) -> Result { - let document: &[u8] = match output { - Output::Alias(alias_output) => alias_output.state_metadata(), - _ => panic!("not an alias output"), - }; - Self::deserialize_inner(alias_id, document) - } - - /// Deserializes a JSON-encoded `StardustDocument` from an Alias Output block. - pub fn deserialize_from_block(block: &Block) -> Result { - let (alias_id, document) = Self::parse_block(block); - Self::deserialize_inner(&alias_id, document) - } - - pub fn deserialize_inner(alias_id: &AliasId, document: &[u8]) -> Result { - let did: CoreDID = Self::alias_id_to_did(alias_id)?; - StateMetadataDocument::unpack(document).map(|doc| doc.into_stardust_document(&did)) - } -} - -impl Default for StardustDocument { - fn default() -> Self { - Self::new() - } -} - -impl Display for StardustDocument { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - self.fmt_json(f) - } -} - -// helper function to get the output id for the first alias output -fn get_alias_output_id_from_payload(payload: &Payload) -> OutputId { - match payload { - Payload::Transaction(tx_payload) => { - let TransactionEssence::Regular(regular) = tx_payload.essence(); - for (index, output) in regular.outputs().iter().enumerate() { - if let Output::Alias(_alias_output) = output { - return OutputId::new(tx_payload.id(), index.try_into().unwrap()).unwrap(); - } - } - panic!("No alias output in transaction essence") - } - _ => panic!("No tx payload"), - } -} - -impl From for CoreDocument { - fn from(document: StardustDocument) -> Self { - // TODO: convert any additional properties (that cannot be inferred from the Output) into - // JSON Object or an alternative struct in CoreDocument. - document.0 - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_new() { - let document: StardustDocument = StardustDocument::new(); - assert_eq!(document.0.id(), StardustDocument::placeholder_did()); - } -} diff --git a/identity_stardust/src/state_metadata/document.rs b/identity_stardust/src/state_metadata/document.rs index 0fc530bd37..1858e87543 100644 --- a/identity_stardust/src/state_metadata/document.rs +++ b/identity_stardust/src/state_metadata/document.rs @@ -11,7 +11,9 @@ use serde::Serialize; use crate::error::Result; use crate::Error; +use crate::StardustCoreDocument; use crate::StardustDocument; +use crate::StardustDocumentMetadata; use super::StateMetadataEncoding; use super::StateMetadataVersion; @@ -21,15 +23,21 @@ pub(crate) static PLACEHOLDER_DID: Lazy = Lazy::new(|| CoreDID::parse(" /// Magic bytes used to mark DID documents. const DID_MARKER: &[u8] = b"DID"; -/// The DID document as it is contained in the state metadata of an alias output. +/// Intermediate representation of the DID document as it is contained in the state metadata of +/// an Alias Output. +/// +/// DID instances in the document are replaced by the `PLACEHOLDER_DID`. #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] -pub(crate) struct StateMetadataDocument(CoreDocument); +pub(crate) struct StateMetadataDocument { + document: CoreDocument, + metadata: StardustDocumentMetadata, +} impl StateMetadataDocument { /// Transforms the document into a [`StardustDocument`] by replacing all placeholders with `original_did`. pub fn into_stardust_document(self, original_did: &CoreDID) -> StardustDocument { - let core_doc: CoreDocument = self.0; - let core_document: CoreDocument = core_doc.map( + let Self { document, metadata } = self; + let core_document: StardustCoreDocument = document.map( // Replace placeholder identifiers. |did| { if did == PLACEHOLDER_DID.as_ref() { @@ -41,7 +49,7 @@ impl StateMetadataDocument { // Do not modify properties. |o| o, ); - StardustDocument(core_document) + StardustDocument::from((core_document, metadata)) } /// Pack a [`StateMetadataDocument`] into bytes, suitable for inclusion in @@ -107,9 +115,9 @@ impl From for StateMetadataDocument { /// Transforms a [`StardustDocument`] into its state metadata representation by replacing all /// occurrences of its did with a placeholder. fn from(document: StardustDocument) -> Self { - let core_document: CoreDocument = CoreDocument::from(document); - let id: CoreDID = core_document.id().clone(); - let core_doc: CoreDocument = core_document.map( + let StardustDocument { document, metadata } = document; + let id: CoreDID = document.id().clone(); + let core_document: CoreDocument = document.map( // Replace self-referential identifiers with a placeholder, but not others. |did| { if did == id { @@ -121,96 +129,98 @@ impl From for StateMetadataDocument { // Do not modify properties. |o| o, ); - StateMetadataDocument(core_doc) + StateMetadataDocument { + document: core_document, + metadata, + } } } #[cfg(test)] mod tests { + use identity_core::common::Object; use identity_core::common::OneOrSet; use identity_core::common::Url; use identity_core::crypto::KeyPair; use identity_core::crypto::KeyType; use identity_did::did::CoreDID; + use identity_did::did::DID; use identity_did::verification::MethodScope; use crate::state_metadata::PLACEHOLDER_DID; use crate::StardustDocument; + use crate::StardustService; + use crate::StardustVerificationMethod; use crate::StateMetadataDocument; use crate::StateMetadataEncoding; struct TestSetup { document: StardustDocument, - original_did: CoreDID, - other_did: CoreDID, + did_self: CoreDID, + did_foreign: CoreDID, } fn test_document() -> TestSetup { - let original_did = + let did_self = CoreDID::parse("did:stardust:8036235b6b5939435a45d68bcea7890eef399209a669c8c263fac7f5089b2ec6").unwrap(); - let other_did = + let did_foreign = CoreDID::parse("did:stardust:71b709dff439f1ac9dd2b9c2e28db0807156b378e13bfa3605ce665aa0d0fdca").unwrap(); - let mut document: StardustDocument = StardustDocument::new(); - document.tmp_set_id(original_did.clone()); - + let mut document: StardustDocument = StardustDocument::new_with_id(did_self.clone()); let keypair: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); document - .tmp_add_verification_method( - document.tmp_id().clone(), - &keypair, - "#did-self", + .insert_method( + StardustVerificationMethod::new(document.id().clone(), keypair.type_(), keypair.public(), "did-self").unwrap(), MethodScope::VerificationMethod, ) .unwrap(); - let keypair: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); + let keypair_foreign: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); document - .tmp_add_verification_method( - other_did.clone(), - &keypair, - "#did-foreign", + .insert_method( + StardustVerificationMethod::new( + did_foreign.clone(), + keypair_foreign.type_(), + keypair_foreign.public(), + "did-foreign", + ) + .unwrap(), MethodScope::authentication(), ) .unwrap(); - document - .tmp_add_service( - document.tmp_id().clone(), - "#my-service", - "RevocationList2030", - identity_did::service::ServiceEndpoint::One(Url::parse("https://example.com/xyzabc").unwrap()), - ) - .unwrap(); - - document - .tmp_add_service( - other_did.clone(), - "#my-foreign-service", - "RevocationList2030", - identity_did::service::ServiceEndpoint::One(Url::parse("https://example.com/0xf4c42e9da").unwrap()), - ) - .unwrap(); + assert!(document.insert_service( + StardustService::builder(Object::new()) + .id(document.id().to_url().join("#my-service").unwrap()) + .type_("RevocationList2022") + .service_endpoint(Url::parse("https://example.com/xyzabc").unwrap()) + .build() + .unwrap() + )); + + assert!(document.insert_service( + StardustService::builder(Object::new()) + .id(did_foreign.to_url().join("#my-foreign-service").unwrap()) + .type_("RevocationList2022") + .service_endpoint(Url::parse("https://example.com/0xf4c42e9da").unwrap()) + .build() + .unwrap() + )); document - .0 .also_known_as_mut() .append(Url::parse("did:example:abc").unwrap()); - document - .0 .also_known_as_mut() .append(Url::parse("did:example:xyz").unwrap()); - let mut controllers = OneOrSet::new_one(other_did.clone()); - (&mut controllers).append(original_did.clone()); - let mut controllers = Some(controllers); - std::mem::swap(&mut controllers, document.0.controller_mut()); + let controllers = OneOrSet::try_from(vec![did_foreign.clone(), did_self.clone()]).unwrap(); + *document.core_document_mut().controller_mut() = Some(controllers); TestSetup { document, - original_did, - other_did, + did_self, + did_foreign, } } @@ -218,15 +228,15 @@ mod tests { fn test_transformation_roundtrip() { let TestSetup { document, - original_did, - other_did, + did_self, + did_foreign, } = test_document(); let state_metadata_doc: StateMetadataDocument = StateMetadataDocument::from(document.clone()); assert_eq!( state_metadata_doc - .0 + .document .resolve_method("#did-self", None) .unwrap() .id() @@ -236,29 +246,29 @@ mod tests { assert_eq!( state_metadata_doc - .0 + .document .resolve_method("#did-foreign", None) .unwrap() .id() .did(), - &other_did + &did_foreign ); assert_eq!( state_metadata_doc - .0 + .document .service() .iter() .find(|service| service.id().fragment().unwrap() == "my-foreign-service") .unwrap() .id() .did(), - &other_did + &did_foreign ); assert_eq!( state_metadata_doc - .0 + .document .service() .iter() .find(|service| service.id().fragment().unwrap() == "my-service") @@ -268,11 +278,11 @@ mod tests { PLACEHOLDER_DID.as_ref() ); - let controllers = state_metadata_doc.0.controller().unwrap(); - assert_eq!(controllers.get(0).unwrap(), &other_did); + let controllers = state_metadata_doc.document.controller().unwrap(); + assert_eq!(controllers.get(0).unwrap(), &did_foreign); assert_eq!(controllers.get(1).unwrap(), PLACEHOLDER_DID.as_ref()); - let stardust_document = state_metadata_doc.into_stardust_document(&original_did); + let stardust_document = state_metadata_doc.into_stardust_document(&did_self); assert_eq!(stardust_document, document); } @@ -282,11 +292,9 @@ mod tests { let TestSetup { document, .. } = test_document(); let state_metadata_doc: StateMetadataDocument = StateMetadataDocument::from(document); - let packed_bytes: Vec = state_metadata_doc.clone().pack(StateMetadataEncoding::Json).unwrap(); let unpacked_doc = StateMetadataDocument::unpack(&packed_bytes).unwrap(); - assert_eq!(state_metadata_doc, unpacked_doc); } } From a097ed4795346bf7cd9e1d5d6d1fb158d44a94ca Mon Sep 17 00:00:00 2001 From: cycraig Date: Fri, 22 Jul 2022 11:26:55 +0200 Subject: [PATCH 27/89] Simplify Wasm JSON conversion code with macro (#955) * Simplify JSON conversion code with macro * Remove unnecessary derives * Add Node unit tests to GitHub CI --- .github/workflows/shared-build-wasm.yml | 16 +- bindings/wasm/docs/api-reference.md | 395 +++++++++++------- .../wasm/src/account/identity/chain_state.rs | 19 +- .../wasm/src/account/types/agreement_info.rs | 22 +- bindings/wasm/src/account/types/auto_save.rs | 17 +- .../wasm/src/account/types/cek_algorithm.rs | 25 +- .../wasm/src/account/types/encrypted_data.rs | 17 +- .../src/account/types/encryption_algorithm.rs | 22 +- .../wasm/src/account/types/key_location.rs | 15 +- .../wasm/src/account/types/method_content.rs | 20 +- bindings/wasm/src/account/types/signature.rs | 18 +- .../wasm/src/account/wasm_account/account.rs | 8 +- bindings/wasm/src/chain/document_history.rs | 16 +- bindings/wasm/src/common/timestamp.rs | 28 +- bindings/wasm/src/credential/credential.rs | 14 +- bindings/wasm/src/credential/presentation.rs | 14 +- .../wasm/src/credential/validation_options.rs | 30 +- bindings/wasm/src/crypto/key_pair.rs | 1 - bindings/wasm/src/crypto/wasm_proof.rs | 15 +- .../wasm/src/crypto/wasm_proof_options.rs | 1 + .../wasm/src/crypto/wasm_proof_purpose.rs | 17 +- bindings/wasm/src/did/wasm_did.rs | 15 +- bindings/wasm/src/did/wasm_did_url.rs | 8 +- bindings/wasm/src/did/wasm_diff_message.rs | 17 +- bindings/wasm/src/did/wasm_document.rs | 18 +- .../wasm/src/did/wasm_document_metadata.rs | 2 +- bindings/wasm/src/did/wasm_method_data.rs | 18 +- bindings/wasm/src/did/wasm_method_scope.rs | 17 +- bindings/wasm/src/did/wasm_method_type.rs | 21 +- .../wasm/src/did/wasm_resolved_document.rs | 18 +- bindings/wasm/src/did/wasm_service.rs | 13 +- .../wasm/src/did/wasm_verification_method.rs | 14 +- .../wasm/src/did/wasm_verifier_options.rs | 18 +- bindings/wasm/src/macros.rs | 22 + bindings/wasm/src/tangle/client.rs | 1 - bindings/wasm/src/tangle/explorer.rs | 3 +- bindings/wasm/src/tangle/network.rs | 13 +- bindings/wasm/src/tangle/receipt.rs | 16 +- bindings/wasm/src/tangle/resolver.rs | 10 +- bindings/wasm/tests/wasm.rs | 4 +- 40 files changed, 339 insertions(+), 639 deletions(-) diff --git a/.github/workflows/shared-build-wasm.yml b/.github/workflows/shared-build-wasm.yml index 102876bb3a..7112c9945b 100644 --- a/.github/workflows/shared-build-wasm.yml +++ b/.github/workflows/shared-build-wasm.yml @@ -15,7 +15,7 @@ on: description: "Name used for the output build artifact" required: true type: string -jobs: +jobs: build-wasm: defaults: run: @@ -55,10 +55,10 @@ jobs: # Download a pre-compiled wasm-bindgen binary. - name: Install wasm-bindgen-cli uses: jetli/wasm-bindgen-action@24ba6f9fff570246106ac3f80f35185600c3f6c9 - + - name: Setup sccache - uses: './.github/actions/rust/sccache/setup-sccache' - with: + uses: './.github/actions/rust/sccache/setup-sccache' + with: os: ${{matrix.os}} - name: Set up Node.js @@ -76,12 +76,16 @@ jobs: if: ${{ inputs.run-unit-tests }} run: npm run test:unit + - name: Run Node unit tests + if: ${{ inputs.run-unit-tests }} + run: npm run test:unit:node + - name: Build Wasm examples run: npm run build:examples - name: Stop sccache uses: './.github/actions/rust/sccache/stop-sccache' - with: + with: os: ${{matrix.os}} - name: Upload artifact @@ -93,4 +97,4 @@ jobs: bindings/wasm/web bindings/wasm/examples/dist if-no-files-found: error - retention-days: 1 \ No newline at end of file + retention-days: 1 diff --git a/bindings/wasm/docs/api-reference.md b/bindings/wasm/docs/api-reference.md index f1c6031ddd..b7c149eadb 100644 --- a/bindings/wasm/docs/api-reference.md +++ b/bindings/wasm/docs/api-reference.md @@ -128,7 +128,8 @@ with a DID subject.

See: https://www.w3.org/TR/did-core/#services

Signature
-
+

A digital signature.

+
StorageTestSuite

A test suite for the Storage interface.

This module contains a set of tests that a correct storage implementation @@ -197,10 +198,10 @@ This variant is the default used if no other variant is specified when construct

FirstError

Return after the first error occurs.

-
KeyType
-
MethodRelationship
+
KeyType
+
## Functions @@ -223,7 +224,9 @@ publishing to the Tangle. * [Account](#Account) * [.createService(options)](#Account+createService) ⇒ Promise.<void> + * [.attachMethodRelationships(options)](#Account+attachMethodRelationships) ⇒ Promise.<void> * [.createMethod(options)](#Account+createMethod) ⇒ Promise.<void> + * [.detachMethodRelationships(options)](#Account+detachMethodRelationships) ⇒ Promise.<void> * [.did()](#Account+did) ⇒ [DID](#DID) * [.autopublish()](#Account+autopublish) ⇒ boolean * [.autosave()](#Account+autosave) ⇒ [AutoSave](#AutoSave) @@ -241,12 +244,10 @@ publishing to the Tangle. * [.unrevokeCredentials(fragment, indices)](#Account+unrevokeCredentials) ⇒ Promise.<void> * [.encryptData(plaintext, associated_data, encryption_algorithm, cek_algorithm, public_key)](#Account+encryptData) ⇒ [Promise.<EncryptedData>](#EncryptedData) * [.decryptData(data, encryption_algorithm, cek_algorithm, fragment)](#Account+decryptData) ⇒ Promise.<Uint8Array> - * [.attachMethodRelationships(options)](#Account+attachMethodRelationships) ⇒ Promise.<void> - * [.detachMethodRelationships(options)](#Account+detachMethodRelationships) ⇒ Promise.<void> + * [.deleteMethod(options)](#Account+deleteMethod) ⇒ Promise.<void> * [.deleteService(options)](#Account+deleteService) ⇒ Promise.<void> * [.setAlsoKnownAs(options)](#Account+setAlsoKnownAs) ⇒ Promise.<void> * [.setController(options)](#Account+setController) ⇒ Promise.<void> - * [.deleteMethod(options)](#Account+deleteMethod) ⇒ Promise.<void> @@ -259,6 +260,20 @@ Adds a new Service to the DID Document. | --- | --- | | options | CreateServiceOptions | + + +### account.attachMethodRelationships(options) ⇒ Promise.<void> +Attach one or more verification relationships to a method. + +Note: the method must exist and be in the set of verification methods; +it cannot be an embedded method. + +**Kind**: instance method of [Account](#Account) + +| Param | Type | +| --- | --- | +| options | AttachMethodRelationshipOptions | + ### account.createMethod(options) ⇒ Promise.<void> @@ -270,6 +285,17 @@ Adds a new verification method to the DID document. | --- | --- | | options | CreateMethodOptions | + + +### account.detachMethodRelationships(options) ⇒ Promise.<void> +Detaches the given relationship from the given method, if the method exists. + +**Kind**: instance method of [Account](#Account) + +| Param | Type | +| --- | --- | +| options | DetachMethodRelationshipOptions | + ### account.did() ⇒ [DID](#DID) @@ -461,30 +487,16 @@ Returns the decrypted text. | cek_algorithm | [CekAlgorithm](#CekAlgorithm) | | fragment | string | - - -### account.attachMethodRelationships(options) ⇒ Promise.<void> -Attach one or more verification relationships to a method. - -Note: the method must exist and be in the set of verification methods; -it cannot be an embedded method. - -**Kind**: instance method of [Account](#Account) - -| Param | Type | -| --- | --- | -| options | AttachMethodRelationshipOptions | - - + -### account.detachMethodRelationships(options) ⇒ Promise.<void> -Detaches the given relationship from the given method, if the method exists. +### account.deleteMethod(options) ⇒ Promise.<void> +Deletes a verification method if the method exists. **Kind**: instance method of [Account](#Account) | Param | Type | | --- | --- | -| options | DetachMethodRelationshipOptions | +| options | DeleteMethodOptions | @@ -519,17 +531,6 @@ Sets the controllers of the DID document. | --- | --- | | options | SetControllerOptions | - - -### account.deleteMethod(options) ⇒ Promise.<void> -Deletes a verification method if the method exists. - -**Kind**: instance method of [Account](#Account) - -| Param | Type | -| --- | --- | -| options | DeleteMethodOptions | - ## AccountBuilder @@ -602,7 +603,7 @@ Agreement information used as the input for the concat KDF. * [.privInfo()](#AgreementInfo+privInfo) ⇒ Uint8Array * [.toJSON()](#AgreementInfo+toJSON) ⇒ any * _static_ - * [.fromJSON(json_value)](#AgreementInfo.fromJSON) ⇒ [AgreementInfo](#AgreementInfo) + * [.fromJSON(json)](#AgreementInfo.fromJSON) ⇒ [AgreementInfo](#AgreementInfo) @@ -644,19 +645,19 @@ Returns a copy of `privInfo' ### agreementInfo.toJSON() ⇒ any -Serializes `AgreementInfo` as a JSON object. +Serializes this to a JSON object. **Kind**: instance method of [AgreementInfo](#AgreementInfo) -### AgreementInfo.fromJSON(json_value) ⇒ [AgreementInfo](#AgreementInfo) -Deserializes `AgreementInfo` from a JSON object. +### AgreementInfo.fromJSON(json) ⇒ [AgreementInfo](#AgreementInfo) +Deserializes an instance from a JSON object. **Kind**: static method of [AgreementInfo](#AgreementInfo) | Param | Type | | --- | --- | -| json_value | any | +| json | any | @@ -670,12 +671,12 @@ Deserializes `AgreementInfo` from a JSON object. * [.never()](#AutoSave.never) ⇒ [AutoSave](#AutoSave) * [.every()](#AutoSave.every) ⇒ [AutoSave](#AutoSave) * [.batch(number_of_actions)](#AutoSave.batch) ⇒ [AutoSave](#AutoSave) - * [.fromJSON(json_value)](#AutoSave.fromJSON) ⇒ [AutoSave](#AutoSave) + * [.fromJSON(json)](#AutoSave.fromJSON) ⇒ [AutoSave](#AutoSave) ### autoSave.toJSON() ⇒ any -Serializes `AutoSave` as a JSON object. +Serializes this to a JSON object. **Kind**: instance method of [AutoSave](#AutoSave) @@ -703,14 +704,14 @@ Save after every N actions. -### AutoSave.fromJSON(json_value) ⇒ [AutoSave](#AutoSave) -Deserializes `AutoSave` from a JSON object. +### AutoSave.fromJSON(json) ⇒ [AutoSave](#AutoSave) +Deserializes an instance from a JSON object. **Kind**: static method of [AutoSave](#AutoSave) | Param | Type | | --- | --- | -| json_value | any | +| json | any | @@ -725,12 +726,12 @@ Supported algorithms used to determine and potentially encrypt the content encry * _static_ * [.EcdhEs(agreement)](#CekAlgorithm.EcdhEs) ⇒ [CekAlgorithm](#CekAlgorithm) * [.EcdhEsA256Kw(agreement)](#CekAlgorithm.EcdhEsA256Kw) ⇒ [CekAlgorithm](#CekAlgorithm) - * [.fromJSON(json_value)](#CekAlgorithm.fromJSON) ⇒ [CekAlgorithm](#CekAlgorithm) + * [.fromJSON(json)](#CekAlgorithm.fromJSON) ⇒ [CekAlgorithm](#CekAlgorithm) ### cekAlgorithm.toJSON() ⇒ any -Serializes `CekAlgorithm` as a JSON object. +Serializes this to a JSON object. **Kind**: instance method of [CekAlgorithm](#CekAlgorithm) @@ -757,14 +758,14 @@ Elliptic Curve Diffie-Hellman Ephemeral Static key agreement using Concat KDF. -### CekAlgorithm.fromJSON(json_value) ⇒ [CekAlgorithm](#CekAlgorithm) -Deserializes `CekAlgorithm` from a JSON object. +### CekAlgorithm.fromJSON(json) ⇒ [CekAlgorithm](#CekAlgorithm) +Deserializes an instance from a JSON object. **Kind**: static method of [CekAlgorithm](#CekAlgorithm) | Param | Type | | --- | --- | -| json_value | any | +| json | any | @@ -774,25 +775,32 @@ Deserializes `CekAlgorithm` from a JSON object. * [ChainState](#ChainState) * _instance_ * [.toJSON()](#ChainState+toJSON) ⇒ any + * [.clone()](#ChainState+clone) ⇒ [ChainState](#ChainState) * _static_ - * [.fromJSON(json_value)](#ChainState.fromJSON) ⇒ [ChainState](#ChainState) + * [.fromJSON(json)](#ChainState.fromJSON) ⇒ [ChainState](#ChainState) ### chainState.toJSON() ⇒ any -Serializes a `ChainState` object as a JSON object. +Serializes this to a JSON object. + +**Kind**: instance method of [ChainState](#ChainState) + + +### chainState.clone() ⇒ [ChainState](#ChainState) +Deep clones the object. **Kind**: instance method of [ChainState](#ChainState) -### ChainState.fromJSON(json_value) ⇒ [ChainState](#ChainState) -Deserializes a JSON object as `ChainState`. +### ChainState.fromJSON(json) ⇒ [ChainState](#ChainState) +Deserializes an instance from a JSON object. **Kind**: static method of [ChainState](#ChainState) | Param | Type | | --- | --- | -| json_value | any | +| json | any | @@ -1073,7 +1081,7 @@ Returns a copy of the miscellaneous properties on the `Credential`. ### credential.toJSON() ⇒ any -Serializes a `Credential` to a JSON object. +Serializes this to a JSON object. **Kind**: instance method of [Credential](#Credential) @@ -1097,7 +1105,7 @@ Returns the base type. ### Credential.fromJSON(json) ⇒ [Credential](#Credential) -Deserializes a `Credential` from a JSON object. +Deserializes an instance from a JSON object. **Kind**: static method of [Credential](#Credential) @@ -1136,7 +1144,7 @@ Throws an error if any of the options are invalid. ### credentialValidationOptions.toJSON() ⇒ any -Serializes a `CredentialValidationOptions` as a JSON object. +Serializes this to a JSON object. **Kind**: instance method of [CredentialValidationOptions](#CredentialValidationOptions) @@ -1154,7 +1162,7 @@ Creates a new `CredentialValidationOptions` with defaults. ### CredentialValidationOptions.fromJSON(json) ⇒ [CredentialValidationOptions](#CredentialValidationOptions) -Deserializes a `CredentialValidationOptions` from a JSON object. +Deserializes an instance from a JSON object. **Kind**: static method of [CredentialValidationOptions](#CredentialValidationOptions) @@ -1335,7 +1343,7 @@ Fails if the issuer field is not a valid DID. * [.clone()](#DID+clone) ⇒ [DID](#DID) * _static_ * [.parse(input)](#DID.parse) ⇒ [DID](#DID) - * [.fromJSON(json_value)](#DID.fromJSON) ⇒ [DID](#DID) + * [.fromJSON(json)](#DID.fromJSON) ⇒ [DID](#DID) @@ -1398,7 +1406,7 @@ Returns the `DID` as a string. ### did.toJSON() ⇒ any -Serializes a `DID` as a JSON object. +Serializes this to a JSON object. **Kind**: instance method of [DID](#DID) @@ -1420,14 +1428,14 @@ Parses a `DID` from the input string. -### DID.fromJSON(json_value) ⇒ [DID](#DID) -Deserializes a JSON object as `DID`. +### DID.fromJSON(json) ⇒ [DID](#DID) +Deserializes an instance from a JSON object. **Kind**: static method of [DID](#DID) | Param | Type | | --- | --- | -| json_value | any | +| json | any | @@ -1450,6 +1458,7 @@ Deserializes a JSON object as `DID`. * [.clone()](#DIDUrl+clone) ⇒ [DIDUrl](#DIDUrl) * _static_ * [.parse(input)](#DIDUrl.parse) ⇒ [DIDUrl](#DIDUrl) + * [.fromJSON(json)](#DIDUrl.fromJSON) ⇒ [DIDUrl](#DIDUrl) @@ -1542,7 +1551,7 @@ Returns the `DIDUrl` as a string. ### didUrl.toJSON() ⇒ any -Serializes a `DIDUrl` as a JSON object. +Serializes this to a JSON object. **Kind**: instance method of [DIDUrl](#DIDUrl) @@ -1562,6 +1571,17 @@ Parses a `DIDUrl` from the input string. | --- | --- | | input | string | + + +### DIDUrl.fromJSON(json) ⇒ [DIDUrl](#DIDUrl) +Deserializes an instance from a JSON object. + +**Kind**: static method of [DIDUrl](#DIDUrl) + +| Param | Type | +| --- | --- | +| json | any | + ## ~~DiffChainHistory~~ @@ -1630,10 +1650,10 @@ Defines the difference between two DID `Document`s' JSON representations. * ~~[.setPreviousMessageId(message_id)](#DiffMessage+setPreviousMessageId)~~ * ~~[.proof()](#DiffMessage+proof) ⇒ [Proof](#Proof) \| undefined~~ * ~~[.merge(document)](#DiffMessage+merge) ⇒ [Document](#Document)~~ - * ~~[.toJSON()](#DiffMessage+toJSON) ⇒ any~~ + * [.toJSON()](#DiffMessage+toJSON) ⇒ any * [.clone()](#DiffMessage+clone) ⇒ [DiffMessage](#DiffMessage) * _static_ - * ~~[.fromJSON(json)](#DiffMessage.fromJSON) ⇒ [DiffMessage](#DiffMessage)~~ + * [.fromJSON(json)](#DiffMessage.fromJSON) ⇒ [DiffMessage](#DiffMessage) @@ -1727,10 +1747,8 @@ with the given Document. -### ~~diffMessage.toJSON() ⇒ any~~ -***Deprecated*** - -Serializes a `DiffMessage` as a JSON object. +### diffMessage.toJSON() ⇒ any +Serializes this to a JSON object. **Kind**: instance method of [DiffMessage](#DiffMessage) @@ -1741,10 +1759,8 @@ Deep clones the object. **Kind**: instance method of [DiffMessage](#DiffMessage) -### ~~DiffMessage.fromJSON(json) ⇒ [DiffMessage](#DiffMessage)~~ -***Deprecated*** - -Deserializes a `DiffMessage` from a JSON object. +### DiffMessage.fromJSON(json) ⇒ [DiffMessage](#DiffMessage) +Deserializes an instance from a JSON object. **Kind**: static method of [DiffMessage](#DiffMessage) @@ -2307,7 +2323,7 @@ unrevoke all specified `indices`. ### document.toJSON() ⇒ any -Serializes a `Document` as a JSON object. +Serializes this to a JSON object. **Kind**: instance method of [Document](#Document) @@ -2374,7 +2390,7 @@ This is the Base58-btc encoded SHA-256 digest of the hex-encoded message id. ### Document.fromJSON(json) ⇒ [Document](#Document) -Deserializes a `Document` from a JSON object. +Deserializes an instance from a JSON object. **Kind**: static method of [Document](#Document) @@ -2441,7 +2457,7 @@ NOTE: clones the data. ### documentHistory.toJSON() ⇒ any -Serializes `DocumentHistory` as a JSON object. +Serializes this to a JSON object. **Kind**: instance method of [DocumentHistory](#DocumentHistory) @@ -2453,7 +2469,7 @@ Deep clones the object. ### DocumentHistory.fromJSON(json) ⇒ [DocumentHistory](#DocumentHistory) -Deserializes `DocumentHistory` from a JSON object. +Deserializes an instance from a JSON object. **Kind**: static method of [DocumentHistory](#DocumentHistory) @@ -2469,10 +2485,14 @@ Additional attributes related to an IOTA DID Document. **Kind**: global class * [DocumentMetadata](#DocumentMetadata) - * [.previousMessageId](#DocumentMetadata+previousMessageId) ⇒ string - * [.created()](#DocumentMetadata+created) ⇒ [Timestamp](#Timestamp) \| undefined - * [.updated()](#DocumentMetadata+updated) ⇒ [Timestamp](#Timestamp) \| undefined - * [.clone()](#DocumentMetadata+clone) ⇒ [DocumentMetadata](#DocumentMetadata) + * _instance_ + * [.previousMessageId](#DocumentMetadata+previousMessageId) ⇒ string + * [.created()](#DocumentMetadata+created) ⇒ [Timestamp](#Timestamp) \| undefined + * [.updated()](#DocumentMetadata+updated) ⇒ [Timestamp](#Timestamp) \| undefined + * [.toJSON()](#DocumentMetadata+toJSON) ⇒ any + * [.clone()](#DocumentMetadata+clone) ⇒ [DocumentMetadata](#DocumentMetadata) + * _static_ + * [.fromJSON(json)](#DocumentMetadata.fromJSON) ⇒ [DocumentMetadata](#DocumentMetadata) @@ -2489,6 +2509,12 @@ Returns a copy of the timestamp of when the DID document was created. ### documentMetadata.updated() ⇒ [Timestamp](#Timestamp) \| undefined Returns a copy of the timestamp of the last DID document update. +**Kind**: instance method of [DocumentMetadata](#DocumentMetadata) + + +### documentMetadata.toJSON() ⇒ any +Serializes this to a JSON object. + **Kind**: instance method of [DocumentMetadata](#DocumentMetadata) @@ -2496,6 +2522,17 @@ Returns a copy of the timestamp of the last DID document update. Deep clones the object. **Kind**: instance method of [DocumentMetadata](#DocumentMetadata) + + +### DocumentMetadata.fromJSON(json) ⇒ [DocumentMetadata](#DocumentMetadata) +Deserializes an instance from a JSON object. + +**Kind**: static method of [DocumentMetadata](#DocumentMetadata) + +| Param | Type | +| --- | --- | +| json | any | + ## Duration @@ -2517,7 +2554,7 @@ A span of time. ### duration.toJSON() ⇒ any -Serializes a `Duration` as a JSON object. +Serializes this to a JSON object. **Kind**: instance method of [Duration](#Duration) @@ -2578,7 +2615,7 @@ Create a new `Duration` with the given number of weeks. ### Duration.fromJSON(json) ⇒ [Duration](#Duration) -Deserializes a `Duration` from a JSON object. +Deserializes an instance from a JSON object. **Kind**: static method of [Duration](#Duration) @@ -2665,7 +2702,7 @@ The structure returned after encrypting data * [.tag()](#EncryptedData+tag) ⇒ Uint8Array * [.toJSON()](#EncryptedData+toJSON) ⇒ any * _static_ - * [.fromJSON(json_value)](#EncryptedData.fromJSON) ⇒ [EncryptedData](#EncryptedData) + * [.fromJSON(json)](#EncryptedData.fromJSON) ⇒ [EncryptedData](#EncryptedData) @@ -2694,19 +2731,19 @@ Returns a copy of the tag ### encryptedData.toJSON() ⇒ any -Serializes `EncryptedData` as a JSON object. +Serializes this to a JSON object. **Kind**: instance method of [EncryptedData](#EncryptedData) -### EncryptedData.fromJSON(json_value) ⇒ [EncryptedData](#EncryptedData) -Deserializes `EncryptedData` from a JSON object. +### EncryptedData.fromJSON(json) ⇒ [EncryptedData](#EncryptedData) +Deserializes an instance from a JSON object. **Kind**: static method of [EncryptedData](#EncryptedData) | Param | Type | | --- | --- | -| json_value | any | +| json | any | @@ -2721,7 +2758,7 @@ Supported content encryption algorithms. * [.toJSON()](#EncryptionAlgorithm+toJSON) ⇒ any * _static_ * [.A256GCM()](#EncryptionAlgorithm.A256GCM) ⇒ [EncryptionAlgorithm](#EncryptionAlgorithm) - * [.fromJSON(json_value)](#EncryptionAlgorithm.fromJSON) ⇒ [EncryptionAlgorithm](#EncryptionAlgorithm) + * [.fromJSON(json)](#EncryptionAlgorithm.fromJSON) ⇒ [EncryptionAlgorithm](#EncryptionAlgorithm) @@ -2732,7 +2769,7 @@ Returns the length of the cipher's key. ### encryptionAlgorithm.toJSON() ⇒ any -Serializes `EncryptionAlgorithm` as a JSON object. +Serializes this to a JSON object. **Kind**: instance method of [EncryptionAlgorithm](#EncryptionAlgorithm) @@ -2743,14 +2780,14 @@ AES GCM using 256-bit key. **Kind**: static method of [EncryptionAlgorithm](#EncryptionAlgorithm) -### EncryptionAlgorithm.fromJSON(json_value) ⇒ [EncryptionAlgorithm](#EncryptionAlgorithm) -Deserializes `EncryptionAlgorithm` from a JSON object. +### EncryptionAlgorithm.fromJSON(json) ⇒ [EncryptionAlgorithm](#EncryptionAlgorithm) +Deserializes an instance from a JSON object. **Kind**: static method of [EncryptionAlgorithm](#EncryptionAlgorithm) | Param | Type | | --- | --- | -| json_value | any | +| json | any | @@ -2762,10 +2799,12 @@ Deserializes `EncryptionAlgorithm` from a JSON object. * [.messageUrl(message_id)](#ExplorerUrl+messageUrl) ⇒ string * [.resolverUrl(did)](#ExplorerUrl+resolverUrl) ⇒ string * [.toString()](#ExplorerUrl+toString) ⇒ string + * [.toJSON()](#ExplorerUrl+toJSON) ⇒ any * _static_ * [.parse(url)](#ExplorerUrl.parse) ⇒ [ExplorerUrl](#ExplorerUrl) * [.mainnet()](#ExplorerUrl.mainnet) ⇒ [ExplorerUrl](#ExplorerUrl) * [.devnet()](#ExplorerUrl.devnet) ⇒ [ExplorerUrl](#ExplorerUrl) + * [.fromJSON(json)](#ExplorerUrl.fromJSON) ⇒ [ExplorerUrl](#ExplorerUrl) @@ -2796,6 +2835,12 @@ E.g. https://explorer.iota.org/mainnet/identity-resolver/{did} ### explorerUrl.toString() ⇒ string +**Kind**: instance method of [ExplorerUrl](#ExplorerUrl) + + +### explorerUrl.toJSON() ⇒ any +Serializes this to a JSON object. + **Kind**: instance method of [ExplorerUrl](#ExplorerUrl) @@ -2823,6 +2868,17 @@ Returns the Tangle explorer URL for the mainnet. Returns the Tangle explorer URL for the devnet. **Kind**: static method of [ExplorerUrl](#ExplorerUrl) + + +### ExplorerUrl.fromJSON(json) ⇒ [ExplorerUrl](#ExplorerUrl) +Deserializes an instance from a JSON object. + +**Kind**: static method of [ExplorerUrl](#ExplorerUrl) + +| Param | Type | +| --- | --- | +| json | any | + ## IntegrationChainHistory @@ -2889,11 +2945,11 @@ The string representation of that location can be obtained via `canonicalRepr`. * _instance_ * [.canonical()](#KeyLocation+canonical) ⇒ string * [.keyType()](#KeyLocation+keyType) ⇒ number - * [.toJSON()](#KeyLocation+toJSON) ⇒ any * [.toString()](#KeyLocation+toString) ⇒ string + * [.toJSON()](#KeyLocation+toJSON) ⇒ any * _static_ * [.fromVerificationMethod(method)](#KeyLocation.fromVerificationMethod) ⇒ [KeyLocation](#KeyLocation) - * [.fromJSON(json_value)](#KeyLocation.fromJSON) ⇒ [KeyLocation](#KeyLocation) + * [.fromJSON(json)](#KeyLocation.fromJSON) ⇒ [KeyLocation](#KeyLocation) @@ -2921,16 +2977,16 @@ This should be used as the representation for storage keys. ### keyLocation.keyType() ⇒ number Returns a copy of the key type of the key location. +**Kind**: instance method of [KeyLocation](#KeyLocation) + + +### keyLocation.toString() ⇒ string **Kind**: instance method of [KeyLocation](#KeyLocation) ### keyLocation.toJSON() ⇒ any -Serializes `KeyLocation` as a JSON object. +Serializes this to a JSON object. -**Kind**: instance method of [KeyLocation](#KeyLocation) - - -### keyLocation.toString() ⇒ string **Kind**: instance method of [KeyLocation](#KeyLocation) @@ -2945,14 +3001,14 @@ Obtain the location of a verification method's key in storage. -### KeyLocation.fromJSON(json_value) ⇒ [KeyLocation](#KeyLocation) -Deserializes a JSON object into a `KeyLocation`. +### KeyLocation.fromJSON(json) ⇒ [KeyLocation](#KeyLocation) +Deserializes an instance from a JSON object. **Kind**: static method of [KeyLocation](#KeyLocation) | Param | Type | | --- | --- | -| json_value | any | +| json | any | @@ -3067,12 +3123,12 @@ Deserializes a `KeyPair` object from a JSON object. * [.GenerateX25519()](#MethodContent.GenerateX25519) ⇒ [MethodContent](#MethodContent) * [.PrivateX25519(privateKey)](#MethodContent.PrivateX25519) ⇒ [MethodContent](#MethodContent) * [.PublicX25519(publicKey)](#MethodContent.PublicX25519) ⇒ [MethodContent](#MethodContent) - * [.fromJSON(json_value)](#MethodContent.fromJSON) ⇒ [MethodContent](#MethodContent) + * [.fromJSON(json)](#MethodContent.fromJSON) ⇒ [MethodContent](#MethodContent) ### methodContent.toJSON() ⇒ any -Serializes `MethodContent` as a JSON object. +Serializes this to a JSON object. **Kind**: instance method of [MethodContent](#MethodContent) @@ -3141,14 +3197,14 @@ NOTE: the method will be unable to be used for key exchange without a private ke -### MethodContent.fromJSON(json_value) ⇒ [MethodContent](#MethodContent) -Deserializes `MethodContent` from a JSON object. +### MethodContent.fromJSON(json) ⇒ [MethodContent](#MethodContent) +Deserializes an instance from a JSON object. **Kind**: static method of [MethodContent](#MethodContent) | Param | Type | | --- | --- | -| json_value | any | +| json | any | @@ -3182,7 +3238,7 @@ represented as a vector of bytes. ### methodData.toJSON() ⇒ any -Serializes a `MethodData` object as a JSON object. +Serializes this to a JSON object. **Kind**: instance method of [MethodData](#MethodData) @@ -3216,7 +3272,7 @@ Creates a new `MethodData` variant with Multibase-encoded content. ### MethodData.fromJSON(json) ⇒ [MethodData](#MethodData) -Deserializes a `MethodData` object from a JSON object. +Deserializes an instance from a JSON object. **Kind**: static method of [MethodData](#MethodData) @@ -3254,7 +3310,7 @@ Returns the `MethodScope` as a string. ### methodScope.toJSON() ⇒ any -Serializes a `MethodScope` object as a JSON object. +Serializes this to a JSON object. **Kind**: instance method of [MethodScope](#MethodScope) @@ -3290,7 +3346,7 @@ Deep clones the object. ### MethodScope.fromJSON(json) ⇒ [MethodScope](#MethodScope) -Deserializes a `MethodScope` object from a JSON object. +Deserializes an instance from a JSON object. **Kind**: static method of [MethodScope](#MethodScope) @@ -3307,25 +3363,25 @@ Supported verification method types. * [MethodType](#MethodType) * _instance_ - * [.toJSON()](#MethodType+toJSON) ⇒ any * [.toString()](#MethodType+toString) ⇒ string + * [.toJSON()](#MethodType+toJSON) ⇒ any * [.clone()](#MethodType+clone) ⇒ [MethodType](#MethodType) * _static_ * [.Ed25519VerificationKey2018()](#MethodType.Ed25519VerificationKey2018) ⇒ [MethodType](#MethodType) * [.X25519KeyAgreementKey2019()](#MethodType.X25519KeyAgreementKey2019) ⇒ [MethodType](#MethodType) * [.fromJSON(json)](#MethodType.fromJSON) ⇒ [MethodType](#MethodType) - - -### methodType.toJSON() ⇒ any -Serializes a `MethodType` object as a JSON object. - -**Kind**: instance method of [MethodType](#MethodType) ### methodType.toString() ⇒ string Returns the `MethodType` as a string. +**Kind**: instance method of [MethodType](#MethodType) + + +### methodType.toJSON() ⇒ any +Serializes this to a JSON object. + **Kind**: instance method of [MethodType](#MethodType) @@ -3344,7 +3400,7 @@ Deep clones the object. ### MethodType.fromJSON(json) ⇒ [MethodType](#MethodType) -Deserializes a `MethodType` object from a JSON object. +Deserializes an instance from a JSON object. **Kind**: static method of [MethodType](#MethodType) @@ -3389,7 +3445,7 @@ Returns a copy of the node URL of the Tangle network. ### network.toJSON() ⇒ any -Serializes a `Network` as a JSON object. +Serializes this to a JSON object. **Kind**: instance method of [Network](#Network) @@ -3422,7 +3478,7 @@ Errors if the name is invalid. ### Network.fromJSON(json) ⇒ [Network](#Network) -Deserializes a `Network` from a JSON object. +Deserializes an instance from a JSON object. **Kind**: static method of [Network](#Network) @@ -3521,7 +3577,7 @@ Returns a copy of the miscellaneous properties on the `Presentation`. ### presentation.toJSON() ⇒ any -Serializes a `Presentation` as a JSON object. +Serializes this to a JSON object. **Kind**: instance method of [Presentation](#Presentation) @@ -3545,7 +3601,7 @@ Returns the base type. ### Presentation.fromJSON(json) ⇒ [Presentation](#Presentation) -Deserializes a `Presentation` from a JSON object. +Deserializes an instance from a JSON object. **Kind**: static method of [Presentation](#Presentation) @@ -3584,7 +3640,7 @@ Throws an error if any of the options are invalid. ### presentationValidationOptions.toJSON() ⇒ any -Serializes a `PresentationValidationOptions` as a JSON object. +Serializes this to a JSON object. **Kind**: instance method of [PresentationValidationOptions](#PresentationValidationOptions) @@ -3602,7 +3658,7 @@ Creates a new `PresentationValidationOptions` with defaults. ### PresentationValidationOptions.fromJSON(json) ⇒ [PresentationValidationOptions](#PresentationValidationOptions) -Deserializes a `PresentationValidationOptions` from a JSON object. +Deserializes an instance from a JSON object. **Kind**: static method of [PresentationValidationOptions](#PresentationValidationOptions) @@ -3782,7 +3838,7 @@ Purpose for which the proof was generated. ### proof.toJSON() ⇒ any -Serializes a `Proof` to a JSON object. +Serializes this to a JSON object. **Kind**: instance method of [Proof](#Proof) @@ -3794,7 +3850,7 @@ Deep clones the object. ### Proof.fromJSON(json) ⇒ [Proof](#Proof) -Deserializes a `Proof` from a JSON object. +Deserializes an instance from a JSON object. **Kind**: static method of [Proof](#Proof) @@ -3813,9 +3869,11 @@ See `IProofOptions`. * [ProofOptions](#ProofOptions) * [new ProofOptions(options)](#new_ProofOptions_new) * _instance_ + * [.toJSON()](#ProofOptions+toJSON) ⇒ any * [.clone()](#ProofOptions+clone) ⇒ [ProofOptions](#ProofOptions) * _static_ * [.default()](#ProofOptions.default) ⇒ [ProofOptions](#ProofOptions) + * [.fromJSON(json)](#ProofOptions.fromJSON) ⇒ [ProofOptions](#ProofOptions) @@ -3829,6 +3887,12 @@ Throws an error if any of the options are invalid. | --- | --- | | options | IProofOptions | + + +### proofOptions.toJSON() ⇒ any +Serializes this to a JSON object. + +**Kind**: instance method of [ProofOptions](#ProofOptions) ### proofOptions.clone() ⇒ [ProofOptions](#ProofOptions) @@ -3841,6 +3905,17 @@ Deep clones the object. Creates a new `ProofOptions` with default options. **Kind**: static method of [ProofOptions](#ProofOptions) + + +### ProofOptions.fromJSON(json) ⇒ [ProofOptions](#ProofOptions) +Deserializes an instance from a JSON object. + +**Kind**: static method of [ProofOptions](#ProofOptions) + +| Param | Type | +| --- | --- | +| json | any | + ## ProofPurpose @@ -3862,7 +3937,7 @@ See https://w3c-ccg.github.io/security-vocab/#proofPurpose ### proofPurpose.toJSON() ⇒ any -Serializes a `ProofPurpose` to a JSON object. +Serializes this to a JSON object. **Kind**: instance method of [ProofPurpose](#ProofPurpose) @@ -3888,7 +3963,7 @@ See https://www.w3.org/TR/did-core/#authentication ### ProofPurpose.fromJSON(json) ⇒ [ProofPurpose](#ProofPurpose) -Deserializes a `ProofPurpose` from a JSON object. +Deserializes an instance from a JSON object. **Kind**: static method of [ProofPurpose](#ProofPurpose) @@ -3939,7 +4014,7 @@ Returns a copy of the message `nonce`. ### receipt.toJSON() ⇒ any -Serializes a `Receipt` as a JSON object. +Serializes this to a JSON object. **Kind**: instance method of [Receipt](#Receipt) @@ -3951,7 +4026,7 @@ Deep clones the object. ### Receipt.fromJSON(json) ⇒ [Receipt](#Receipt) -Deserializes a `Receipt` from a JSON object. +Deserializes an instance from a JSON object. **Kind**: static method of [Receipt](#Receipt) @@ -4062,7 +4137,7 @@ Sets the integration chain message id. ### resolvedDocument.toJSON() ⇒ any -Serializes a `Document` object as a JSON object. +Serializes this to a JSON object. **Kind**: instance method of [ResolvedDocument](#ResolvedDocument) @@ -4074,7 +4149,7 @@ Deep clones the object. ### ResolvedDocument.fromJSON(json) ⇒ [ResolvedDocument](#ResolvedDocument) -Deserializes a `Document` object from a JSON object. +Deserializes an instance from a JSON object. **Kind**: static method of [ResolvedDocument](#ResolvedDocument) @@ -4398,7 +4473,7 @@ See: https://www.w3.org/TR/did-core/#services * [.toJSON()](#Service+toJSON) ⇒ any * [.clone()](#Service+clone) ⇒ [Service](#Service) * _static_ - * [.fromJSON(value)](#Service.fromJSON) ⇒ [Service](#Service) + * [.fromJSON(json)](#Service.fromJSON) ⇒ [Service](#Service) @@ -4435,7 +4510,7 @@ Returns a copy of the custom properties on the `Service`. ### service.toJSON() ⇒ any -Serializes a `Service` object as a JSON object. +Serializes this to a JSON object. **Kind**: instance method of [Service](#Service) @@ -4446,18 +4521,20 @@ Deep clones the object. **Kind**: instance method of [Service](#Service) -### Service.fromJSON(value) ⇒ [Service](#Service) -Deserializes a `Service` object from a JSON object. +### Service.fromJSON(json) ⇒ [Service](#Service) +Deserializes an instance from a JSON object. **Kind**: static method of [Service](#Service) | Param | Type | | --- | --- | -| value | any | +| json | any | ## Signature +A digital signature. + **Kind**: global class * [Signature](#Signature) @@ -4466,7 +4543,7 @@ Deserializes a `Service` object from a JSON object. * [.asBytes()](#Signature+asBytes) ⇒ Uint8Array * [.toJSON()](#Signature+toJSON) ⇒ any * _static_ - * [.fromJSON(json_value)](#Signature.fromJSON) ⇒ [Signature](#Signature) + * [.fromJSON(json)](#Signature.fromJSON) ⇒ [Signature](#Signature) @@ -4487,19 +4564,19 @@ Returns a copy of the signature as a `UInt8Array`. ### signature.toJSON() ⇒ any -Serializes a `Signature` as a JSON object. +Serializes this to a JSON object. **Kind**: instance method of [Signature](#Signature) -### Signature.fromJSON(json_value) ⇒ [Signature](#Signature) -Deserializes a JSON object as `Signature`. +### Signature.fromJSON(json) ⇒ [Signature](#Signature) +Deserializes an instance from a JSON object. **Kind**: static method of [Signature](#Signature) | Param | Type | | --- | --- | -| json_value | any | +| json | any | @@ -4659,7 +4736,7 @@ Returns `null` if the operation leads to a timestamp not in the valid range for ### timestamp.toJSON() ⇒ any -Serializes a `Timestamp` as a JSON object. +Serializes this to a JSON object. **Kind**: instance method of [Timestamp](#Timestamp) @@ -4682,7 +4759,7 @@ Creates a new `Timestamp` with the current date and time. ### Timestamp.fromJSON(json) ⇒ [Timestamp](#Timestamp) -Deserializes a `Timestamp` from a JSON object. +Deserializes an instance from a JSON object. **Kind**: static method of [Timestamp](#Timestamp) @@ -4706,7 +4783,7 @@ Deserializes a `Timestamp` from a JSON object. * [.toJSON()](#VerificationMethod+toJSON) ⇒ any * [.clone()](#VerificationMethod+clone) ⇒ [VerificationMethod](#VerificationMethod) * _static_ - * [.fromJSON(value)](#VerificationMethod.fromJSON) ⇒ [VerificationMethod](#VerificationMethod) + * [.fromJSON(json)](#VerificationMethod.fromJSON) ⇒ [VerificationMethod](#VerificationMethod) @@ -4759,7 +4836,7 @@ Returns a copy of the `VerificationMethod` public key data. ### verificationMethod.toJSON() ⇒ any -Serializes a `VerificationMethod` object as a JSON object. +Serializes this to a JSON object. **Kind**: instance method of [VerificationMethod](#VerificationMethod) @@ -4770,14 +4847,14 @@ Deep clones the object. **Kind**: instance method of [VerificationMethod](#VerificationMethod) -### VerificationMethod.fromJSON(value) ⇒ [VerificationMethod](#VerificationMethod) -Deserializes a `VerificationMethod` object from a JSON object. +### VerificationMethod.fromJSON(json) ⇒ [VerificationMethod](#VerificationMethod) +Deserializes an instance from a JSON object. **Kind**: static method of [VerificationMethod](#VerificationMethod) | Param | Type | | --- | --- | -| value | any | +| json | any | @@ -4811,7 +4888,7 @@ Throws an error if any of the options are invalid. ### verifierOptions.toJSON() ⇒ any -Serializes a `VerifierOptions` as a JSON object. +Serializes this to a JSON object. **Kind**: instance method of [VerifierOptions](#VerifierOptions) @@ -4829,7 +4906,7 @@ Creates a new `VerifierOptions` with default options. ### VerifierOptions.fromJSON(json) ⇒ [VerifierOptions](#VerifierOptions) -Deserializes a `VerifierOptions` from a JSON object. +Deserializes an instance from a JSON object. **Kind**: static method of [VerifierOptions](#VerifierOptions) @@ -4983,15 +5060,15 @@ Return all errors that occur during validation. ## FirstError Return after the first error occurs. -**Kind**: global variable - - -## KeyType **Kind**: global variable ## MethodRelationship **Kind**: global variable + + +## KeyType +**Kind**: global variable ## start() diff --git a/bindings/wasm/src/account/identity/chain_state.rs b/bindings/wasm/src/account/identity/chain_state.rs index 2c4ef63ccf..789bd46642 100644 --- a/bindings/wasm/src/account/identity/chain_state.rs +++ b/bindings/wasm/src/account/identity/chain_state.rs @@ -4,26 +4,11 @@ use identity_iota::account_storage::ChainState; use wasm_bindgen::prelude::*; -use crate::error::Result; -use crate::error::WasmResult; - #[wasm_bindgen(js_name = ChainState, inspectable)] pub struct WasmChainState(pub(crate) ChainState); -#[wasm_bindgen(js_class = ChainState)] -impl WasmChainState { - /// Serializes a `ChainState` object as a JSON object. - #[wasm_bindgen(js_name = toJSON)] - pub fn to_json(&self) -> Result { - JsValue::from_serde(&self.0).wasm_result() - } - - /// Deserializes a JSON object as `ChainState`. - #[wasm_bindgen(js_name = fromJSON)] - pub fn from_json(json_value: JsValue) -> Result { - json_value.into_serde().map(Self).wasm_result() - } -} +impl_wasm_json!(WasmChainState, ChainState); +impl_wasm_clone!(WasmChainState, ChainState); impl From for WasmChainState { fn from(chain_state: ChainState) -> Self { diff --git a/bindings/wasm/src/account/types/agreement_info.rs b/bindings/wasm/src/account/types/agreement_info.rs index a7814f6350..7d6f6ad08a 100644 --- a/bindings/wasm/src/account/types/agreement_info.rs +++ b/bindings/wasm/src/account/types/agreement_info.rs @@ -2,17 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 use identity_iota::account_storage::AgreementInfo; -use serde::Deserialize; -use serde::Serialize; use wasm_bindgen::prelude::*; -use crate::error::Result; -use crate::error::WasmResult; - /// Agreement information used as the input for the concat KDF. -#[derive(Clone, Serialize, Deserialize)] #[wasm_bindgen(js_name = AgreementInfo, inspectable)] -pub struct WasmAgreementInfo(AgreementInfo); +pub struct WasmAgreementInfo(pub(crate) AgreementInfo); #[wasm_bindgen(js_class = AgreementInfo)] impl WasmAgreementInfo { @@ -45,20 +39,10 @@ impl WasmAgreementInfo { pub fn priv_info(&self) -> Vec { self.0.priv_info.clone() } - - /// Serializes `AgreementInfo` as a JSON object. - #[wasm_bindgen(js_name = toJSON)] - pub fn to_json(&self) -> Result { - JsValue::from_serde(&self.0).wasm_result() - } - - /// Deserializes `AgreementInfo` from a JSON object. - #[wasm_bindgen(js_name = fromJSON)] - pub fn from_json(json_value: JsValue) -> Result { - json_value.into_serde().map(Self).wasm_result() - } } +impl_wasm_json!(WasmAgreementInfo, AgreementInfo); + impl From for AgreementInfo { fn from(wasm_agreement_info: WasmAgreementInfo) -> Self { wasm_agreement_info.0 diff --git a/bindings/wasm/src/account/types/auto_save.rs b/bindings/wasm/src/account/types/auto_save.rs index ae846d670c..53ad86904b 100644 --- a/bindings/wasm/src/account/types/auto_save.rs +++ b/bindings/wasm/src/account/types/auto_save.rs @@ -4,9 +4,6 @@ use identity_iota::account::AutoSave; use wasm_bindgen::prelude::*; -use crate::error::Result; -use crate::error::WasmResult; - #[wasm_bindgen] extern "C" { #[wasm_bindgen(typescript_type = "AutoSave | undefined")] @@ -36,16 +33,6 @@ impl WasmAutoSave { pub fn batch(number_of_actions: usize) -> WasmAutoSave { Self(AutoSave::Batch(number_of_actions)) } - - /// Serializes `AutoSave` as a JSON object. - #[wasm_bindgen(js_name = toJSON)] - pub fn to_json(&self) -> Result { - JsValue::from_serde(&self.0).wasm_result() - } - - /// Deserializes `AutoSave` from a JSON object. - #[wasm_bindgen(js_name = fromJSON)] - pub fn from_json(json_value: JsValue) -> Result { - json_value.into_serde().map(Self).wasm_result() - } } + +impl_wasm_json!(WasmAutoSave, AutoSave); diff --git a/bindings/wasm/src/account/types/cek_algorithm.rs b/bindings/wasm/src/account/types/cek_algorithm.rs index 59a3d12f47..b4e5f38049 100644 --- a/bindings/wasm/src/account/types/cek_algorithm.rs +++ b/bindings/wasm/src/account/types/cek_algorithm.rs @@ -2,46 +2,31 @@ // SPDX-License-Identifier: Apache-2.0 use identity_iota::account_storage::CekAlgorithm; -use serde::Deserialize; -use serde::Serialize; use wasm_bindgen::prelude::*; use crate::account::types::WasmAgreementInfo; -use crate::error::Result; -use crate::error::WasmResult; /// Supported algorithms used to determine and potentially encrypt the content encryption key (CEK). -#[derive(Clone, Serialize, Deserialize)] #[wasm_bindgen(js_name = CekAlgorithm, inspectable)] -pub struct WasmCekAlgorithm(CekAlgorithm); +pub struct WasmCekAlgorithm(pub(crate) CekAlgorithm); #[wasm_bindgen(js_class = CekAlgorithm)] impl WasmCekAlgorithm { /// Elliptic Curve Diffie-Hellman Ephemeral Static key agreement using Concat KDF. #[wasm_bindgen(js_name = EcdhEs)] pub fn ecdh_es(agreement: &WasmAgreementInfo) -> WasmCekAlgorithm { - Self(CekAlgorithm::ECDH_ES(agreement.clone().into())) + Self(CekAlgorithm::ECDH_ES(agreement.0.clone())) } /// Elliptic Curve Diffie-Hellman Ephemeral Static key agreement using Concat KDF. #[wasm_bindgen(js_name = EcdhEsA256Kw)] pub fn ecdh_es_a256kw(agreement: &WasmAgreementInfo) -> WasmCekAlgorithm { - Self(CekAlgorithm::ECDH_ES_A256KW(agreement.clone().into())) - } - - /// Serializes `CekAlgorithm` as a JSON object. - #[wasm_bindgen(js_name = toJSON)] - pub fn to_json(&self) -> Result { - JsValue::from_serde(&self.0).wasm_result() - } - - /// Deserializes `CekAlgorithm` from a JSON object. - #[wasm_bindgen(js_name = fromJSON)] - pub fn from_json(json_value: JsValue) -> Result { - json_value.into_serde().map(Self).wasm_result() + Self(CekAlgorithm::ECDH_ES_A256KW(agreement.0.clone())) } } +impl_wasm_json!(WasmCekAlgorithm, CekAlgorithm); + impl From for CekAlgorithm { fn from(wasm_cek_algorithm: WasmCekAlgorithm) -> Self { wasm_cek_algorithm.0 diff --git a/bindings/wasm/src/account/types/encrypted_data.rs b/bindings/wasm/src/account/types/encrypted_data.rs index b17b9a9073..17b81588ba 100644 --- a/bindings/wasm/src/account/types/encrypted_data.rs +++ b/bindings/wasm/src/account/types/encrypted_data.rs @@ -4,9 +4,6 @@ use identity_iota::account_storage::EncryptedData; use wasm_bindgen::prelude::*; -use crate::error::Result; -use crate::error::WasmResult; - /// The structure returned after encrypting data #[wasm_bindgen(js_name = EncryptedData, inspectable)] pub struct WasmEncryptedData(pub(crate) EncryptedData); @@ -36,20 +33,10 @@ impl WasmEncryptedData { pub fn tag(&self) -> Vec { self.0.tag.clone() } - - /// Serializes `EncryptedData` as a JSON object. - #[wasm_bindgen(js_name = toJSON)] - pub fn to_json(&self) -> Result { - JsValue::from_serde(&self.0).wasm_result() - } - - /// Deserializes `EncryptedData` from a JSON object. - #[wasm_bindgen(js_name = fromJSON)] - pub fn from_json(json_value: JsValue) -> Result { - json_value.into_serde().map(Self).wasm_result() - } } +impl_wasm_json!(WasmEncryptedData, EncryptedData); + impl From for EncryptedData { fn from(wasm_encrypted_data: WasmEncryptedData) -> Self { wasm_encrypted_data.0 diff --git a/bindings/wasm/src/account/types/encryption_algorithm.rs b/bindings/wasm/src/account/types/encryption_algorithm.rs index 252b6a66a0..6cc2695bd3 100644 --- a/bindings/wasm/src/account/types/encryption_algorithm.rs +++ b/bindings/wasm/src/account/types/encryption_algorithm.rs @@ -2,17 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 use identity_iota::account_storage::EncryptionAlgorithm; -use serde::Deserialize; -use serde::Serialize; use wasm_bindgen::prelude::*; -use crate::error::Result; -use crate::error::WasmResult; - /// Supported content encryption algorithms. -#[derive(Clone, Serialize, Deserialize)] #[wasm_bindgen(js_name = EncryptionAlgorithm, inspectable)] -pub struct WasmEncryptionAlgorithm(EncryptionAlgorithm); +pub struct WasmEncryptionAlgorithm(pub(crate) EncryptionAlgorithm); #[wasm_bindgen(js_class = EncryptionAlgorithm)] impl WasmEncryptionAlgorithm { @@ -27,20 +21,10 @@ impl WasmEncryptionAlgorithm { pub fn key_length(&self) -> usize { self.0.key_length() } - - /// Serializes `EncryptionAlgorithm` as a JSON object. - #[wasm_bindgen(js_name = toJSON)] - pub fn to_json(&self) -> Result { - JsValue::from_serde(&self.0).wasm_result() - } - - /// Deserializes `EncryptionAlgorithm` from a JSON object. - #[wasm_bindgen(js_name = fromJSON)] - pub fn from_json(json_value: JsValue) -> Result { - json_value.into_serde().map(Self).wasm_result() - } } +impl_wasm_json!(WasmEncryptionAlgorithm, EncryptionAlgorithm); + impl From for EncryptionAlgorithm { fn from(wasm_encryption_algorithm: WasmEncryptionAlgorithm) -> Self { wasm_encryption_algorithm.0 diff --git a/bindings/wasm/src/account/types/key_location.rs b/bindings/wasm/src/account/types/key_location.rs index de7ce72614..40a14f8b34 100644 --- a/bindings/wasm/src/account/types/key_location.rs +++ b/bindings/wasm/src/account/types/key_location.rs @@ -18,7 +18,6 @@ use crate::error::WasmResult; /// situations like these. /// /// The string representation of that location can be obtained via `canonicalRepr`. -#[derive(Debug)] #[wasm_bindgen(js_name = KeyLocation, inspectable)] pub struct WasmKeyLocation(pub(crate) KeyLocation); @@ -54,18 +53,6 @@ impl WasmKeyLocation { self.0.key_type.into() } - /// Serializes `KeyLocation` as a JSON object. - #[wasm_bindgen(js_name = toJSON)] - pub fn to_json(&self) -> Result { - JsValue::from_serde(&self.0).wasm_result() - } - - /// Deserializes a JSON object into a `KeyLocation`. - #[wasm_bindgen(js_name = fromJSON)] - pub fn from_json(json_value: JsValue) -> Result { - json_value.into_serde().map(Self).wasm_result() - } - #[wasm_bindgen(js_name = toString)] #[allow(clippy::inherent_to_string)] pub fn to_string(&self) -> String { @@ -73,6 +60,8 @@ impl WasmKeyLocation { } } +impl_wasm_json!(WasmKeyLocation, KeyLocation); + impl From for KeyLocation { fn from(wasm_key_location: WasmKeyLocation) -> Self { wasm_key_location.0 diff --git a/bindings/wasm/src/account/types/method_content.rs b/bindings/wasm/src/account/types/method_content.rs index b6366eb938..5b1781af55 100644 --- a/bindings/wasm/src/account/types/method_content.rs +++ b/bindings/wasm/src/account/types/method_content.rs @@ -8,9 +8,6 @@ use serde::Deserialize; use serde::Serialize; use wasm_bindgen::prelude::*; -use crate::error::Result; -use crate::error::WasmResult; - #[wasm_bindgen] extern "C" { #[wasm_bindgen(typescript_type = "MethodContent | undefined")] @@ -82,23 +79,10 @@ impl WasmMethodContent { pub fn public_x25519(publicKey: Vec) -> WasmMethodContent { Self(WasmMethodContentInner::PublicX25519(publicKey)) } - - /// Serializes `MethodContent` as a JSON object. - #[wasm_bindgen(js_name = toJSON)] - pub fn to_json(&self) -> Result { - JsValue::from_serde(&self.0).wasm_result() - } - - /// Deserializes `MethodContent` from a JSON object. - #[wasm_bindgen(js_name = fromJSON)] - pub fn from_json(json_value: JsValue) -> Result { - json_value - .into_serde::() - .map(Self) - .wasm_result() - } } +impl_wasm_json!(WasmMethodContent, MethodContent); + impl From for MethodContent { fn from(content: WasmMethodContent) -> Self { match content.0 { diff --git a/bindings/wasm/src/account/types/signature.rs b/bindings/wasm/src/account/types/signature.rs index 51b428cbf4..cf03389b70 100644 --- a/bindings/wasm/src/account/types/signature.rs +++ b/bindings/wasm/src/account/types/signature.rs @@ -4,9 +4,7 @@ use identity_iota::account_storage::Signature; use wasm_bindgen::prelude::*; -use crate::error::Result; -use crate::error::WasmResult; - +/// A digital signature. #[wasm_bindgen(js_name = Signature, inspectable)] pub struct WasmSignature(pub(crate) Signature); @@ -23,20 +21,10 @@ impl WasmSignature { pub fn as_bytes(&self) -> Vec { self.0.as_bytes().to_vec() } - - /// Serializes a `Signature` as a JSON object. - #[wasm_bindgen(js_name = toJSON)] - pub fn to_json(&self) -> Result { - JsValue::from_serde(&self.0).wasm_result() - } - - /// Deserializes a JSON object as `Signature`. - #[wasm_bindgen(js_name = fromJSON)] - pub fn from_json(json_value: JsValue) -> Result { - json_value.into_serde().map(Self).wasm_result() - } } +impl_wasm_json!(WasmSignature, Signature); + impl From for Signature { fn from(wasm_signature: WasmSignature) -> Self { wasm_signature.0 diff --git a/bindings/wasm/src/account/wasm_account/account.rs b/bindings/wasm/src/account/wasm_account/account.rs index e212c20d81..5a55917ab5 100644 --- a/bindings/wasm/src/account/wasm_account/account.rs +++ b/bindings/wasm/src/account/wasm_account/account.rs @@ -328,8 +328,8 @@ impl WasmAccount { public_key: Vec, ) -> PromiseEncryptedData { let account = self.0.clone(); - let encryption_algorithm: EncryptionAlgorithm = encryption_algorithm.clone().into(); - let cek_algorithm: CekAlgorithm = cek_algorithm.clone().into(); + let encryption_algorithm: EncryptionAlgorithm = encryption_algorithm.0; + let cek_algorithm: CekAlgorithm = cek_algorithm.0.clone(); let public_key: PublicKey = public_key.to_vec().into(); future_to_promise(async move { @@ -364,8 +364,8 @@ impl WasmAccount { ) -> PromiseData { let account = self.0.clone(); let data: EncryptedData = data.0.clone(); - let encryption_algorithm: EncryptionAlgorithm = encryption_algorithm.clone().into(); - let cek_algorithm: CekAlgorithm = cek_algorithm.clone().into(); + let encryption_algorithm: EncryptionAlgorithm = encryption_algorithm.0; + let cek_algorithm: CekAlgorithm = cek_algorithm.0.clone(); future_to_promise(async move { let data: Vec = account diff --git a/bindings/wasm/src/chain/document_history.rs b/bindings/wasm/src/chain/document_history.rs index 58660e3c3a..87a97dc653 100644 --- a/bindings/wasm/src/chain/document_history.rs +++ b/bindings/wasm/src/chain/document_history.rs @@ -17,7 +17,6 @@ use crate::error::WasmResult; /// A DID Document's history and current state. #[wasm_bindgen(js_name = DocumentHistory, inspectable)] -#[derive(Clone, Debug, Serialize, Deserialize)] pub struct WasmDocumentHistory(DocumentHistory); // Workaround for Typescript type annotations on async function returns and arrays. @@ -107,20 +106,9 @@ impl WasmDocumentHistory { .collect::() .unchecked_into::() } - - /// Serializes `DocumentHistory` as a JSON object. - #[wasm_bindgen(js_name = toJSON)] - pub fn to_json(&self) -> Result { - JsValue::from_serde(&self.0).wasm_result() - } - - /// Deserializes `DocumentHistory` from a JSON object. - #[wasm_bindgen(js_name = fromJSON)] - pub fn from_json(json: &JsValue) -> Result { - json.into_serde().map(Self).wasm_result() - } } +impl_wasm_json!(WasmDocumentHistory, DocumentHistory); impl_wasm_clone!(WasmDocumentHistory, DocumentHistory); impl From for WasmDocumentHistory { @@ -130,12 +118,10 @@ impl From for WasmDocumentHistory { } #[wasm_bindgen(inspectable)] -#[derive(Clone, Debug, Serialize, Deserialize)] pub struct IntegrationChainHistory(ChainHistory); /// @deprecated since 0.5.0, diff chain features are slated for removal. #[wasm_bindgen(inspectable)] -#[derive(Clone, Debug, Serialize, Deserialize)] pub struct DiffChainHistory(ChainHistory); #[wasm_bindgen] diff --git a/bindings/wasm/src/common/timestamp.rs b/bindings/wasm/src/common/timestamp.rs index 0bc09ae4b9..56093e062c 100644 --- a/bindings/wasm/src/common/timestamp.rs +++ b/bindings/wasm/src/common/timestamp.rs @@ -47,20 +47,10 @@ impl WasmTimestamp { pub fn checked_sub(&self, duration: &WasmDuration) -> Option { self.0.checked_sub(duration.0).map(WasmTimestamp) } - - /// Serializes a `Timestamp` as a JSON object. - #[wasm_bindgen(js_name = toJSON)] - pub fn to_json(&self) -> Result { - JsValue::from_serde(&self.0).wasm_result() - } - - /// Deserializes a `Timestamp` from a JSON object. - #[wasm_bindgen(js_name = fromJSON)] - pub fn from_json(json: &JsValue) -> Result { - json.into_serde().map(Self).wasm_result() - } } +impl_wasm_json!(WasmTimestamp, Timestamp); + impl From for WasmTimestamp { fn from(timestamp: Timestamp) -> Self { Self(timestamp) @@ -101,20 +91,10 @@ impl WasmDuration { pub fn weeks(weeks: u32) -> WasmDuration { Self(Duration::weeks(weeks)) } - - /// Serializes a `Duration` as a JSON object. - #[wasm_bindgen(js_name = toJSON)] - pub fn to_json(&self) -> Result { - JsValue::from_serde(&self.0).wasm_result() - } - - /// Deserializes a `Duration` from a JSON object. - #[wasm_bindgen(js_name = fromJSON)] - pub fn from_json(json: &JsValue) -> Result { - json.into_serde().map(Self).wasm_result() - } } +impl_wasm_json!(WasmDuration, Duration); + impl From for WasmDuration { fn from(duration: Duration) -> Self { Self(duration) diff --git a/bindings/wasm/src/credential/credential.rs b/bindings/wasm/src/credential/credential.rs index df961363d9..9149b00dfa 100644 --- a/bindings/wasm/src/credential/credential.rs +++ b/bindings/wasm/src/credential/credential.rs @@ -25,7 +25,6 @@ use crate::error::Result; use crate::error::WasmResult; #[wasm_bindgen(js_name = Credential, inspectable)] -#[derive(Clone, Debug)] pub struct WasmCredential(pub(crate) Credential); #[wasm_bindgen(js_class = Credential)] @@ -200,20 +199,9 @@ impl WasmCredential { pub fn properties(&self) -> Result { MapStringAny::try_from(&self.0.properties) } - - /// Serializes a `Credential` to a JSON object. - #[wasm_bindgen(js_name = toJSON)] - pub fn to_json(&self) -> Result { - JsValue::from_serde(&self.0).wasm_result() - } - - /// Deserializes a `Credential` from a JSON object. - #[wasm_bindgen(js_name = fromJSON)] - pub fn from_json(json: &JsValue) -> Result { - json.into_serde().map(Self).wasm_result() - } } +impl_wasm_json!(WasmCredential, Credential); impl_wasm_clone!(WasmCredential, Credential); impl From for WasmCredential { diff --git a/bindings/wasm/src/credential/presentation.rs b/bindings/wasm/src/credential/presentation.rs index 154919d6f6..55e8a4e726 100644 --- a/bindings/wasm/src/credential/presentation.rs +++ b/bindings/wasm/src/credential/presentation.rs @@ -20,7 +20,6 @@ use crate::error::Result; use crate::error::WasmResult; #[wasm_bindgen(js_name = Presentation, inspectable)] -#[derive(Clone, Debug, PartialEq)] pub struct WasmPresentation(pub(crate) Presentation); // Workaround for Typescript type annotations for returned arrays. @@ -143,20 +142,9 @@ impl WasmPresentation { pub fn properties(&self) -> Result { MapStringAny::try_from(&self.0.properties) } - - /// Serializes a `Presentation` as a JSON object. - #[wasm_bindgen(js_name = toJSON)] - pub fn to_json(&self) -> Result { - JsValue::from_serde(&self.0).wasm_result() - } - - /// Deserializes a `Presentation` from a JSON object. - #[wasm_bindgen(js_name = fromJSON)] - pub fn from_json(json: &JsValue) -> Result { - json.into_serde().map(Self).wasm_result() - } } +impl_wasm_json!(WasmPresentation, Presentation); impl_wasm_clone!(WasmPresentation, Presentation); impl From for WasmPresentation { diff --git a/bindings/wasm/src/credential/validation_options.rs b/bindings/wasm/src/credential/validation_options.rs index be2f9174ec..69974784f2 100644 --- a/bindings/wasm/src/credential/validation_options.rs +++ b/bindings/wasm/src/credential/validation_options.rs @@ -15,7 +15,6 @@ use crate::error::WasmResult; /// Options to declare validation criteria when validating credentials. #[wasm_bindgen(js_name = CredentialValidationOptions)] -#[derive(Clone)] pub struct WasmCredentialValidationOptions(pub(crate) CredentialValidationOptions); #[wasm_bindgen(js_class = CredentialValidationOptions)] @@ -34,20 +33,9 @@ impl WasmCredentialValidationOptions { pub fn default() -> WasmCredentialValidationOptions { WasmCredentialValidationOptions::from(CredentialValidationOptions::default()) } - - /// Serializes a `CredentialValidationOptions` as a JSON object. - #[wasm_bindgen(js_name = toJSON)] - pub fn to_json(&self) -> Result { - JsValue::from_serde(&self.0).wasm_result() - } - - /// Deserializes a `CredentialValidationOptions` from a JSON object. - #[wasm_bindgen(js_name = fromJSON)] - pub fn from_json(json: &JsValue) -> Result { - json.into_serde().map(Self).wasm_result() - } } +impl_wasm_json!(WasmCredentialValidationOptions, CredentialValidationOptions); impl_wasm_clone!(WasmCredentialValidationOptions, CredentialValidationOptions); impl From for WasmCredentialValidationOptions { @@ -64,7 +52,6 @@ impl From for CredentialValidationOptions { /// Options to declare validation criteria when validating presentation. #[wasm_bindgen(js_name = PresentationValidationOptions)] -#[derive(Clone)] pub struct WasmPresentationValidationOptions(pub(crate) PresentationValidationOptions); #[wasm_bindgen(js_class = PresentationValidationOptions)] @@ -83,20 +70,9 @@ impl WasmPresentationValidationOptions { pub fn default() -> WasmPresentationValidationOptions { WasmPresentationValidationOptions::from(PresentationValidationOptions::default()) } - - /// Serializes a `PresentationValidationOptions` as a JSON object. - #[wasm_bindgen(js_name = toJSON)] - pub fn to_json(&self) -> Result { - JsValue::from_serde(&self.0).wasm_result() - } - - /// Deserializes a `PresentationValidationOptions` from a JSON object. - #[wasm_bindgen(js_name = fromJSON)] - pub fn from_json(json: &JsValue) -> Result { - json.into_serde().map(Self).wasm_result() - } } +impl_wasm_json!(WasmPresentationValidationOptions, PresentationValidationOptions); impl_wasm_clone!(WasmPresentationValidationOptions, PresentationValidationOptions); impl From for WasmPresentationValidationOptions { @@ -114,7 +90,7 @@ impl From for PresentationValidationOptions { /// Controls validation behaviour when checking whether or not a credential has been revoked by its /// [`credentialStatus`](https://www.w3.org/TR/vc-data-model/#status). #[wasm_bindgen(js_name = StatusCheck)] -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize_repr, Deserialize_repr)] +#[derive(Debug, Clone, Copy, Serialize_repr, Deserialize_repr)] #[repr(u8)] pub enum WasmStatusCheck { /// Validate the status if supported, reject any unsupported diff --git a/bindings/wasm/src/crypto/key_pair.rs b/bindings/wasm/src/crypto/key_pair.rs index 744f39b211..4c20bf3259 100644 --- a/bindings/wasm/src/crypto/key_pair.rs +++ b/bindings/wasm/src/crypto/key_pair.rs @@ -21,7 +21,6 @@ struct JsonData { // ============================================================================= #[wasm_bindgen(inspectable, js_name = KeyPair)] -#[derive(Clone, Debug)] pub struct WasmKeyPair(pub(crate) KeyPair); #[wasm_bindgen(js_class = KeyPair)] diff --git a/bindings/wasm/src/crypto/wasm_proof.rs b/bindings/wasm/src/crypto/wasm_proof.rs index cdcecce483..3ad177317a 100644 --- a/bindings/wasm/src/crypto/wasm_proof.rs +++ b/bindings/wasm/src/crypto/wasm_proof.rs @@ -7,8 +7,6 @@ use wasm_bindgen::JsValue; use crate::common::WasmTimestamp; use crate::crypto::WasmProofPurpose; -use crate::error::Result; -use crate::error::WasmResult; /// A digital signature. /// @@ -65,20 +63,9 @@ impl WasmProof { pub fn purpose(&self) -> Option { self.0.purpose.map(WasmProofPurpose::from) } - - /// Serializes a `Proof` to a JSON object. - #[wasm_bindgen(js_name = toJSON)] - pub fn to_json(&self) -> Result { - JsValue::from_serde(&self.0).wasm_result() - } - - /// Deserializes a `Proof` from a JSON object. - #[wasm_bindgen(js_name = fromJSON)] - pub fn from_json(json: &JsValue) -> Result { - json.into_serde().map(Self).wasm_result() - } } +impl_wasm_json!(WasmProof, Proof); impl_wasm_clone!(WasmProof, Proof); impl From for WasmProof { diff --git a/bindings/wasm/src/crypto/wasm_proof_options.rs b/bindings/wasm/src/crypto/wasm_proof_options.rs index 89dcd85640..69b5ecdfb0 100644 --- a/bindings/wasm/src/crypto/wasm_proof_options.rs +++ b/bindings/wasm/src/crypto/wasm_proof_options.rs @@ -30,6 +30,7 @@ impl WasmProofOptions { } } +impl_wasm_json!(WasmProofOptions, ProofOptions); impl_wasm_clone!(WasmProofOptions, ProofOptions); impl From for WasmProofOptions { diff --git a/bindings/wasm/src/crypto/wasm_proof_purpose.rs b/bindings/wasm/src/crypto/wasm_proof_purpose.rs index d91e9e2ccc..cd6807d64e 100644 --- a/bindings/wasm/src/crypto/wasm_proof_purpose.rs +++ b/bindings/wasm/src/crypto/wasm_proof_purpose.rs @@ -4,14 +4,10 @@ use identity_iota::crypto::ProofPurpose; use wasm_bindgen::prelude::*; -use crate::error::Result; -use crate::error::WasmResult; - /// Associates a purpose with a {@link Proof}. /// /// See https://w3c-ccg.github.io/security-vocab/#proofPurpose #[wasm_bindgen(js_name = ProofPurpose, inspectable)] -#[derive(Clone, Debug)] pub struct WasmProofPurpose(pub(crate) ProofPurpose); #[wasm_bindgen(js_class = ProofPurpose)] @@ -29,20 +25,9 @@ impl WasmProofPurpose { pub fn authentication() -> WasmProofPurpose { WasmProofPurpose(ProofPurpose::Authentication) } - - /// Serializes a `ProofPurpose` to a JSON object. - #[wasm_bindgen(js_name = toJSON)] - pub fn to_json(&self) -> Result { - JsValue::from_serde(&self.0).wasm_result() - } - - /// Deserializes a `ProofPurpose` from a JSON object. - #[wasm_bindgen(js_name = fromJSON)] - pub fn from_json(json: &JsValue) -> Result { - json.into_serde().map(Self).wasm_result() - } } +impl_wasm_json!(WasmProofPurpose, ProofPurpose); impl_wasm_clone!(WasmProofPurpose, ProofPurpose); impl From for WasmProofPurpose { diff --git a/bindings/wasm/src/did/wasm_did.rs b/bindings/wasm/src/did/wasm_did.rs index 28eec7107c..87b1931a1a 100644 --- a/bindings/wasm/src/did/wasm_did.rs +++ b/bindings/wasm/src/did/wasm_did.rs @@ -12,7 +12,6 @@ use crate::tangle::WasmNetwork; /// @typicalname did #[wasm_bindgen(js_name = DID, inspectable)] -#[derive(Clone, Debug, PartialEq)] pub struct WasmDID(pub(crate) IotaDID); #[wasm_bindgen(js_class = DID)] @@ -81,21 +80,9 @@ impl WasmDID { pub fn to_string(&self) -> String { self.0.to_string() } - - /// Deserializes a JSON object as `DID`. - #[wasm_bindgen(js_name = fromJSON)] - pub fn from_json(json_value: JsValue) -> Result { - json_value.into_serde().map(Self).wasm_result() - } - - /// Serializes a `DID` as a JSON object. - #[wasm_bindgen(js_name = toJSON)] - pub fn to_json(&self) -> JsValue { - // This must match the serialization of IotaDID for UWasmDID to work. - JsValue::from_str(self.0.as_str()) - } } +impl_wasm_json!(WasmDID, DID); impl_wasm_clone!(WasmDID, DID); impl From for WasmDID { diff --git a/bindings/wasm/src/did/wasm_did_url.rs b/bindings/wasm/src/did/wasm_did_url.rs index 067f61e9d9..353677671b 100644 --- a/bindings/wasm/src/did/wasm_did_url.rs +++ b/bindings/wasm/src/did/wasm_did_url.rs @@ -12,7 +12,6 @@ use crate::error::WasmResult; /// @typicalname didUrl #[wasm_bindgen(js_name = DIDUrl, inspectable)] -#[derive(Clone, Debug, PartialEq)] pub struct WasmDIDUrl(pub(crate) IotaDIDUrl); #[wasm_bindgen(js_class = DIDUrl)] @@ -91,14 +90,9 @@ impl WasmDIDUrl { pub fn to_string(&self) -> String { self.0.to_string() } - - /// Serializes a `DIDUrl` as a JSON object. - #[wasm_bindgen(js_name = toJSON)] - pub fn to_json(&self) -> Result { - JsValue::from_serde(&self.0).wasm_result() - } } +impl_wasm_json!(WasmDIDUrl, DIDUrl); impl_wasm_clone!(WasmDIDUrl, DIDUrl); impl From for WasmDIDUrl { diff --git a/bindings/wasm/src/did/wasm_diff_message.rs b/bindings/wasm/src/did/wasm_diff_message.rs index 6863d3c214..e10fde8383 100644 --- a/bindings/wasm/src/did/wasm_diff_message.rs +++ b/bindings/wasm/src/did/wasm_diff_message.rs @@ -103,24 +103,9 @@ impl WasmDiffMessage { pub fn merge(&self, document: &WasmDocument) -> Result { self.0.merge(&document.0).map(WasmDocument).wasm_result() } - - /// Serializes a `DiffMessage` as a JSON object. - /// - /// @deprecated since 0.5.0, diff chain features are slated for removal. - #[wasm_bindgen(js_name = toJSON)] - pub fn to_json(&self) -> Result { - JsValue::from_serde(&self.0).wasm_result() - } - - /// Deserializes a `DiffMessage` from a JSON object. - /// - /// @deprecated since 0.5.0, diff chain features are slated for removal. - #[wasm_bindgen(js_name = fromJSON)] - pub fn from_json(json: &JsValue) -> Result { - json.into_serde().map(Self).wasm_result() - } } +impl_wasm_json!(WasmDiffMessage, DiffMessage); impl_wasm_clone!(WasmDiffMessage, DiffMessage); impl From for WasmDiffMessage { diff --git a/bindings/wasm/src/did/wasm_document.rs b/bindings/wasm/src/did/wasm_document.rs index a93b35a60b..ea278f2dc4 100644 --- a/bindings/wasm/src/did/wasm_document.rs +++ b/bindings/wasm/src/did/wasm_document.rs @@ -50,7 +50,6 @@ use crate::error::WasmResult; // ============================================================================= #[wasm_bindgen(js_name = Document, inspectable)] -#[derive(Clone, Debug)] pub struct WasmDocument(pub(crate) IotaDocument); #[wasm_bindgen(js_class = Document)] @@ -660,24 +659,9 @@ impl WasmDocument { self.0.unrevoke_credentials(&query, indices.as_slice()).wasm_result() } - - // =========================================================================== - // JSON - // =========================================================================== - - /// Serializes a `Document` as a JSON object. - #[wasm_bindgen(js_name = toJSON)] - pub fn to_json(&self) -> Result { - JsValue::from_serde(&self.0).wasm_result() - } - - /// Deserializes a `Document` from a JSON object. - #[wasm_bindgen(js_name = fromJSON)] - pub fn from_json(json: &JsValue) -> Result { - json.into_serde().map(Self).wasm_result() - } } +impl_wasm_json!(WasmDocument, Document); impl_wasm_clone!(WasmDocument, Document); impl From for WasmDocument { diff --git a/bindings/wasm/src/did/wasm_document_metadata.rs b/bindings/wasm/src/did/wasm_document_metadata.rs index 0d1d179205..27f3987b79 100644 --- a/bindings/wasm/src/did/wasm_document_metadata.rs +++ b/bindings/wasm/src/did/wasm_document_metadata.rs @@ -11,7 +11,6 @@ use crate::common::WasmTimestamp; /// Additional attributes related to an IOTA DID Document. #[wasm_bindgen(js_name = DocumentMetadata, inspectable)] -#[derive(Clone, Debug, PartialEq)] pub struct WasmDocumentMetadata(pub(crate) IotaDocumentMetadata); // NOTE: these properties are read-only (no setters) to prevent bugs where a clone of the metadata @@ -36,6 +35,7 @@ impl WasmDocumentMetadata { } } +impl_wasm_json!(WasmDocumentMetadata, DocumentMetadata); impl_wasm_clone!(WasmDocumentMetadata, DocumentMetadata); impl From for WasmDocumentMetadata { diff --git a/bindings/wasm/src/did/wasm_method_data.rs b/bindings/wasm/src/did/wasm_method_data.rs index 054d379311..d040fbebf7 100644 --- a/bindings/wasm/src/did/wasm_method_data.rs +++ b/bindings/wasm/src/did/wasm_method_data.rs @@ -9,7 +9,6 @@ use crate::error::WasmResult; /// Supported verification method data formats. #[wasm_bindgen(js_name = MethodData, inspectable)] -#[derive(Clone, Debug)] pub struct WasmMethodData(pub(crate) MethodData); #[wasm_bindgen(js_class = MethodData)] @@ -37,20 +36,11 @@ impl WasmMethodData { pub fn try_decode(&self) -> Result> { self.0.try_decode().wasm_result() } - - /// Serializes a `MethodData` object as a JSON object. - #[wasm_bindgen(js_name = toJSON)] - pub fn to_json(&self) -> Result { - JsValue::from_serde(&self.0).wasm_result() - } - - /// Deserializes a `MethodData` object from a JSON object. - #[wasm_bindgen(js_name = fromJSON)] - pub fn from_json(json: &JsValue) -> Result { - json.into_serde().map(Self).wasm_result() - } } +impl_wasm_json!(WasmMethodData, MethodData); +impl_wasm_clone!(WasmMethodData, MethodData); + impl From for MethodData { fn from(data: WasmMethodData) -> Self { data.0 @@ -62,5 +52,3 @@ impl From for WasmMethodData { WasmMethodData(data) } } - -impl_wasm_clone!(WasmMethodData, MethodData); diff --git a/bindings/wasm/src/did/wasm_method_scope.rs b/bindings/wasm/src/did/wasm_method_scope.rs index 9cec5e0df5..8764b342bb 100644 --- a/bindings/wasm/src/did/wasm_method_scope.rs +++ b/bindings/wasm/src/did/wasm_method_scope.rs @@ -4,9 +4,6 @@ use identity_iota::did::MethodScope; use wasm_bindgen::prelude::*; -use crate::error::Result; -use crate::error::WasmResult; - #[wasm_bindgen] extern "C" { // Workaround for lack of Option<&Type>/&Option support. @@ -19,7 +16,6 @@ extern "C" { /// Supported verification method types. #[wasm_bindgen(js_name = MethodScope, inspectable)] -#[derive(Clone, Debug)] pub struct WasmMethodScope(pub(crate) MethodScope); #[wasm_bindgen(js_class = MethodScope)] @@ -60,18 +56,7 @@ impl WasmMethodScope { pub fn to_string(&self) -> String { self.0.to_string() } - - /// Serializes a `MethodScope` object as a JSON object. - #[wasm_bindgen(js_name = toJSON)] - pub fn to_json(&self) -> Result { - JsValue::from_serde(&self.0).wasm_result() - } - - /// Deserializes a `MethodScope` object from a JSON object. - #[wasm_bindgen(js_name = fromJSON)] - pub fn from_json(json: &JsValue) -> Result { - json.into_serde().map(Self).wasm_result() - } } +impl_wasm_json!(WasmMethodScope, MethodScope); impl_wasm_clone!(WasmMethodScope, MethodScope); diff --git a/bindings/wasm/src/did/wasm_method_type.rs b/bindings/wasm/src/did/wasm_method_type.rs index 142aa6043d..7256e43f6a 100644 --- a/bindings/wasm/src/did/wasm_method_type.rs +++ b/bindings/wasm/src/did/wasm_method_type.rs @@ -4,12 +4,8 @@ use identity_iota::did::MethodType; use wasm_bindgen::prelude::*; -use crate::error::Result; -use crate::error::WasmResult; - /// Supported verification method types. #[wasm_bindgen(js_name = MethodType, inspectable)] -#[derive(Clone, Debug)] pub struct WasmMethodType(pub(crate) MethodType); #[wasm_bindgen(js_class = MethodType)] @@ -24,18 +20,6 @@ impl WasmMethodType { WasmMethodType(MethodType::X25519KeyAgreementKey2019) } - /// Serializes a `MethodType` object as a JSON object. - #[wasm_bindgen(js_name = toJSON)] - pub fn to_json(&self) -> Result { - JsValue::from_serde(&self.0).wasm_result() - } - - /// Deserializes a `MethodType` object from a JSON object. - #[wasm_bindgen(js_name = fromJSON)] - pub fn from_json(json: &JsValue) -> Result { - json.into_serde().map(Self).wasm_result() - } - /// Returns the `MethodType` as a string. #[allow(clippy::inherent_to_string)] #[wasm_bindgen(js_name = toString)] @@ -45,6 +29,9 @@ impl WasmMethodType { } } +impl_wasm_json!(WasmMethodType, MethodType); +impl_wasm_clone!(WasmMethodType, MethodType); + impl From for MethodType { fn from(wasm_method_type: WasmMethodType) -> Self { wasm_method_type.0 @@ -56,5 +43,3 @@ impl From for WasmMethodType { WasmMethodType(method_type) } } - -impl_wasm_clone!(WasmMethodType, MethodType); diff --git a/bindings/wasm/src/did/wasm_resolved_document.rs b/bindings/wasm/src/did/wasm_resolved_document.rs index e7b4e3208a..e8fefccb5b 100644 --- a/bindings/wasm/src/did/wasm_resolved_document.rs +++ b/bindings/wasm/src/did/wasm_resolved_document.rs @@ -15,7 +15,6 @@ use crate::error::WasmResult; /// An IOTA DID document resolved from the Tangle. Represents an integration chain message possibly /// merged with one or more `DiffMessages`. #[wasm_bindgen(js_name = ResolvedDocument, inspectable)] -#[derive(Clone, Debug)] pub struct WasmResolvedDocument(pub(crate) ResolvedIotaDocument); #[wasm_bindgen] @@ -116,24 +115,9 @@ impl WasmResolvedDocument { self.0.integration_message_id = message_id; Ok(()) } - - // =========================================================================== - // JSON - // =========================================================================== - - /// Serializes a `Document` object as a JSON object. - #[wasm_bindgen(js_name = toJSON)] - pub fn to_json(&self) -> Result { - JsValue::from_serde(&self.0).wasm_result() - } - - /// Deserializes a `Document` object from a JSON object. - #[wasm_bindgen(js_name = fromJSON)] - pub fn from_json(json: &JsValue) -> Result { - json.into_serde().map(Self).wasm_result() - } } +impl_wasm_json!(WasmResolvedDocument, ResolvedDocument); impl_wasm_clone!(WasmResolvedDocument, ResolvedDocument); impl From for WasmResolvedDocument { diff --git a/bindings/wasm/src/did/wasm_service.rs b/bindings/wasm/src/did/wasm_service.rs index 63a90b7f1b..3cc11c92b5 100644 --- a/bindings/wasm/src/did/wasm_service.rs +++ b/bindings/wasm/src/did/wasm_service.rs @@ -70,20 +70,9 @@ impl WasmService { pub fn properties(&self) -> Result { MapStringAny::try_from(self.0.properties()) } - - /// Serializes a `Service` object as a JSON object. - #[wasm_bindgen(js_name = toJSON)] - pub fn to_json(&self) -> Result { - JsValue::from_serde(&self.0).wasm_result() - } - - /// Deserializes a `Service` object from a JSON object. - #[wasm_bindgen(js_name = fromJSON)] - pub fn from_json(value: &JsValue) -> Result { - value.into_serde().map(Self).wasm_result() - } } +impl_wasm_json!(WasmService, Service); impl_wasm_clone!(WasmService, Service); impl From for WasmService { diff --git a/bindings/wasm/src/did/wasm_verification_method.rs b/bindings/wasm/src/did/wasm_verification_method.rs index dff3e72ad8..8f48858e03 100644 --- a/bindings/wasm/src/did/wasm_verification_method.rs +++ b/bindings/wasm/src/did/wasm_verification_method.rs @@ -14,7 +14,6 @@ use crate::error::Result; use crate::error::WasmResult; #[wasm_bindgen(js_name = VerificationMethod, inspectable)] -#[derive(Clone, Debug, PartialEq)] pub struct WasmVerificationMethod(pub(crate) IotaVerificationMethod); #[wasm_bindgen(js_class = VerificationMethod)] @@ -62,20 +61,9 @@ impl WasmVerificationMethod { pub fn data(&self) -> WasmMethodData { WasmMethodData::from(self.0.data().clone()) } - - /// Serializes a `VerificationMethod` object as a JSON object. - #[wasm_bindgen(js_name = toJSON)] - pub fn to_json(&self) -> Result { - JsValue::from_serde(&self.0).wasm_result() - } - - /// Deserializes a `VerificationMethod` object from a JSON object. - #[wasm_bindgen(js_name = fromJSON)] - pub fn from_json(value: &JsValue) -> Result { - value.into_serde().map(Self).wasm_result() - } } +impl_wasm_json!(WasmVerificationMethod, VerificationMethod); impl_wasm_clone!(WasmVerificationMethod, VerificationMethod); impl From for WasmVerificationMethod { diff --git a/bindings/wasm/src/did/wasm_verifier_options.rs b/bindings/wasm/src/did/wasm_verifier_options.rs index 0527131615..71fdbee98e 100644 --- a/bindings/wasm/src/did/wasm_verifier_options.rs +++ b/bindings/wasm/src/did/wasm_verifier_options.rs @@ -10,7 +10,6 @@ use crate::error::WasmResult; /// Holds additional proof verification options. /// See `IVerifierOptions`. #[wasm_bindgen(js_name = VerifierOptions, inspectable)] -#[derive(Clone, Debug)] pub struct WasmVerifierOptions(pub(crate) VerifierOptions); #[wasm_bindgen(js_class = VerifierOptions)] @@ -29,20 +28,11 @@ impl WasmVerifierOptions { pub fn default() -> WasmVerifierOptions { WasmVerifierOptions(VerifierOptions::default()) } - - /// Serializes a `VerifierOptions` as a JSON object. - #[wasm_bindgen(js_name = toJSON)] - pub fn to_json(&self) -> Result { - JsValue::from_serde(&self.0).wasm_result() - } - - /// Deserializes a `VerifierOptions` from a JSON object. - #[wasm_bindgen(js_name = fromJSON)] - pub fn from_json(json: &JsValue) -> Result { - json.into_serde().map(Self).wasm_result() - } } +impl_wasm_json!(WasmVerifierOptions, VerifierOptions); +impl_wasm_clone!(WasmVerifierOptions, VerifierOptions); + impl From for WasmVerifierOptions { fn from(options: VerifierOptions) -> Self { WasmVerifierOptions(options) @@ -55,8 +45,6 @@ impl From for VerifierOptions { } } -impl_wasm_clone!(WasmVerifierOptions, VerifierOptions); - /// Duck-typed interface to allow creating `VerifierOptions` easily. #[wasm_bindgen] extern "C" { diff --git a/bindings/wasm/src/macros.rs b/bindings/wasm/src/macros.rs index c72165b3ed..ecc99a8082 100644 --- a/bindings/wasm/src/macros.rs +++ b/bindings/wasm/src/macros.rs @@ -21,3 +21,25 @@ macro_rules! impl_wasm_clone { } }; } + +#[macro_export] +macro_rules! impl_wasm_json { + ($wasm_class:ident, $js_class:ident) => { + #[wasm_bindgen(js_class = $js_class)] + impl $wasm_class { + /// Serializes this to a JSON object. + #[wasm_bindgen(js_name = toJSON)] + pub fn to_json(&self) -> $crate::error::Result { + use $crate::error::WasmResult; + JsValue::from_serde(&self.0).wasm_result() + } + + /// Deserializes an instance from a JSON object. + #[wasm_bindgen(js_name = fromJSON)] + pub fn from_json(json: &JsValue) -> $crate::error::Result<$wasm_class> { + use $crate::error::WasmResult; + json.into_serde().map(Self).wasm_result() + } + } + }; +} diff --git a/bindings/wasm/src/tangle/client.rs b/bindings/wasm/src/tangle/client.rs index c9bf9d7c81..0560a40b4e 100644 --- a/bindings/wasm/src/tangle/client.rs +++ b/bindings/wasm/src/tangle/client.rs @@ -36,7 +36,6 @@ use crate::tangle::WasmNetwork; use crate::tangle::WasmReceipt; #[wasm_bindgen(js_name = Client)] -#[derive(Debug)] pub struct WasmClient { pub(crate) client: Rc, } diff --git a/bindings/wasm/src/tangle/explorer.rs b/bindings/wasm/src/tangle/explorer.rs index cab11d3c20..d075e83bea 100644 --- a/bindings/wasm/src/tangle/explorer.rs +++ b/bindings/wasm/src/tangle/explorer.rs @@ -13,7 +13,6 @@ use crate::error::Result; use crate::error::WasmResult; #[wasm_bindgen(js_name = ExplorerUrl)] -#[derive(Clone, Debug)] pub struct WasmExplorerUrl(ExplorerUrl); #[wasm_bindgen(js_class = ExplorerUrl)] @@ -66,6 +65,8 @@ impl WasmExplorerUrl { } } +impl_wasm_json!(WasmExplorerUrl, ExplorerUrl); + impl From for ExplorerUrl { fn from(other: WasmExplorerUrl) -> Self { other.0 diff --git a/bindings/wasm/src/tangle/network.rs b/bindings/wasm/src/tangle/network.rs index 7ace91d12d..fc88b166b4 100644 --- a/bindings/wasm/src/tangle/network.rs +++ b/bindings/wasm/src/tangle/network.rs @@ -47,20 +47,9 @@ impl WasmNetwork { pub fn to_string(&self) -> String { self.0.name_str().to_owned() } - - /// Serializes a `Network` as a JSON object. - #[wasm_bindgen(js_name = toJSON)] - pub fn to_json(&self) -> Result { - JsValue::from_serde(&self.0).wasm_result() - } - - /// Deserializes a `Network` from a JSON object. - #[wasm_bindgen(js_name = fromJSON)] - pub fn from_json(json: &JsValue) -> Result { - json.into_serde().map(Self).wasm_result() - } } +impl_wasm_json!(WasmNetwork, Network); impl_wasm_clone!(WasmNetwork, Network); impl From for Network { diff --git a/bindings/wasm/src/tangle/receipt.rs b/bindings/wasm/src/tangle/receipt.rs index 7164139152..b600219689 100644 --- a/bindings/wasm/src/tangle/receipt.rs +++ b/bindings/wasm/src/tangle/receipt.rs @@ -4,12 +4,9 @@ use identity_iota::client::Receipt; use wasm_bindgen::prelude::*; -use crate::error::Result; -use crate::error::WasmResult; use crate::tangle::WasmNetwork; #[wasm_bindgen(js_name = Receipt, inspectable)] -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] pub struct WasmReceipt(pub(crate) Receipt); // Workaround for Typescript type annotations on async function returns. @@ -46,20 +43,9 @@ impl WasmReceipt { // NOTE: do not return u64 to avoid BigInt64Array/BigUint64Array compatibility issues. self.0.nonce().to_string() } - - /// Serializes a `Receipt` as a JSON object. - #[wasm_bindgen(js_name = toJSON)] - pub fn to_json(&self) -> Result { - JsValue::from_serde(&self.0).wasm_result() - } - - /// Deserializes a `Receipt` from a JSON object. - #[wasm_bindgen(js_name = fromJSON)] - pub fn from_json(json: &JsValue) -> Result { - json.into_serde().map(Self).wasm_result() - } } +impl_wasm_json!(WasmReceipt, Receipt); impl_wasm_clone!(WasmReceipt, Receipt); impl From for WasmReceipt { diff --git a/bindings/wasm/src/tangle/resolver.rs b/bindings/wasm/src/tangle/resolver.rs index 531f6ce550..dab1f124ab 100644 --- a/bindings/wasm/src/tangle/resolver.rs +++ b/bindings/wasm/src/tangle/resolver.rs @@ -10,6 +10,8 @@ use identity_iota::client::ClientBuilder; use identity_iota::client::ResolvedIotaDocument; use identity_iota::client::Resolver; use identity_iota::client::ResolverBuilder; +use identity_iota::credential::Presentation; +use identity_iota::credential::PresentationValidationOptions; use identity_iota::credential::PresentationValidator; use identity_iota::credential::ValidatorDocument; use identity_iota::iota_core::IotaDID; @@ -261,8 +263,8 @@ impl WasmResolver { let issuers: Option> = issuers.map(|js| js.into_serde().wasm_result()).transpose()?; let resolver: Rc>> = Rc::clone(&self.0); - let presentation: WasmPresentation = presentation.clone(); - let options: WasmPresentationValidationOptions = options.clone(); + let presentation: Presentation = presentation.0.clone(); + let options: PresentationValidationOptions = options.0.clone(); let promise: Promise = future_to_promise(async move { let issuer_refs: Option> = issuers @@ -270,8 +272,8 @@ impl WasmResolver { .map(|issuers| issuers.iter().map(ValidatorDocument::as_validator).collect()); resolver .verify_presentation( - &presentation.0, - &options.0, + &presentation, + &options, fail_fast.into(), holder.as_ref().map(ValidatorDocument::as_validator), issuer_refs.as_deref(), diff --git a/bindings/wasm/tests/wasm.rs b/bindings/wasm/tests/wasm.rs index f2fd542728..58c88af649 100644 --- a/bindings/wasm/tests/wasm.rs +++ b/bindings/wasm/tests/wasm.rs @@ -273,7 +273,7 @@ fn test_did_serde() { // Check WasmDID deserialization. { let wasm_did: WasmDID = WasmDID::from(expected.clone()); - let de: IotaDID = wasm_did.to_json().into_serde().unwrap(); + let de: IotaDID = wasm_did.to_json().unwrap().into_serde().unwrap(); assert_eq!(de, expected); } @@ -305,7 +305,7 @@ fn test_sign_document() { let document1: WasmDocument = WasmDocument::new(&keypair1, None, None).unwrap(); // Replace the default signing method. - let mut document2: WasmDocument = document1.clone(); + let mut document2: WasmDocument = document1.deep_clone(); let keypair2: WasmKeyPair = WasmKeyPair::new(WasmKeyType::Ed25519).unwrap(); let method: WasmVerificationMethod = WasmVerificationMethod::new( &document2.id(), From 180519c3e10edec6762bd654b05996ca0a58afeb Mon Sep 17 00:00:00 2001 From: "Oliver E. Anderson" Date: Mon, 25 Jul 2022 18:34:04 +0200 Subject: [PATCH 28/89] StardustDID (#949) --- README.md | 4 +- identity_stardust/Cargo.toml | 6 +- identity_stardust/README.md | 2 +- identity_stardust/src/did/mod.rs | 7 + identity_stardust/src/did/stardust_did.rs | 910 ++++++++++++++++++ identity_stardust/src/error.rs | 2 + identity_stardust/src/lib.rs | 6 + identity_stardust/src/network/mod.rs | 5 + identity_stardust/src/network/network_name.rs | 123 +++ 9 files changed, 1060 insertions(+), 5 deletions(-) create mode 100644 identity_stardust/src/did/mod.rs create mode 100644 identity_stardust/src/did/stardust_did.rs create mode 100644 identity_stardust/src/network/mod.rs create mode 100644 identity_stardust/src/network/network_name.rs diff --git a/README.md b/README.md index 4fd9e6422d..d8f7db8ce6 100644 --- a/README.md +++ b/README.md @@ -49,8 +49,8 @@ The individual libraries are developed to be agnostic about the utilized [Distri ## Prerequisites -- [Rust](https://www.rust-lang.org/) (>= 1.60) -- [Cargo](https://doc.rust-lang.org/cargo/) (>= 1.60) +- [Rust](https://www.rust-lang.org/) (>= 1.62) +- [Cargo](https://doc.rust-lang.org/cargo/) (>= 1.62) ## Getting Started diff --git a/identity_stardust/Cargo.toml b/identity_stardust/Cargo.toml index a431737f50..e3d4e10697 100644 --- a/identity_stardust/Cargo.toml +++ b/identity_stardust/Cargo.toml @@ -8,17 +8,19 @@ keywords = ["iota", "tangle", "stardust", "identity"] license = "Apache-2.0" readme = "../README.md" repository = "https://github.com/iotaledger/identity.rs" +rust-version = "1.62" description = "An IOTA Ledger integration for the identity.rs library." - [workspace] [dependencies] identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } identity_credential = { version = "=0.6.0", path = "../identity_credential", default-features = false } identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } + num-derive = { version = "0.3", default-features = false } num-traits = { version = "0.2", default-features = false, features = ["std"] } -once_cell = { version = "1.13", default-features = false } +once_cell = { version = "1", default-features = false, features = ["std"] } +prefix-hex = { version = "0.4", default-features = false } serde = { version = "1.0", default-features = false, features = ["std", "derive"] } strum = { version = "0.21", features = ["derive"] } thiserror = { version = "1.0", default-features = false } diff --git a/identity_stardust/README.md b/identity_stardust/README.md index 6363517c21..67eac9593c 100644 --- a/identity_stardust/README.md +++ b/identity_stardust/README.md @@ -2,4 +2,4 @@ This is a work-in-progress intended to replace the `did:iota` DID Method. -`cargo run --example create_did` +`cargo run --example create_did` \ No newline at end of file diff --git a/identity_stardust/src/did/mod.rs b/identity_stardust/src/did/mod.rs new file mode 100644 index 0000000000..9a182e6015 --- /dev/null +++ b/identity_stardust/src/did/mod.rs @@ -0,0 +1,7 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +mod stardust_did; +pub use stardust_did::StardustDID; +// TODO: Uncomment once the `document` module gets refactored to use the types from this module. +//pub use stardust_did::StardustDIDUrl; diff --git a/identity_stardust/src/did/stardust_did.rs b/identity_stardust/src/did/stardust_did.rs new file mode 100644 index 0000000000..37bbb004b5 --- /dev/null +++ b/identity_stardust/src/did/stardust_did.rs @@ -0,0 +1,910 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use core::convert::TryFrom; +use core::fmt::Debug; +use core::fmt::Display; +use core::fmt::Formatter; +use core::str::FromStr; +use identity_core::common::KeyComparable; +use serde::Deserialize; +use serde::Serialize; + +use identity_did::did::BaseDIDUrl; +use identity_did::did::CoreDID; +use identity_did::did::DIDError; +use identity_did::did::DIDUrl; +use identity_did::did::DID; + +use crate::NetworkName; + +pub type Result = std::result::Result; + +// The length of an AliasID, which is a BLAKE2b-256 hash (32-bytes). +const TAG_BYTES_LEN: usize = 32; + +/// A DID conforming to the IOTA UTXO DID method specification. +/// +/// This is a thin wrapper around the [`DID`][`CoreDID`] type from the +/// [`identity_did`][`identity_did`] crate. +#[derive(Clone, Hash, Debug, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)] +#[repr(transparent)] +#[serde(into = "CoreDID", try_from = "CoreDID")] +pub struct StardustDID(CoreDID); + +impl StardustDID { + /// The URL scheme for Decentralized Identifiers. + pub const SCHEME: &'static str = CoreDID::SCHEME; + + /// The IOTA UTXO DID method name (`"stardust"`). + // TODO: This will be changed to `iota` in the future. + pub const METHOD: &'static str = "stardust"; + + /// The default Tangle network (`"main"`). + pub const DEFAULT_NETWORK: &'static str = "main"; + + /// Converts an owned [`CoreDID`] to a [`StardustDID`]. + /// + /// # Errors + /// + /// Returns `Err` if the input does not conform to the [`StardustDID`] specification. + pub fn try_from_core(did: CoreDID) -> Result { + Self::check_validity(&did)?; + + Ok(Self(Self::normalize(did))) + } + + /// Constructs a new [`StardustDID`] from a byte representation of the tag and the given network name. + /// + /// See also [`StardustDID::placeholder`]. + /// + /// # Example + /// + /// ``` + /// # use identity_did::did::DID; + /// # use identity_stardust::NetworkName; + /// # use identity_stardust::StardustDID; + /// # + /// let did = StardustDID::new(&[1;32], &NetworkName::try_from("smr").unwrap()); + /// assert_eq!(did.as_str(), "did:stardust:smr:0x0101010101010101010101010101010101010101010101010101010101010101"); + pub fn new(bytes: &[u8; 32], network_name: &NetworkName) -> Self { + let tag = prefix_hex::encode(bytes); + let did: String = format!("did:{}:{}:{}", Self::METHOD, network_name, tag); + + Self::parse(did).expect("DIDs constructed with new should be valid") + } + + /// Parses an [`StardustDID`] from the given `input`. + /// + /// # Errors + /// + /// Returns `Err` if the input does not conform to the [`StardustDID`] specification. + pub fn parse(input: impl AsRef) -> Result { + CoreDID::parse(input).and_then(Self::try_from_core) + } + + /// Creates a new placeholder [`StardustDID`] with the given network name. + /// + /// # Example + /// + /// ``` + /// # use identity_did::did::DID; + /// # use identity_stardust::NetworkName; + /// # use identity_stardust::StardustDID; + /// # + /// let placeholder = StardustDID::placeholder(&NetworkName::try_from("smr").unwrap()); + /// assert_eq!(placeholder.as_str(), "did:stardust:smr:0x0000000000000000000000000000000000000000000000000000000000000000"); + pub fn placeholder(network_name: &NetworkName) -> Self { + Self::new(&[0; 32], network_name) + } + + // Check if the tag matches a potential alias_id + fn check_tag_str(tag: &str) -> Result<()> { + prefix_hex::decode::<[u8; TAG_BYTES_LEN]>(tag) + .map_err(|_| DIDError::InvalidMethodId) + .map(|_| ()) + } + + // Normalizes the DID `method_id` by removing the default network segment if present. + // + // E.g. + // - `"did:stardust:main:123" -> "did:stardust:123"` is normalized + // - `"did:stardust:dev:123" -> "did:stardust:dev:123"` is unchanged + // TODO: Remove the lint once this bug in clippy has been fixed. Without to_owned a mutable reference will be aliased. + #[allow(clippy::unnecessary_to_owned)] + fn normalize(mut did: CoreDID) -> CoreDID { + let method_id = did.method_id(); + let (network, tag) = Self::denormalized_components(method_id); + if tag.len() == method_id.len() || network != Self::DEFAULT_NETWORK { + did + } else { + did + .set_method_id(tag.to_owned()) + .expect("normalizing a valid CoreDID should be Ok"); + did + } + } + + /// Checks if the given `DID` has a valid [`StardustDID`] network name. + /// + /// # Errors + /// + /// Returns `Err` if the input is not a valid network name according to the [`StardustDID`] method specification. + pub fn check_network(did: &D) -> Result<()> { + let network_name = Self::denormalized_components(did.method_id()).0; + NetworkName::validate_network_name(network_name).map_err(|_| DIDError::Other("invalid network name")) + } + + /// Checks if the given `DID` is syntactically valid according to the [`StardustDID`] method specification. + /// + /// # Errors + /// + /// Returns `Err` if the input is not a syntactically valid [`StardustDID`]. + pub fn check_validity(did: &D) -> Result<()> { + Self::check_method(did).and_then(|_| Self::check_method_id(did)) + } + + /// Checks if the given `DID` has a valid [`StardustDID`] `method` (i.e. `"stardust"`). + /// + /// # Errors + /// + /// Returns `Err` if the input represents another method. + // TODO: Change the naming in the docs once we remove the code for the current IOTA method. + pub fn check_method(did: &D) -> Result<()> { + (did.method() == Self::METHOD) + .then_some(()) + .ok_or(DIDError::InvalidMethodName) + } + + /// Checks if the given `DID` has a valid [`StardustDID`] `method_id`. + /// + /// # Errors + /// + /// Returns `Err` if the input does not have a [`StardustDID`] compliant method id. + pub fn check_method_id(did: &D) -> Result<()> { + let (network, tag) = Self::denormalized_components(did.method_id()); + NetworkName::validate_network_name(network) + .map_err(|_| DIDError::InvalidMethodId) + .and_then(|_| Self::check_tag_str(tag)) + } + + /// Returns a `bool` indicating if the given `DID` is valid according to the + /// [`StardustDID`] method specification. + /// + /// Equivalent to `Self::check_validity(did).is_ok()`. + pub fn is_valid(did: &CoreDID) -> bool { + Self::check_validity(did).is_ok() + } + + /// Returns the IOTA `network` name of the `DID`. + pub fn network_str(&self) -> &str { + Self::denormalized_components(self.method_id()).0 + } + + /// foo:bar -> (foo,bar) + /// foo:bar:baz -> (foo, bar:baz) + /// foo -> (StardustDID::DEFAULT_NETWORK.as_ref(), foo) + #[inline(always)] + fn denormalized_components(input: &str) -> (&str, &str) { + input + .find(':') + .map(|idx| input.split_at(idx)) + .map(|(network, tail)| (network, &tail[1..])) + // Self::DEFAULT_NETWORK is built from a static reference so unwrapping is fine + .unwrap_or((Self::DEFAULT_NETWORK, input)) + } + + /// Returns the unique tag of the `DID`. + pub fn tag(&self) -> &str { + Self::denormalized_components(self.method_id()).1 + } + + /// Replace the network name of this [`StardustDID`] leaving all other segments (did, method, tag) intact. + pub fn with_network_name(mut self, name: NetworkName) -> Self { + let new_method_id: String = format!("{}:{}", name, self.tag()); + // unwrap is fine as we are only replacing the network + self.0.set_method_id(new_method_id).unwrap(); + self + } +} + +impl DID for StardustDID { + /// Returns the [`StardustDID`] scheme. See [`DID::SCHEME`]. + fn scheme(&self) -> &'static str { + self.0.scheme() + } + + /// Returns the [`StardustDID`] authority. + fn authority(&self) -> &str { + self.0.authority() + } + + /// Returns the [`StardustDID`] method name. + fn method(&self) -> &str { + self.0.method() + } + + /// Returns the [`StardustDID`] method-specific ID. + fn method_id(&self) -> &str { + self.0.method_id() + } + + /// Returns the serialized [`StardustDID`]. + /// + /// This is fast since the serialized value is stored in the [`DID`]. + fn as_str(&self) -> &str { + self.0.as_str() + } + + /// Consumes the [`StardustDID`] and returns the serialization. + fn into_string(self) -> String { + self.0.into_string() + } + + // TODO: Link [`StardustDIDUrl`] after `document` has been refactored to use the types in this module. + /// Creates a new DIDUrl by joining with a relative DID Url string. + /// + /// # Errors + /// + /// Returns `Err` if any base or relative DID segments are invalid. + fn join(self, segment: impl AsRef) -> std::result::Result, DIDError> { + self.into_url().join(segment) + } + + fn to_url(&self) -> DIDUrl { + DIDUrl::new(self.clone(), None) + } + + fn into_url(self) -> DIDUrl { + DIDUrl::new(self, None) + } +} + +impl FromStr for StardustDID { + type Err = DIDError; + + fn from_str(s: &str) -> std::result::Result { + Self::parse(s) + } +} + +impl TryFrom<&str> for StardustDID { + type Error = DIDError; + + fn try_from(other: &str) -> std::result::Result { + Self::parse(other) + } +} + +impl TryFrom for StardustDID { + type Error = DIDError; + + fn try_from(other: String) -> std::result::Result { + Self::parse(other) + } +} + +impl Display for StardustDID { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + write!(f, "{}", self.0) + } +} + +impl From for CoreDID { + fn from(id: StardustDID) -> Self { + id.0 + } +} + +impl From for String { + fn from(did: StardustDID) -> Self { + did.into_string() + } +} + +impl TryFrom for StardustDID { + type Error = DIDError; + fn try_from(value: CoreDID) -> std::result::Result { + Self::try_from_core(value) + } +} + +impl TryFrom for StardustDID { + type Error = DIDError; + + fn try_from(other: BaseDIDUrl) -> Result { + let core_did: CoreDID = CoreDID::try_from(other)?; + Self::try_from(core_did) + } +} + +impl AsRef for StardustDID { + fn as_ref(&self) -> &CoreDID { + &self.0 + } +} + +impl KeyComparable for StardustDID { + type Key = CoreDID; + + #[inline] + fn key(&self) -> &Self::Key { + self.as_ref() + } +} + +#[cfg(test)] +mod tests { + + use iota_client::block::output::AliasId; + use iota_client::block::output::OutputId; + use iota_client::block::output::OUTPUT_INDEX_RANGE; + use iota_client::block::payload::transaction::TransactionId; + use once_cell::sync::Lazy; + use proptest::strategy::Strategy; + use proptest::*; + + use super::*; + + const INITIAL_ALIAS_ID_STR: &str = "0x0000000000000000000000000000000000000000000000000000000000000000"; + + // =========================================================================================================================== + // Reusable constants and statics + // =========================================================================================================================== + + // obtained AliasID from a valid OutputID string + // output_id copied from https://github.com/iotaledger/bee/blob/30cab4f02e9f5d72ffe137fd9eb09723b4f0fdb6/bee-block/tests/output_id.rs + // value of AliasID computed from AliasId::from(OutputId).to_string() + const VALID_ALIAS_ID_STR: &str = "0xf29dd16310c2100fd1bf568b345fb1cc14d71caa3bd9b5ad735d2bd6d455ca3b"; + + const LEN_VALID_ALIAS_STR: usize = VALID_ALIAS_ID_STR.len(); + + static VALID_STARDUST_DID_STRING: Lazy = + Lazy::new(|| format!("did:{}:{}", StardustDID::METHOD, VALID_ALIAS_ID_STR)); + + // Rules are: at least one character, at most six characters and may only contain digits and/or lowercase ascii + // characters. + const VALID_NETWORK_NAMES: [&str; 13] = [ + StardustDID::DEFAULT_NETWORK, + "main", + "dev", + "smr", + "rms", + "test", + "foo", + "foobar", + "123456", + "0", + "foo42", + "bar123", + "42foo", + ]; + + const INVALID_NETWORK_NAMES: [&str; 10] = [ + "Main", "fOo", "deV", "féta", "", " ", "foo ", " foo", "1234567", "foobar0", + ]; + + static VALID_STARDUST_DID_STRINGS: Lazy> = Lazy::new(|| { + let network_tag_to_did = |network, tag| format!("did:{}:{}:{}", StardustDID::METHOD, network, tag); + + let valid_strings: Vec = VALID_NETWORK_NAMES + .iter() + .flat_map(|network| { + [VALID_ALIAS_ID_STR, INITIAL_ALIAS_ID_STR] + .iter() + .map(move |tag| network_tag_to_did(network, tag)) + }) + .collect(); + + // in principle the previous binding is not necessary (we could have just returned the value), + // but let's just ensure that it contains the expected number of elements first. + assert_eq!(valid_strings.len(), 2 * VALID_NETWORK_NAMES.len()); + + valid_strings + }); + + // =========================================================================================================================== + // Test check_* methods + // =========================================================================================================================== + + #[test] + fn invalid_check_method() { + let did_key_core: CoreDID = CoreDID::parse("did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK").unwrap(); + assert!(matches!( + StardustDID::check_method(&did_key_core), + Err(DIDError::InvalidMethodName) + )); + } + + #[test] + fn valid_check_method() { + let did_stardust_core: CoreDID = CoreDID::parse(&*VALID_STARDUST_DID_STRING).unwrap(); + assert!(StardustDID::check_method(&did_stardust_core).is_ok()); + } + + #[test] + fn valid_check_network() { + let assert_check_network = |input: &str| { + let did_core: CoreDID = + CoreDID::parse(input).unwrap_or_else(|_| panic!("expected {} to parse to a valid CoreDID", input)); + assert!( + StardustDID::check_network(&did_core).is_ok(), + "test: valid_check_network failed with input {}", + input, + ); + }; + + for network_name in VALID_NETWORK_NAMES { + let did_string = format!( + "did:method:{}:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK", + network_name + ); + assert_check_network(&did_string); + } + + assert_check_network("did:method:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"); + } + + #[test] + fn invalid_check_network() { + // Loop over list of network names known to be invalid, attempt to create a CoreDID containing the given network + // name in the method_id sub-string and ensure that `StardustDID::check_network` fails. If the provided network + // name is in conflict with the DID Core spec itself then proceed to the next network name. + + // Ensure that this test is robust to changes in the supplied list of network names, i.e. fail if none of the + // network names can be contained in a generic CoreDID. + + let mut check_network_executed: bool = false; + + for network_name in INVALID_NETWORK_NAMES { + let did_string: String = format!( + "did:method:{}:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK", + network_name + ); + let did_core: CoreDID = { + match CoreDID::parse(&did_string) { + Ok(did_core) => did_core, + Err(_) => continue, + } + }; + + assert!(matches!(StardustDID::check_network(&did_core), Err(DIDError::Other(_)))); + check_network_executed = true; + } + assert!( + check_network_executed, + "StardustDID::check_network` should have been executed" + ); + } + + #[test] + fn valid_check_method_id() { + for input in VALID_STARDUST_DID_STRINGS.iter() { + let did_core: CoreDID = CoreDID::parse(input).unwrap(); + assert!( + StardustDID::check_method_id(&did_core).is_ok(), + "test: valid_check_method_id failed on input {}", + input + ); + } + + // Should also work for DID's of the form: did::: + let did_other_string: String = format!("did:method:{}", VALID_ALIAS_ID_STR); + let did_other_with_network: String = format!("did:method:test:{}", VALID_ALIAS_ID_STR); + let did_other_core: CoreDID = CoreDID::parse(&did_other_string).unwrap(); + let did_other_with_network_core: CoreDID = CoreDID::parse(&did_other_with_network).unwrap(); + + assert!(StardustDID::check_method_id(&did_other_core).is_ok()); + assert!(StardustDID::check_method_id(&did_other_with_network_core).is_ok()); + } + + #[test] + fn invalid_check_method_id() { + let invalid_method_id_strings = [ + // Invalid network name + format!("did:method:1234567:{}", VALID_ALIAS_ID_STR), + // Too many segments + format!("did:method:main:test:{}", VALID_ALIAS_ID_STR), + // Tag is not prefixed + format!("did:method:{}", &VALID_ALIAS_ID_STR.strip_prefix("0x").unwrap()), + // Tag is too long + format!( + "did:method:{}", + &VALID_ALIAS_ID_STR.chars().chain("a".chars()).collect::() + ), + // Tag is too short (omit last character) + format!("did:method:main:{}", &VALID_ALIAS_ID_STR[..65]), + ]; + + for input in invalid_method_id_strings { + let did_core: CoreDID = CoreDID::parse(input).unwrap(); + assert!(matches!( + StardustDID::check_method_id(&did_core), + Err(DIDError::InvalidMethodId) + )); + } + } + + // =========================================================================================================================== + // Test constructors + // =========================================================================================================================== + + #[test] + fn placeholder_produces_a_did_with_expected_string_representation() { + assert_eq!( + StardustDID::placeholder(&NetworkName::try_from(StardustDID::DEFAULT_NETWORK).unwrap()).as_str(), + format!("did:{}:{}", StardustDID::METHOD, INITIAL_ALIAS_ID_STR) + ); + + for name in VALID_NETWORK_NAMES + .iter() + .filter(|name| *name != &StardustDID::DEFAULT_NETWORK) + { + let network_name: NetworkName = NetworkName::try_from(*name).unwrap(); + let did: StardustDID = StardustDID::placeholder(&network_name); + assert_eq!( + did.as_str(), + format!("did:{}:{}:{}", StardustDID::METHOD, name, INITIAL_ALIAS_ID_STR) + ); + } + } + + #[test] + fn normalization_in_constructors() { + let did_with_default_network_string: String = format!( + "did:{}:{}:{}", + StardustDID::METHOD, + StardustDID::DEFAULT_NETWORK, + VALID_ALIAS_ID_STR + ); + let expected_normalization_string_representation: String = + format!("did:{}:{}", StardustDID::METHOD, VALID_ALIAS_ID_STR); + + assert_eq!( + StardustDID::parse(did_with_default_network_string).unwrap().as_str(), + expected_normalization_string_representation + ); + } + + #[test] + fn parse_valid() { + for did_str in VALID_STARDUST_DID_STRINGS.iter() { + assert!(StardustDID::parse(&did_str).is_ok()); + } + } + + #[test] + fn parse_invalid() { + let execute_assertions = |valid_alias_id: &str| { + assert!(matches!( + StardustDID::parse(format!("dod:{}:{}", StardustDID::METHOD, valid_alias_id)), + Err(DIDError::InvalidScheme) + )); + + assert!(matches!( + StardustDID::parse(format!("did:key:{}", valid_alias_id)), + Err(DIDError::InvalidMethodName) + )); + + // invalid network name (exceeded six characters) + assert!(matches!( + StardustDID::parse(format!("did:{}:1234567:{}", StardustDID::METHOD, valid_alias_id)), + Err(DIDError::InvalidMethodId) + )); + + // invalid network name (contains non ascii character é) + assert!(matches!( + StardustDID::parse(format!("did:{}:féta:{}", StardustDID::METHOD, valid_alias_id)), + Err(DIDError::InvalidMethodId) + )); + + // invalid tag + assert!(matches!( + StardustDID::parse(format!("did:{}:", StardustDID::METHOD)), + Err(DIDError::InvalidMethodId) + )); + + // too many segments in method_id + assert!(matches!( + StardustDID::parse(format!("did:{}:test:foo:{}", StardustDID::METHOD, valid_alias_id)), + Err(DIDError::InvalidMethodId) + )); + }; + + execute_assertions(INITIAL_ALIAS_ID_STR); + execute_assertions(VALID_ALIAS_ID_STR); + } + + // =========================================================================================================================== + // Test constructors with randomly generated input + // =========================================================================================================================== + + fn arbitrary_alias_id() -> impl Strategy { + (proptest::prelude::any::<[u8; 32]>(), OUTPUT_INDEX_RANGE).prop_map(|(bytes, idx)| { + let transaction_id: TransactionId = TransactionId::new(bytes); + let output_id: OutputId = OutputId::new(transaction_id, idx).unwrap(); + AliasId::from(output_id) + }) + } + + proptest! { + #[test] + fn property_based_valid_parse(alias_id in arbitrary_alias_id()) { + let did: String = format!("did:{}:{}",StardustDID::METHOD, alias_id); + assert!(StardustDID::parse(&did).is_ok()); + } + } + + proptest! { + #[test] + fn property_based_new(bytes in proptest::prelude::any::<[u8;32]>()) { + for network_name in VALID_NETWORK_NAMES.iter().map(|name| NetworkName::try_from(*name).unwrap()) { + // check that this does not panic + StardustDID::new(&bytes, &network_name); + } + } + } + + proptest! { + #[test] + fn property_based_alias_id_string_representation_roundtrip(alias_id in arbitrary_alias_id()) { + for network_name in VALID_NETWORK_NAMES.iter().map(|name| NetworkName::try_from(*name).unwrap()) { + assert_eq!( + AliasId::from_str(StardustDID::new(&alias_id, &network_name).tag()).unwrap(), + alias_id + ); + } + } + } + + fn arbitrary_alias_id_string_replica() -> impl Strategy { + proptest::string::string_regex(&format!("0x([a-f]|[0-9]){{{}}}", (LEN_VALID_ALIAS_STR - 2))) + .expect("regex should be ok") + } + + proptest! { + #[test] + fn valid_alias_id_string_replicas(tag in arbitrary_alias_id_string_replica()) { + let did : String = format!("did:{}:{}", StardustDID::METHOD, tag); + assert!( + StardustDID::parse(did).is_ok() + ); + } + } + + fn arbitrary_invalid_tag() -> impl Strategy { + proptest::string::string_regex("[[:^alpha:]|[a-z]|[1-9]]*") + .expect("regex should be ok") + .prop_map(|arb_string| { + if arb_string + .chars() + .all(|c| c.is_ascii_hexdigit() && c.is_ascii_lowercase()) + && arb_string.len() == LEN_VALID_ALIAS_STR + && arb_string.starts_with("0x") + { + // this means we are in the rare case of generating a valid string hence we replace the last 0 with the non + // ascii character é + let mut counter = 0; + arb_string + .chars() + .rev() + .map(|value| { + if value == '0' && counter == 0 { + counter += 1; + 'é' + } else { + value + } + }) + .collect::() + } else { + arb_string + } + }) + } + + proptest! { + #[test] + fn invalid_tag_property_based_parse(tag in arbitrary_invalid_tag()) { + let did: String = format!("did:{}:{}", StardustDID::METHOD, tag); + assert!( + StardustDID::parse(did).is_err() + ); + } + } + + fn arbitrary_delimiter_mixed_in_prefix_hex() -> impl Strategy { + proptest::string::string_regex("0x([a-f]|[:]|[0-9])*").expect("regex should be ok") + } + + proptest! { + #[test] + fn invalid_hex_mixed_with_delimiter(tag in arbitrary_delimiter_mixed_in_prefix_hex()) { + let did: String = format!("did:{}:{}", StardustDID::METHOD, tag); + assert!(StardustDID::parse(did).is_err()); + } + } + + // =========================================================================================================================== + // Test getters + // =========================================================================================================================== + #[test] + fn test_network() { + let execute_assertions = |valid_alias_id: &str| { + let did: StardustDID = format!("did:{}:{}", StardustDID::METHOD, valid_alias_id) + .parse() + .unwrap(); + assert_eq!(did.network_str(), StardustDID::DEFAULT_NETWORK); + + let did: StardustDID = format!("did:{}:dev:{}", StardustDID::METHOD, valid_alias_id) + .parse() + .unwrap(); + assert_eq!(did.network_str(), "dev"); + + let did: StardustDID = format!("did:{}:test:{}", StardustDID::METHOD, valid_alias_id) + .parse() + .unwrap(); + assert_eq!(did.network_str(), "test"); + + let did: StardustDID = format!("did:{}:custom:{}", StardustDID::METHOD, valid_alias_id) + .parse() + .unwrap(); + assert_eq!(did.network_str(), "custom"); + }; + + execute_assertions(INITIAL_ALIAS_ID_STR); + execute_assertions(VALID_ALIAS_ID_STR); + } + + #[test] + fn test_tag() { + let execute_assertions = |valid_alias_id: &str| { + let did: StardustDID = format!("did:{}:{}", StardustDID::METHOD, valid_alias_id) + .parse() + .unwrap(); + assert_eq!(did.tag(), valid_alias_id); + + let did: StardustDID = format!( + "did:{}:{}:{}", + StardustDID::METHOD, + StardustDID::DEFAULT_NETWORK, + valid_alias_id + ) + .parse() + .unwrap(); + assert_eq!(did.tag(), valid_alias_id); + + let did: StardustDID = format!("did:{}:dev:{}", StardustDID::METHOD, valid_alias_id) + .parse() + .unwrap(); + assert_eq!(did.tag(), valid_alias_id); + + let did: StardustDID = format!("did:{}:custom:{}", StardustDID::METHOD, valid_alias_id) + .parse() + .unwrap(); + assert_eq!(did.tag(), valid_alias_id); + }; + execute_assertions(INITIAL_ALIAS_ID_STR); + execute_assertions(VALID_ALIAS_ID_STR); + } + + // =========================================================================================================================== + // Test setters + // =========================================================================================================================== + + #[test] + fn replace_network_name() { + for did in VALID_STARDUST_DID_STRINGS.iter() { + let stardust_did: StardustDID = StardustDID::parse(did).unwrap(); + for name in VALID_NETWORK_NAMES { + let old_tag: String = stardust_did.tag().to_string(); + let network_name: NetworkName = NetworkName::try_from(name).unwrap(); + let transfromed: StardustDID = stardust_did.clone().with_network_name(network_name.clone()); + assert_eq!(old_tag, transfromed.tag()); + assert_eq!(transfromed.network_str(), name); + } + } + } + + // =========================================================================================================================== + // Test DIDUrl + // =========================================================================================================================== + + // TODO: Move `StardustDIDUrl` out of this test module once the `document` module gets refactored to use the types + // from this module. + /// A DID URL conforming to the IOTA Stardust UTXO DID method specification. + /// + /// See [`DIDUrl`]. + type StardustDIDUrl = DIDUrl; + #[test] + fn test_parse_did_url_valid() { + let execute_assertions = |valid_alias_id: &str| { + assert!(StardustDIDUrl::parse(format!("did:{}:{}", StardustDID::METHOD, valid_alias_id)).is_ok()); + assert!(StardustDIDUrl::parse(format!("did:{}:{}#fragment", StardustDID::METHOD, valid_alias_id)).is_ok()); + assert!(StardustDIDUrl::parse(format!( + "did:{}:{}?somequery=somevalue", + StardustDID::METHOD, + valid_alias_id + )) + .is_ok()); + assert!(StardustDIDUrl::parse(format!( + "did:{}:{}?somequery=somevalue#fragment", + StardustDID::METHOD, + valid_alias_id + )) + .is_ok()); + + assert!(StardustDIDUrl::parse(format!("did:{}:main:{}", StardustDID::METHOD, valid_alias_id)).is_ok()); + assert!(StardustDIDUrl::parse(format!("did:{}:main:{}#fragment", StardustDID::METHOD, valid_alias_id)).is_ok()); + assert!(StardustDIDUrl::parse(format!( + "did:{}:main:{}?somequery=somevalue", + StardustDID::METHOD, + valid_alias_id + )) + .is_ok()); + assert!(StardustDIDUrl::parse(format!( + "did:{}:main:{}?somequery=somevalue#fragment", + StardustDID::METHOD, + valid_alias_id + )) + .is_ok()); + + assert!(StardustDIDUrl::parse(format!("did:{}:dev:{}", StardustDID::METHOD, valid_alias_id)).is_ok()); + assert!(StardustDIDUrl::parse(format!("did:{}:dev:{}#fragment", StardustDID::METHOD, valid_alias_id)).is_ok()); + assert!(StardustDIDUrl::parse(format!( + "did:{}:dev:{}?somequery=somevalue", + StardustDID::METHOD, + valid_alias_id + )) + .is_ok()); + assert!(StardustDIDUrl::parse(format!( + "did:{}:dev:{}?somequery=somevalue#fragment", + StardustDID::METHOD, + valid_alias_id + )) + .is_ok()); + + assert!(StardustDIDUrl::parse(format!("did:{}:custom:{}", StardustDID::METHOD, valid_alias_id)).is_ok()); + assert!(StardustDIDUrl::parse(format!( + "did:{}:custom:{}#fragment", + StardustDID::METHOD, + valid_alias_id + )) + .is_ok()); + assert!(StardustDIDUrl::parse(format!( + "did:{}:custom:{}?somequery=somevalue", + StardustDID::METHOD, + valid_alias_id + )) + .is_ok()); + assert!(StardustDIDUrl::parse(format!( + "did:{}:custom:{}?somequery=somevalue#fragment", + StardustDID::METHOD, + valid_alias_id + )) + .is_ok()); + }; + execute_assertions(INITIAL_ALIAS_ID_STR); + execute_assertions(VALID_ALIAS_ID_STR); + } + + #[test] + fn valid_url_setters() { + let execute_assertions = |valid_alias_id: &str| { + let mut did_url: StardustDIDUrl = StardustDID::parse(format!("did:{}:{}", StardustDID::METHOD, valid_alias_id)) + .unwrap() + .into_url(); + + did_url.set_path(Some("/foo")).unwrap(); + did_url.set_query(Some("diff=true")).unwrap(); + did_url.set_fragment(Some("foo")).unwrap(); + + assert_eq!(did_url.path(), Some("/foo")); + assert_eq!(did_url.query(), Some("diff=true")); + assert_eq!(did_url.fragment(), Some("foo")); + }; + execute_assertions(INITIAL_ALIAS_ID_STR); + execute_assertions(VALID_ALIAS_ID_STR); + } +} diff --git a/identity_stardust/src/error.rs b/identity_stardust/src/error.rs index 0eaedb3160..67e7dc4666 100644 --- a/identity_stardust/src/error.rs +++ b/identity_stardust/src/error.rs @@ -12,6 +12,8 @@ pub enum Error { CredError(#[from] identity_credential::Error), #[error("{0}")] InvalidDID(#[from] identity_did::did::DIDError), + #[error("invalid network name")] + InvalidNetworkName, #[error("{0}")] InvalidDoc(#[from] identity_did::Error), #[error("{0}")] diff --git a/identity_stardust/src/lib.rs b/identity_stardust/src/lib.rs index d466fd22d8..200fb656d5 100644 --- a/identity_stardust/src/lib.rs +++ b/identity_stardust/src/lib.rs @@ -10,6 +10,12 @@ pub use self::error::Result; pub use document::*; pub use state_metadata::*; +pub use did::StardustDID; +// TODO: Uncomment once `document` has been refactored to use the types from the `did` module in this crate. +// pub use did::StardustDIDUrl; +pub use network::NetworkName; +mod did; mod document; mod error; +mod network; mod state_metadata; diff --git a/identity_stardust/src/network/mod.rs b/identity_stardust/src/network/mod.rs new file mode 100644 index 0000000000..2058f736d7 --- /dev/null +++ b/identity_stardust/src/network/mod.rs @@ -0,0 +1,5 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +mod network_name; +pub use network_name::NetworkName; diff --git a/identity_stardust/src/network/network_name.rs b/identity_stardust/src/network/network_name.rs new file mode 100644 index 0000000000..f360fe739a --- /dev/null +++ b/identity_stardust/src/network/network_name.rs @@ -0,0 +1,123 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::borrow::Cow; + +use core::convert::TryFrom; +use core::fmt::Display; +use core::fmt::Formatter; +use core::ops::Deref; +use std::fmt::Debug; + +use serde::Deserialize; +use serde::Serialize; + +use crate::error::Error; +use crate::error::Result; + +/// Network name compliant with the [`crate::StardustDID`] method specification. +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)] +#[repr(transparent)] +pub struct NetworkName(Cow<'static, str>); + +impl NetworkName { + pub const MAX_LENGTH: usize = 6; + + /// Creates a new [`NetworkName`] if the name passes validation. + pub fn try_from(name: T) -> Result + where + T: Into>, + { + let name_cow: Cow<'static, str> = name.into(); + Self::validate_network_name(&name_cow)?; + Ok(Self(name_cow)) + } + + /// Validates whether a string is a spec-compliant IOTA UTXO DID [`NetworkName`]. + pub fn validate_network_name(name: &str) -> Result<()> { + Some(()) + .filter(|_| { + !name.is_empty() + && (name.len() <= Self::MAX_LENGTH) + && name.chars().all(|ch| ch.is_ascii_lowercase() || ch.is_ascii_digit()) + }) + .ok_or(Error::InvalidNetworkName) + } +} + +impl AsRef for NetworkName { + fn as_ref(&self) -> &str { + self.0.as_ref() + } +} + +impl From for Cow<'static, str> { + fn from(network_name: NetworkName) -> Self { + network_name.0 + } +} + +impl Deref for NetworkName { + type Target = Cow<'static, str>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl TryFrom<&'static str> for NetworkName { + type Error = Error; + + fn try_from(name: &'static str) -> Result { + Self::try_from(Cow::Borrowed(name)) + } +} + +impl TryFrom for NetworkName { + type Error = Error; + + fn try_from(name: String) -> Result { + Self::try_from(Cow::Owned(name)) + } +} + +impl Debug for NetworkName { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + f.write_str(self.as_ref()) + } +} + +impl Display for NetworkName { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + f.write_str(self.as_ref()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + // Rules are: at least one character, at most six characters and may only contain digits and/or lowercase ascii + // characters. + const VALID_NETWORK_NAMES: [&str; 12] = [ + "main", "dev", "smr", "rms", "test", "foo", "foobar", "123456", "0", "foo42", "bar123", "42foo", + ]; + + const INVALID_NETWORK_NAMES: [&str; 10] = [ + "Main", "fOo", "deV", "féta", "", " ", "foo ", " foo", "1234567", "foobar0", + ]; + + #[test] + fn valid_validate_network_name() { + for name in VALID_NETWORK_NAMES { + assert!(NetworkName::validate_network_name(name).is_ok()); + } + } + + #[test] + fn invalid_validate_network_name() { + for name in INVALID_NETWORK_NAMES { + assert!(NetworkName::validate_network_name(name).is_err()); + } + } +} From f05fc56504345ca0e62dbca89d048b3a985b6d85 Mon Sep 17 00:00:00 2001 From: Henrique Nogara Date: Mon, 25 Jul 2022 22:57:48 -0300 Subject: [PATCH 29/89] Change `Storage` to store arbitrary blobs (#953) * Add arbitrary bytes storage * Add arbitrary storage for bindings * Fix naming for storage function * Bump MSRV to 1.62 * Remove serde_json dependency; Rename value to blob; Take ownership of blob; Fix bindings * Fix doc comments --- bindings/stronghold-nodejs/Cargo.lock | 156 ++++++++++++++---- bindings/stronghold-nodejs/js/stronghold.ts | 32 +--- bindings/stronghold-nodejs/src/stronghold.rs | 34 +--- .../examples-account/src/custom_storage.ts | 38 ++--- bindings/wasm/src/account/storage/traits.rs | 69 ++------ .../docs/libraries/rust/getting_started.md | 4 +- identity_account/src/account/account.rs | 32 ++-- identity_account/src/error.rs | 3 + identity_account/src/tests/updates.rs | 17 +- identity_account/src/types/identity_state.rs | 58 +++++++ identity_account/src/types/mod.rs | 2 + .../src/storage/memstore.rs | 44 ++--- .../src/storage/stronghold.rs | 49 +----- .../src/storage/test_suite.rs | 66 ++------ .../src/storage/traits.rs | 16 +- 15 files changed, 298 insertions(+), 322 deletions(-) create mode 100644 identity_account/src/types/identity_state.rs diff --git a/bindings/stronghold-nodejs/Cargo.lock b/bindings/stronghold-nodejs/Cargo.lock index 989f3b1093..cfd7a20ea0 100644 --- a/bindings/stronghold-nodejs/Cargo.lock +++ b/bindings/stronghold-nodejs/Cargo.lock @@ -134,7 +134,7 @@ dependencies = [ "bee-pow", "bee-ternary", "bytemuck", - "digest", + "digest 0.9.0", "hex", "iota-crypto 0.9.1", "serde", @@ -185,7 +185,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a4e37d16930f5459780f5621038b6382b9bb37c19016f39fb6b5808d831f174" dependencies = [ "crypto-mac 0.8.0", - "digest", + "digest 0.9.0", "opaque-debug", ] @@ -198,6 +198,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-buffer" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +dependencies = [ + "generic-array", +] + [[package]] name = "bumpalo" version = "3.10.0" @@ -297,6 +306,16 @@ dependencies = [ "libc", ] +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "crypto-mac" version = "0.8.0" @@ -343,7 +362,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" dependencies = [ "byteorder", - "digest", + "digest 0.9.0", "rand_core 0.5.1", "subtle", "zeroize", @@ -394,6 +413,17 @@ dependencies = [ "generic-array", ] +[[package]] +name = "digest" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +dependencies = [ + "block-buffer 0.10.2", + "crypto-common", + "subtle", +] + [[package]] name = "dirs" version = "4.0.0" @@ -444,10 +474,24 @@ dependencies = [ "curve25519-dalek", "hex", "rand_core 0.5.1", - "sha2", + "sha2 0.9.9", "thiserror", ] +[[package]] +name = "ed25519-zebra" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "403ef3e961ab98f0ba902771d29f842058578bb1ce7e3c59dad5a6a93e784c69" +dependencies = [ + "curve25519-dalek", + "hex", + "rand_core 0.6.3", + "sha2 0.9.9", + "thiserror", + "zeroize", +] + [[package]] name = "fern" version = "0.6.1" @@ -634,12 +678,11 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hkdf" -version = "0.11.0" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01706d578d5c281058480e673ae4086a9f4710d8df1ad80a5b03e39ece5f886b" +checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" dependencies = [ - "digest", - "hmac", + "hmac 0.12.1", ] [[package]] @@ -649,12 +692,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" dependencies = [ "crypto-mac 0.11.1", - "digest", + "digest 0.9.0", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.3", ] [[package]] name = "identity-diff" -version = "0.5.0" +version = "0.6.0" dependencies = [ "serde", "serde_json", @@ -664,7 +716,7 @@ dependencies = [ [[package]] name = "identity-stronghold-nodejs" -version = "0.5.0" +version = "0.6.0" dependencies = [ "identity_account_storage", "identity_core", @@ -678,7 +730,7 @@ dependencies = [ [[package]] name = "identity_account_storage" -version = "0.5.0" +version = "0.6.0" dependencies = [ "async-trait", "futures", @@ -701,7 +753,7 @@ dependencies = [ [[package]] name = "identity_core" -version = "0.5.0" +version = "0.6.0" dependencies = [ "identity-diff", "iota-crypto 0.8.0", @@ -719,7 +771,7 @@ dependencies = [ [[package]] name = "identity_did" -version = "0.5.0" +version = "0.6.0" dependencies = [ "did_url", "form_urlencoded", @@ -732,7 +784,7 @@ dependencies = [ [[package]] name = "identity_iota_core" -version = "0.5.0" +version = "0.6.0" dependencies = [ "bee-message", "identity_core", @@ -778,15 +830,13 @@ dependencies = [ "blake2", "chacha20poly1305", "curve25519-dalek", - "digest", - "ed25519-zebra", + "digest 0.9.0", + "ed25519-zebra 2.2.0", "generic-array", "getrandom 0.2.6", - "hmac", - "pbkdf2", - "serde", - "sha2", - "unicode-normalization", + "hmac 0.11.0", + "pbkdf2 0.8.0", + "sha2 0.9.9", "x25519-dalek", ] @@ -798,20 +848,44 @@ checksum = "8c98a3248cde6b42cb479a52089fe4fc20d12de51b8edd923294cd5240ec89b0" dependencies = [ "bee-ternary", "blake2", - "digest", - "ed25519-zebra", + "digest 0.9.0", + "ed25519-zebra 2.2.0", "lazy_static", ] +[[package]] +name = "iota-crypto" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f03717e934972fad6f1c9b4cd25f662e1753b58a7f76e3dceadeb646e034b252" +dependencies = [ + "aead", + "aes", + "aes-gcm", + "chacha20poly1305", + "curve25519-dalek", + "digest 0.10.3", + "ed25519-zebra 3.0.0", + "generic-array", + "getrandom 0.2.6", + "hmac 0.12.1", + "pbkdf2 0.11.0", + "serde", + "sha2 0.10.2", + "unicode-normalization", + "x25519-dalek", + "zeroize", +] + [[package]] name = "iota_stronghold" -version = "0.5.1" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "273197e986052cac7fed586142e40497ffec2e3536a7416d23bf694f90068b96" +checksum = "89ebdd443fde5c02437469c1a318aa4d56cba83db3913743862a0e3b54ab5736" dependencies = [ "bincode", "hkdf", - "iota-crypto 0.8.0", + "iota-crypto 0.12.1", "serde", "stronghold-derive", "stronghold-rlu", @@ -1051,6 +1125,15 @@ dependencies = [ "crypto-mac 0.11.1", ] +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest 0.10.3", +] + [[package]] name = "percent-encoding" version = "2.1.0" @@ -1285,13 +1368,24 @@ version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ - "block-buffer", + "block-buffer 0.9.0", "cfg-if", "cpufeatures 0.2.2", - "digest", + "digest 0.9.0", "opaque-debug", ] +[[package]] +name = "sha2" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.2", + "digest 0.10.3", +] + [[package]] name = "slab" version = "0.4.6" @@ -1351,9 +1445,9 @@ dependencies = [ [[package]] name = "stronghold-utils" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d8493a083506300a7a112d927f48068e4c733f0b46c63569dea6ab6dfe175c" +checksum = "be1ce0d6205e8ea89771f2b32d64aeaecbc930320919e95ec4efcc0aa96cd951" dependencies = [ "rand", "stronghold-derive", diff --git a/bindings/stronghold-nodejs/js/stronghold.ts b/bindings/stronghold-nodejs/js/stronghold.ts index 329211140f..2a463d3dd5 100644 --- a/bindings/stronghold-nodejs/js/stronghold.ts +++ b/bindings/stronghold-nodejs/js/stronghold.ts @@ -1,4 +1,4 @@ -import { NapiStronghold, NapiDid, NapiKeyLocation, NapiChainState, NapiDocument, NapiKeyType, NapiDidLocation, NapiEncryptedData, NapiEncryptionAlgorithm, NapiCekAlgorithm } from '../napi-dist/napi'; +import { NapiStronghold, NapiDid, NapiKeyLocation, NapiKeyType, NapiDidLocation, NapiEncryptedData, NapiEncryptionAlgorithm, NapiCekAlgorithm } from '../napi-dist/napi'; import { DID, KeyLocation, Signature, ChainState, Storage, KeyType, Document, EncryptedData, EncryptionAlgorithm, CekAlgorithm } from "@iota/identity-wasm/node"; export class Stronghold implements Storage { @@ -115,38 +115,20 @@ export class Stronghold implements Storage { return Uint8Array.from(decryptedData); } - public async chainStateGet(did: DID): Promise { + public async blobGet(did: DID): Promise { const napiDID: NapiDid = NapiDid.fromJSON(did.toJSON()); - const napiChainState: NapiChainState | undefined = await this.napiStronghold.chainStateGet(napiDID); + const value: number[] | undefined = await this.napiStronghold.blobGet(napiDID); - if (napiChainState) { - return ChainState.fromJSON(napiChainState.toJSON()) + if (value) { + return Uint8Array.from(value) } else { return undefined; } } - public async chainStateSet(did: DID, chainState: ChainState): Promise { + public async blobSet(did: DID, value: Uint8Array): Promise { const napiDID: NapiDid = NapiDid.fromJSON(did.toJSON()); - const napiChainState: NapiChainState = NapiChainState.fromJSON(chainState.toJSON()); - return this.napiStronghold.chainStateSet(napiDID, napiChainState); - } - - public async documentGet(did: DID): Promise { - const napiDID: NapiDid = NapiDid.fromJSON(did.toJSON()); - const napiDocument: NapiDocument | undefined = await this.napiStronghold.documentGet(napiDID); - - if (napiDocument) { - return Document.fromJSON(napiDocument.toJSON()) - } else { - return undefined; - } - } - - public async documentSet(did: DID, document: Document): Promise { - const napiDID: NapiDid = NapiDid.fromJSON(did.toJSON()); - const napiDocument: NapiDocument = NapiDocument.fromJSON(document.toJSON()); - return this.napiStronghold.documentSet(napiDID, napiDocument); + return this.napiStronghold.blobSet(napiDID, Array.from(value)); } public async flushChanges() { diff --git a/bindings/stronghold-nodejs/src/stronghold.rs b/bindings/stronghold-nodejs/src/stronghold.rs index 30e32ab789..5a35dfcd5a 100644 --- a/bindings/stronghold-nodejs/src/stronghold.rs +++ b/bindings/stronghold-nodejs/src/stronghold.rs @@ -15,10 +15,8 @@ use napi_derive::napi; use crate::error::NapiResult; use crate::types::NapiCekAlgorithm; -use crate::types::NapiChainState; use crate::types::NapiDid; use crate::types::NapiDidLocation; -use crate::types::NapiDocument; use crate::types::NapiEncryptedData; use crate::types::NapiEncryptionAlgorithm; use crate::types::NapiKeyLocation; @@ -236,38 +234,22 @@ impl NapiStronghold { Ok(data.into_iter().map(u32::from).collect()) } - /// Returns the chain state of the identity specified by `did`. + /// Returns the blob stored by the identity specified by `did`. #[napi] - pub async fn chain_state_get(&self, did: &NapiDid) -> Result> { + pub async fn blob_get(&self, did: &NapiDid) -> Result>> { self .0 - .chain_state_get(&did.0) + .blob_get(&did.0) .await .napi_result() - .map(|opt_chain_state| opt_chain_state.map(|chain_state| chain_state.into())) + .map(|opt_value| opt_value.map(|value| value.into_iter().map(u32::from).collect())) } - /// Set the chain state of the identity specified by `did`. + /// Stores an arbitrary blob for the identity specified by `did`. #[napi] - pub async fn chain_state_set(&self, did: &NapiDid, chain_state: &NapiChainState) -> Result<()> { - self.0.chain_state_set(&did.0, &chain_state.0).await.napi_result() - } - - /// Returns the document of the identity specified by `did`. - #[napi] - pub async fn document_get(&self, did: &NapiDid) -> Result> { - self - .0 - .document_get(&did.0) - .await - .napi_result() - .map(|opt_document| opt_document.map(|doc| doc.into())) - } - - /// Sets a new state for the identity specified by `did`. - #[napi] - pub async fn document_set(&self, did: &NapiDid, state: &NapiDocument) -> Result<()> { - self.0.document_set(&did.0, &state.0).await.napi_result() + pub async fn blob_set(&self, did: &NapiDid, blob: Vec) -> Result<()> { + let blob: Vec = blob.try_into_bytes()?; + self.0.blob_set(&did.0, blob).await.napi_result() } /// Persists any unsaved changes. diff --git a/bindings/wasm/examples-account/src/custom_storage.ts b/bindings/wasm/examples-account/src/custom_storage.ts index cf6b58864d..b0b3b2856d 100644 --- a/bindings/wasm/examples-account/src/custom_storage.ts +++ b/bindings/wasm/examples-account/src/custom_storage.ts @@ -1,7 +1,7 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { ChainState, DID, Document, Ed25519, KeyLocation, KeyPair, KeyType, Signature, Storage, StorageTestSuite, EncryptionAlgorithm, CekAlgorithm, EncryptedData } from '../../node/identity_wasm.js'; +import { DID, Ed25519, KeyLocation, KeyPair, KeyType, Signature, Storage, StorageTestSuite, EncryptionAlgorithm, CekAlgorithm, EncryptedData } from '../../node/identity_wasm.js'; /** An insecure, in-memory `Storage` implementation that serves as an example. This can be passed to the `AccountBuilder` to create accounts with this as the storage. */ @@ -10,17 +10,14 @@ export class MemStore implements Storage { // We use strings as keys rather than DIDs or KeyLocations because Maps use // referential equality for object keys, and thus a primitive type needs to be used instead. - // The map from DIDs to chain states. - private _chainStates: Map; - // The map from DIDs to DID documents. - private _documents: Map; - // The map from DIDs to vaults. + // The map from DIDs to state. + private _blobs: Map; + // Map of DID state blobs. private _vaults: Map>; /** Creates a new, empty `MemStore` instance. */ constructor() { - this._chainStates = new Map(); - this._documents = new Map(); + this._blobs = new Map(); this._vaults = new Map(); } @@ -67,8 +64,7 @@ export class MemStore implements Storage { // so we only need to do work if the DID still exists. // The return value signals whether the DID was actually removed during this operation. if (this._vaults.has(did.toString())) { - this._chainStates.delete(did.toString()); - this._documents.delete(did.toString()); + this._blobs.delete(did.toString()); this._vaults.delete(did.toString()); return true; } @@ -197,24 +193,14 @@ export class MemStore implements Storage { throw new Error('not yet implemented') } - public async chainStateGet(did: DID): Promise { - // Lookup the chain state of the given DID. - return this._chainStates.get(did.toString()); + public async blobGet(did: DID): Promise { + // Lookup the state of the given DID. + return this._blobs.get(did.toString()); } - public async chainStateSet(did: DID, chainState: ChainState): Promise { - // Set the chain state of the given DID. - this._chainStates.set(did.toString(), chainState); - } - - public async documentGet(did: DID): Promise { - // Lookup the DID document of the given DID. - return this._documents.get(did.toString()) - } - - public async documentSet(did: DID, document: Document): Promise { - // Set the DID document of the given DID. - this._documents.set(did.toString(), document); + public async blobSet(did: DID, value: Uint8Array): Promise { + // Set the state of the given DID. + this._blobs.set(did.toString(), value); } public async flushChanges(): Promise { diff --git a/bindings/wasm/src/account/storage/traits.rs b/bindings/wasm/src/account/storage/traits.rs index aec23609f8..c9f067d753 100644 --- a/bindings/wasm/src/account/storage/traits.rs +++ b/bindings/wasm/src/account/storage/traits.rs @@ -5,7 +5,6 @@ use core::fmt::Debug; use core::fmt::Formatter; use identity_iota::account_storage::CekAlgorithm; -use identity_iota::account_storage::ChainState; use identity_iota::account_storage::EncryptedData; use identity_iota::account_storage::EncryptionAlgorithm; use identity_iota::account_storage::Error as AccountStorageError; @@ -17,7 +16,6 @@ use identity_iota::crypto::PrivateKey; use identity_iota::crypto::PublicKey; use identity_iota::iota_core::IotaDID; use identity_iota::iota_core::NetworkName; -use identity_iota::prelude::IotaDocument; use identity_iota::prelude::KeyType; use js_sys::Array; use js_sys::Promise; @@ -26,7 +24,6 @@ use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; use wasm_bindgen_futures::JsFuture; -use crate::account::identity::WasmChainState; use crate::account::types::WasmCekAlgorithm; use crate::account::types::WasmEncryptedData; use crate::account::types::WasmEncryptionAlgorithm; @@ -35,7 +32,6 @@ use crate::common::PromiseBool; use crate::common::PromiseVoid; use crate::crypto::WasmKeyType; use crate::did::WasmDID; -use crate::did::WasmDocument; use crate::error::JsValueResult; #[wasm_bindgen] @@ -44,10 +40,6 @@ extern "C" { pub type PromisePublicKey; #[wasm_bindgen(typescript_type = "Promise")] pub type PromiseSignature; - #[wasm_bindgen(typescript_type = "Promise")] - pub type PromiseOptionChainState; - #[wasm_bindgen(typescript_type = "Promise")] - pub type PromiseOptionDocument; #[wasm_bindgen(typescript_type = "Promise")] pub type PromiseKeyLocation; #[wasm_bindgen(typescript_type = "Promise>")] @@ -58,6 +50,8 @@ extern "C" { pub type PromiseEncryptedData; #[wasm_bindgen(typescript_type = "Promise")] pub type PromiseData; + #[wasm_bindgen(typescript_type = "Promise")] + pub type PromiseOptionBytes; } #[wasm_bindgen] @@ -111,14 +105,10 @@ extern "C" { cek_algorithm: WasmCekAlgorithm, private_key: WasmKeyLocation, ) -> Uint8Array; - #[wasm_bindgen(method, js_name = chainStateGet)] - pub fn chain_state_get(this: &WasmStorage, did: WasmDID) -> PromiseOptionChainState; - #[wasm_bindgen(method, js_name = chainStateSet)] - pub fn chain_state_set(this: &WasmStorage, did: WasmDID, chain_state: WasmChainState) -> PromiseVoid; - #[wasm_bindgen(method, js_name = documentGet)] - pub fn document_get(this: &WasmStorage, did: WasmDID) -> PromiseOptionDocument; - #[wasm_bindgen(method, js_name = documentSet)] - pub fn document_set(this: &WasmStorage, did: WasmDID, document: WasmDocument) -> PromiseVoid; + #[wasm_bindgen(method, js_name = blobGet)] + pub fn blob_get(this: &WasmStorage, did: WasmDID) -> PromiseOptionBytes; + #[wasm_bindgen(method, js_name = blobSet)] + pub fn blob_set(this: &WasmStorage, did: WasmDID, blob: Vec) -> PromiseVoid; #[wasm_bindgen(method, js_name = flushChanges)] pub fn flush_changes(this: &WasmStorage) -> PromiseVoid; } @@ -286,40 +276,19 @@ impl Storage for WasmStorage { Ok(data) } - async fn chain_state_get(&self, did: &IotaDID) -> AccountStorageResult> { - let promise: Promise = Promise::resolve(&self.chain_state_get(did.clone().into())); + async fn blob_get(&self, did: &IotaDID) -> AccountStorageResult>> { + let promise: Promise = Promise::resolve(&self.blob_get(did.clone().into())); let result: JsValueResult = JsFuture::from(promise).await.into(); let js_value: JsValue = result.to_account_error()?; if js_value.is_null() || js_value.is_undefined() { return Ok(None); } - let chain_state: ChainState = js_value - .into_serde() - .map_err(|err| AccountStorageError::SerializationError(err.to_string()))?; - Ok(Some(chain_state)) + let value: Vec = uint8array_to_bytes(js_value)?; + Ok(Some(value)) } - async fn chain_state_set(&self, did: &IotaDID, chain_state: &ChainState) -> AccountStorageResult<()> { - let promise: Promise = Promise::resolve(&self.chain_state_set(did.clone().into(), chain_state.clone().into())); - let result: JsValueResult = JsFuture::from(promise).await.into(); - result.into() - } - - async fn document_get(&self, did: &IotaDID) -> AccountStorageResult> { - let promise: Promise = Promise::resolve(&self.document_get(did.clone().into())); - let result: JsValueResult = JsFuture::from(promise).await.into(); - let js_value: JsValue = result.to_account_error()?; - if js_value.is_null() || js_value.is_undefined() { - return Ok(None); - } - let document: IotaDocument = js_value - .into_serde() - .map_err(|err| AccountStorageError::SerializationError(err.to_string()))?; - Ok(Some(document)) - } - - async fn document_set(&self, did: &IotaDID, document: &IotaDocument) -> AccountStorageResult<()> { - let promise: Promise = Promise::resolve(&self.document_set(did.clone().into(), document.clone().into())); + async fn blob_set(&self, did: &IotaDID, blob: Vec) -> AccountStorageResult<()> { + let promise: Promise = Promise::resolve(&self.blob_set(did.clone().into(), blob)); let result: JsValueResult = JsFuture::from(promise).await.into(); result.into() } @@ -416,17 +385,11 @@ interface Storage { */ dataDecrypt: (did: DID, data: EncryptedData, encryptionAlgorithm: EncryptionAlgorithm, cekAlgorithm: CekAlgorithm, privateKey: KeyLocation) => Promise; - /** Returns the chain state of the identity specified by `did`. */ - chainStateGet: (did: DID) => Promise; - - /** Set the chain state of the identity specified by `did`. */ - chainStateSet: (did: DID, chainState: ChainState) => Promise; - - /** Returns the document of the identity specified by `did`. */ - documentGet: (did: DID) => Promise; + /** Returns the blob stored by the identity specified by `did`. */ + blobGet: (did: DID) => Promise; - /** Sets a new state for the identity specified by `did`. */ - documentSet: (did: DID, document: Document) => Promise; + /** Stores an arbitrary blob for the identity specified by `did`. */ + blobSet: (did: DID, blob: Uint8Array) => Promise; /** Persists any unsaved changes. */ flushChanges: () => Promise; diff --git a/documentation/docs/libraries/rust/getting_started.md b/documentation/docs/libraries/rust/getting_started.md index ecd62a51d5..67f46ca233 100644 --- a/documentation/docs/libraries/rust/getting_started.md +++ b/documentation/docs/libraries/rust/getting_started.md @@ -10,8 +10,8 @@ keywords: ## Requirements -- [Rust](https://www.rust-lang.org/) (>= 1.60) -- [Cargo](https://doc.rust-lang.org/cargo/) (>= 1.60) +- [Rust](https://www.rust-lang.org/) (>= 1.62) +- [Cargo](https://doc.rust-lang.org/cargo/) (>= 1.62) ## Include the Library diff --git a/identity_account/src/account/account.rs b/identity_account/src/account/account.rs index 5365a783bc..03f5964df6 100644 --- a/identity_account/src/account/account.rs +++ b/identity_account/src/account/account.rs @@ -12,6 +12,8 @@ use identity_account_storage::crypto::RemoteKey; use identity_account_storage::identity::ChainState; use identity_account_storage::storage::Storage; use identity_account_storage::types::KeyLocation; +use identity_core::convert::FromJson; +use identity_core::convert::ToJson; use identity_core::crypto::KeyType; use identity_core::crypto::ProofOptions; use identity_core::crypto::SetSignature; @@ -32,6 +34,7 @@ use serde::Serialize; use crate::account::AccountBuilder; use crate::account::PublishOptions; use crate::types::IdentitySetup; +use crate::types::IdentityState; use crate::types::IdentityUpdater; use crate::updates::create_identity; use crate::updates::Update; @@ -126,12 +129,14 @@ where } // Ensure the identity exists in storage - let document: IotaDocument = setup.storage.document_get(&did).await?.ok_or(Error::IdentityNotFound)?; - let chain_state: ChainState = setup - .storage - .chain_state_get(&did) - .await? - .ok_or(Error::IdentityNotFound)?; + let identity_state_bytes: Vec = setup.storage.blob_get(&did).await?.ok_or(Error::IdentityNotFound)?; + let identity_state: IdentityState = IdentityState::from_json_slice(&identity_state_bytes)?; + let chain_state: ChainState = identity_state + .chain_state()? + .ok_or_else(|| Error::InvalidIdentityState("missing chain state".to_owned()))?; + let document: IotaDocument = identity_state + .document()? + .ok_or_else(|| Error::InvalidIdentityState("missing document".to_owned()))?; Self::with_setup(setup, chain_state, document).await } @@ -316,12 +321,17 @@ where // TODO: An account always holds a valid identity, // so if None is returned, that's a broken invariant. // This should be mapped to a fatal error in the future. - self + let identity_state_bytes: Vec = self .storage() .deref() - .document_get(self.did()) + .blob_get(self.did()) .await? - .ok_or(Error::IdentityNotFound) + .ok_or(Error::IdentityNotFound)?; + let identity_state: IdentityState = IdentityState::from_json_slice(&identity_state_bytes)?; + + identity_state + .document()? + .ok_or_else(|| Error::InvalidIdentityState("document not found".to_owned())) } pub(crate) async fn process_update(&mut self, update: Update) -> Result<()> { @@ -410,8 +420,8 @@ where } async fn store_state(&self) -> Result<()> { - self.storage.document_set(self.did(), &self.document).await?; - self.storage.chain_state_set(self.did(), self.chain_state()).await?; + let identity_state: IdentityState = IdentityState::new(Some(&self.document), Some(&self.chain_state))?; + self.storage.blob_set(self.did(), identity_state.to_json_vec()?).await?; self.save(false).await?; diff --git a/identity_account/src/error.rs b/identity_account/src/error.rs index 369f95411d..ad08000fc8 100644 --- a/identity_account/src/error.rs +++ b/identity_account/src/error.rs @@ -36,6 +36,9 @@ pub enum Error { /// Caused by verification methods without fragments. #[error("method missing fragment")] MethodMissingFragment, + /// Caused by reaching an invalid state for the identity. + #[error("invalid identity state: {0}")] + InvalidIdentityState(String), } impl From for Error { diff --git a/identity_account/src/tests/updates.rs b/identity_account/src/tests/updates.rs index 3ac3b0a893..0bf57b726d 100644 --- a/identity_account/src/tests/updates.rs +++ b/identity_account/src/tests/updates.rs @@ -9,6 +9,7 @@ use identity_core::common::OneOrSet; use identity_core::common::OrderedSet; use identity_core::common::Timestamp; use identity_core::common::Url; +use identity_core::convert::FromJson; use identity_core::crypto::KeyPair; use identity_core::crypto::KeyType; use identity_core::crypto::PrivateKey; @@ -31,6 +32,7 @@ use crate::account::AccountSetup; use crate::error::Error; use crate::error::Result; use crate::types::IdentitySetup; +use crate::types::IdentityState; use crate::types::MethodContent; use crate::updates::Update; use crate::updates::UpdateError; @@ -131,12 +133,8 @@ async fn test_create_identity_already_exists() -> Result<()> { .await .unwrap(); - let initial_state = account_setup - .storage - .document_get(account.did()) - .await - .unwrap() - .unwrap(); + let initial_state: Vec = account_setup.storage.blob_get(account.did()).await?.unwrap(); + let initial_state: IdentityState = IdentityState::from_json_slice(&initial_state).unwrap(); let output = Account::create_identity(account_setup.clone(), identity_create).await; @@ -146,10 +144,9 @@ async fn test_create_identity_already_exists() -> Result<()> { )); // Ensure nothing was overwritten in storage - assert_eq!( - initial_state, - account_setup.storage.document_get(account.did()).await?.unwrap() - ); + let account_state: Vec = account_setup.storage.blob_get(account.did()).await?.unwrap(); + let account_state: IdentityState = IdentityState::from_json_slice(&account_state).unwrap(); + assert_eq!(initial_state.document()?, account_state.document()?); } Ok(()) } diff --git a/identity_account/src/types/identity_state.rs b/identity_account/src/types/identity_state.rs new file mode 100644 index 0000000000..26299cdc95 --- /dev/null +++ b/identity_account/src/types/identity_state.rs @@ -0,0 +1,58 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use identity_account_storage::identity::ChainState; +use identity_core::convert::FromJson; +use identity_core::convert::ToJson; +use identity_iota_core::document::IotaDocument; +use serde::Deserialize; +use serde::Serialize; + +use crate::error::Result; + +/// Holds the internal state for the identity. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub(crate) struct IdentityState { + version: StateVersion, + document: Option>, + chain_state: Option>, +} + +impl IdentityState { + /// Creates a new [`IdentityState`]. + pub(crate) fn new(document: Option<&IotaDocument>, chain_state: Option<&ChainState>) -> Result { + let document: Option> = document.map(|iota_doc| iota_doc.to_json_vec()).transpose()?; + let chain_state: Option> = chain_state.map(|chain_state| chain_state.to_json_vec()).transpose()?; + Ok(IdentityState { + version: StateVersion::default(), + document, + chain_state, + }) + } + + /// Returns the deserialized [`IotaDocument`]. + pub(crate) fn document(&self) -> Result> { + match self.version { + StateVersion::V1 => Ok(self.document.as_ref().map(IotaDocument::from_json_slice).transpose()?), + } + } + + /// Returns the deserialized [`ChainState`]. + pub(crate) fn chain_state(&self) -> Result> { + match self.version { + StateVersion::V1 => Ok(self.chain_state.as_ref().map(ChainState::from_json_slice).transpose()?), + } + } + + #[allow(dead_code)] + /// Returns the [`StateVersion`] of the [`IdentityState`]. + pub(crate) fn version(&self) -> StateVersion { + self.version + } +} + +#[derive(Copy, Clone, Debug, Default, PartialEq, Deserialize, Serialize)] +pub(crate) enum StateVersion { + #[default] + V1 = 1, +} diff --git a/identity_account/src/types/mod.rs b/identity_account/src/types/mod.rs index 79211b0f20..b801500afa 100644 --- a/identity_account/src/types/mod.rs +++ b/identity_account/src/types/mod.rs @@ -2,9 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 pub use self::identity_setup::*; +pub(crate) use self::identity_state::IdentityState; pub use self::identity_updater::*; pub use self::method_content::*; mod identity_setup; +mod identity_state; mod identity_updater; mod method_content; diff --git a/identity_account_storage/src/storage/memstore.rs b/identity_account_storage/src/storage/memstore.rs index ef27a591d2..23b19f0b5d 100644 --- a/identity_account_storage/src/storage/memstore.rs +++ b/identity_account_storage/src/storage/memstore.rs @@ -21,7 +21,6 @@ use identity_core::crypto::Sign; #[cfg(feature = "encryption")] use identity_core::crypto::X25519; use identity_iota_core::did::IotaDID; -use identity_iota_core::document::IotaDocument; use identity_iota_core::tangle::NetworkName; use std::sync::RwLockReadGuard; use std::sync::RwLockWriteGuard; @@ -29,7 +28,6 @@ use zeroize::Zeroize; use crate::error::Error; use crate::error::Result; -use crate::identity::ChainState; use crate::storage::Storage; #[cfg(feature = "encryption")] use crate::types::CekAlgorithm; @@ -41,10 +39,6 @@ use crate::types::KeyLocation; use crate::types::Signature; use crate::utils::Shared; -// The map from DIDs to chain states. -type ChainStates = HashMap; -// The map from DIDs to DID documents. -type Documents = HashMap; // The map from DIDs to vaults. type Vaults = HashMap; // The map from key locations to key pairs, that lives within a DID partition. @@ -54,9 +48,7 @@ type MemVault = HashMap; pub struct MemStore { // Controls whether to print the storages content when debugging. expand: bool, - // The `Shared` type is simply a light wrapper around `Rwlock`. - chain_states: Shared, - documents: Shared, + blobs: Shared>>, vaults: Shared, } @@ -65,8 +57,7 @@ impl MemStore { pub fn new() -> Self { Self { expand: false, - chain_states: Shared::new(HashMap::new()), - documents: Shared::new(HashMap::new()), + blobs: Shared::new(HashMap::new()), vaults: Shared::new(HashMap::new()), } } @@ -132,9 +123,7 @@ impl Storage for MemStore { // so we only need to do work if the DID still exists. // The return value signals whether the DID was actually removed during this operation. if self.vaults.write()?.remove(did).is_some() { - let _ = self.documents.write()?.remove(did); - let _ = self.chain_states.write()?.remove(did); - + let _ = self.blobs.write()?.remove(did); Ok(true) } else { Ok(false) @@ -381,28 +370,16 @@ impl Storage for MemStore { } } - async fn chain_state_get(&self, did: &IotaDID) -> Result> { - // Lookup the chain state of the given DID. - self.chain_states.read().map(|states| states.get(did).cloned()) - } - - async fn chain_state_set(&self, did: &IotaDID, chain_state: &ChainState) -> Result<()> { - // Set the chain state of the given DID. - self.chain_states.write()?.insert(did.clone(), chain_state.clone()); + async fn blob_set(&self, did: &IotaDID, value: Vec) -> Result<()> { + // Set the arbitrary value for the given DID. + self.blobs.write()?.insert(did.clone(), value); Ok(()) } - async fn document_get(&self, did: &IotaDID) -> Result> { - // Lookup the DID document of the given DID. - self.documents.read().map(|documents| documents.get(did).cloned()) - } - - async fn document_set(&self, did: &IotaDID, document: &IotaDocument) -> Result<()> { - // Set the DID document of the given DID. - self.documents.write()?.insert(did.clone(), document.clone()); - - Ok(()) + async fn blob_get(&self, did: &IotaDID) -> Result>> { + // Lookup the value stored of the given DID. + self.blobs.read().map(|data| data.get(did).cloned()) } async fn flush_changes(&self) -> Result<()> { @@ -532,8 +509,7 @@ impl Debug for MemStore { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { if self.expand { f.debug_struct("MemStore") - .field("chain_states", &self.chain_states) - .field("states", &self.documents) + .field("blobs", &self.blobs) .field("vaults", &self.vaults) .finish() } else { diff --git a/identity_account_storage/src/storage/stronghold.rs b/identity_account_storage/src/storage/stronghold.rs index 3a9762dedd..25fac69da2 100644 --- a/identity_account_storage/src/storage/stronghold.rs +++ b/identity_account_storage/src/storage/stronghold.rs @@ -14,7 +14,6 @@ use identity_core::crypto::PrivateKey; use identity_core::crypto::PublicKey; use identity_core::crypto::X25519; use identity_iota_core::did::IotaDID; -use identity_iota_core::document::IotaDocument; use identity_iota_core::tangle::NetworkName; use iota_stronghold::procedures; use iota_stronghold::procedures::ProcedureError; @@ -32,7 +31,6 @@ use zeroize::Zeroize; use crate::error::Error; use crate::error::Result; -use crate::identity::ChainState; use crate::storage::Storage; use crate::stronghold::ClientOperation; use crate::stronghold::ClientPath; @@ -52,8 +50,7 @@ static INDEX_CLIENT_PATH: &str = "$index"; // The key in the index store that contains the serialized index. // This happens to be the same as the client path, but for explicitness we define them separately. static INDEX_STORE_KEY: &str = INDEX_CLIENT_PATH; -static CHAIN_STATE_STORE_KEY: &str = "$chain_state"; -static DOCUMENT_STORE_KEY: &str = "$document"; +static BLOB_STORE_KEY: &str = "$blob"; // The static identifier for vaults inside clients. static VAULT_PATH: &[u8; 6] = b"$vault"; @@ -377,58 +374,24 @@ impl Storage for Stronghold { } } - async fn chain_state_get(&self, did: &IotaDID) -> Result> { - let client: Client = self.client(&ClientPath::from(did))?; - let store: Store = client.store(); - - let data: Option> = store - .get(CHAIN_STATE_STORE_KEY.as_bytes()) - .map_err(|err| StrongholdError::Store(StoreOperation::Get, err))?; - - match data { - None => return Ok(None), - Some(data) => Ok(Some(ChainState::from_json_slice(&data)?)), - } - } - - async fn chain_state_set(&self, did: &IotaDID, chain_state: &ChainState) -> Result<()> { - let json: Vec = chain_state.to_json_vec()?; - + async fn blob_set(&self, did: &IotaDID, blob: Vec) -> Result<()> { self.mutate_client(did, |client| { let store: Store = client.store(); store - .insert(CHAIN_STATE_STORE_KEY.as_bytes().to_vec(), json, None) + .insert(BLOB_STORE_KEY.as_bytes().to_vec(), blob, None) .map(|_| ()) .map_err(|err| StrongholdError::Store(StoreOperation::Insert, err).into()) }) } - async fn document_get(&self, did: &IotaDID) -> Result> { + async fn blob_get(&self, did: &IotaDID) -> Result>> { let client: Client = self.client(&ClientPath::from(did))?; let store: Store = client.store(); - let data: Option> = store - .get(DOCUMENT_STORE_KEY.as_bytes()) + .get(BLOB_STORE_KEY.as_bytes()) .map_err(|err| StrongholdError::Store(StoreOperation::Get, err))?; - - match data { - None => return Ok(None), - Some(data) => Ok(Some(IotaDocument::from_json_slice(&data)?)), - } - } - - async fn document_set(&self, did: &IotaDID, document: &IotaDocument) -> Result<()> { - let json: Vec = document.to_json_vec()?; - - self.mutate_client(did, |client| { - let store: Store = client.store(); - - store - .insert(DOCUMENT_STORE_KEY.as_bytes().to_vec(), json, None) - .map(|_| ()) - .map_err(|err| StrongholdError::Store(StoreOperation::Insert, err).into()) - }) + Ok(data) } async fn flush_changes(&self) -> Result<()> { diff --git a/identity_account_storage/src/storage/test_suite.rs b/identity_account_storage/src/storage/test_suite.rs index 63046e310a..9809459800 100644 --- a/identity_account_storage/src/storage/test_suite.rs +++ b/identity_account_storage/src/storage/test_suite.rs @@ -6,6 +6,8 @@ use function_name::named; use rand::distributions::DistString; use rand::rngs::OsRng; +use identity_core::convert::FromJson; +use identity_core::convert::ToJson; use identity_core::crypto::KeyPair; use identity_core::crypto::KeyType; use identity_core::crypto::PrivateKey; @@ -382,25 +384,9 @@ impl StorageTestSuite { .await .context("did_create returned an error")?; - let chain_state: Option = storage - .chain_state_get(&did) - .await - .context("chain_state_get returned an error")?; - - ensure!( - chain_state.is_none(), - "expected chain_state_get to return `None` for a new DID" - ); - - let document: Option = storage - .document_get(&did) - .await - .context("document_get returned an error")?; + let value: Option> = storage.blob_get(&did).await.context("blob_get returned an error")?; - ensure!( - document.is_none(), - "expected document_get to return `None` for a new DID" - ); + ensure!(value.is_none(), "expected blob_get to return `None` for a new DID"); let public_key: PublicKey = storage .key_public(&did, &location) @@ -411,42 +397,30 @@ impl StorageTestSuite { IotaVerificationMethod::new(did.clone(), KeyType::Ed25519, &public_key, &fragment).unwrap(); let expected_document: IotaDocument = IotaDocument::from_verification_method(method).unwrap(); - storage - .document_set(&did, &expected_document) - .await - .context("document_set returned an error")?; - - let document: IotaDocument = storage - .document_get(&did) + .blob_set(&did, expected_document.to_json_vec().unwrap()) .await - .context("document_get returned an error")? - .ok_or_else(|| anyhow::Error::msg("expected `Some(_)` to be returned, got `None`"))?; - + .context("blob_set returned an error")?; + let value: Option> = storage.blob_get(&did).await.context("blob_get returned an error")?; + let document: IotaDocument = IotaDocument::from_json_slice(&value.unwrap()).unwrap(); ensure_eq!( expected_document, document, - "expected document to be `{expected_document}`, got `{document}`" + "expected `{expected_document}`, got `{document}`" ); let mut expected_chain_state: ChainState = ChainState::new(); expected_chain_state.set_last_integration_message_id(MessageId::new([0xff; 32])); - storage - .chain_state_set(&did, &expected_chain_state) - .await - .context("chain_state_set returned an error")?; - - let chain_state: ChainState = storage - .chain_state_get(&did) + .blob_set(&did, expected_chain_state.to_json_vec().unwrap()) .await - .context("chain_state_get returned an error")? - .ok_or_else(|| anyhow::Error::msg("expected `Some(_)` to be returned, got `None`"))?; - + .context("blob_set returned an error")?; + let value: Option> = storage.blob_get(&did).await.context("blob_get returned an error")?; + let chain_state: ChainState = ChainState::from_json_slice(&value.unwrap()).unwrap(); ensure_eq!( expected_chain_state, chain_state, - "expected chain state to be `{expected_chain_state:?}`, got `{chain_state:?}`" + "expected `{expected_chain_state:?}`, got `{chain_state:?}`" ); Ok(()) @@ -474,7 +448,7 @@ impl StorageTestSuite { expected_chain_state.set_last_integration_message_id(MessageId::new([0xff; 32])); storage - .chain_state_set(&did, &expected_chain_state) + .blob_set(&did, expected_chain_state.to_json_vec().unwrap()) .await .context("chain_state_set returned an error")?; @@ -482,15 +456,9 @@ impl StorageTestSuite { ensure!(purged, "expected did `{did}` to have been purged"); - let chain_state: Option = storage - .chain_state_get(&did) - .await - .context("chain_state_get returned an error")?; + let value: Option> = storage.blob_get(&did).await.context("blob_get returned an error")?; - ensure!( - chain_state.is_none(), - "expected chain_state_get to return `None` after purging" - ); + ensure!(value.is_none(), "expected blob_get to return `None` after purging"); let exists: bool = storage .key_exists(&did, &location) diff --git a/identity_account_storage/src/storage/traits.rs b/identity_account_storage/src/storage/traits.rs index 7816ce1e3a..99c004e617 100644 --- a/identity_account_storage/src/storage/traits.rs +++ b/identity_account_storage/src/storage/traits.rs @@ -9,11 +9,9 @@ use identity_core::crypto::KeyType; use identity_core::crypto::PrivateKey; use identity_core::crypto::PublicKey; use identity_iota_core::did::IotaDID; -use identity_iota_core::document::IotaDocument; use identity_iota_core::tangle::NetworkName; use crate::error::Result; -use crate::identity::ChainState; #[cfg(feature = "encryption")] use crate::types::CekAlgorithm; #[cfg(feature = "encryption")] @@ -151,17 +149,11 @@ pub trait Storage: storage_sub_trait::StorageSendSyncMaybe + Debug { private_key: &KeyLocation, ) -> Result>; - /// Returns the chain state of the identity specified by `did`. - async fn chain_state_get(&self, did: &IotaDID) -> Result>; + /// Stores an arbitrary blob for the identity specified by `did`. + async fn blob_set(&self, did: &IotaDID, blob: Vec) -> Result<()>; - /// Set the chain state of the identity specified by `did`. - async fn chain_state_set(&self, did: &IotaDID, chain_state: &ChainState) -> Result<()>; - - /// Returns the [`IotaDocument`] of the identity specified by `did`. - async fn document_get(&self, did: &IotaDID) -> Result>; - - /// Sets a new state for the identity specified by `did`. - async fn document_set(&self, did: &IotaDID, state: &IotaDocument) -> Result<()>; + /// Returns the blob stored by the identity specified by `did`. + async fn blob_get(&self, did: &IotaDID) -> Result>>; /// Persists any unsaved changes. async fn flush_changes(&self) -> Result<()>; From 523a4de5a0c8da2df3de090e2a59bcbdd28a1d5a Mon Sep 17 00:00:00 2001 From: cycraig Date: Tue, 26 Jul 2022 12:00:47 +0200 Subject: [PATCH 30/89] Feature-gate `iota-client` dependency, integrate `StardustDID` (#958) * Integrate StardustDID into StardustDocument * Feature-gate iota-client dependency * Add StardustDID::from_block * Rename deserialize_from_output to unpack_from_output * Fix compilation * Fix rustdoc * Dynamically retrieve network HRP in example * Update identity_stardust/src/did/stardust_did.rs --- identity_stardust/Cargo.toml | 7 +- identity_stardust/examples/create_did.rs | 32 +- identity_stardust/src/did/mod.rs | 6 +- identity_stardust/src/did/stardust_did.rs | 319 ++++++++++-------- identity_stardust/src/document/mod.rs | 1 - .../src/document/stardust_document.rs | 140 +++----- identity_stardust/src/error.rs | 9 +- identity_stardust/src/lib.rs | 13 +- .../src/state_metadata/document.rs | 39 +-- 9 files changed, 286 insertions(+), 280 deletions(-) diff --git a/identity_stardust/Cargo.toml b/identity_stardust/Cargo.toml index e3d4e10697..0731992276 100644 --- a/identity_stardust/Cargo.toml +++ b/identity_stardust/Cargo.toml @@ -30,12 +30,13 @@ git = "https://github.com/iotaledger/iota.rs" rev = "2a35c7affe15034fd1b82884966269882bffb23b" # develop branch, 2022-07-18 features = ["tls"] default-features = false +optional = true [dev-dependencies] anyhow = { version = "1.0.57" } iota-crypto = { version = "0.12.1", default-features = false, features = ["bip39", "bip39-en"] } proptest = { version = "1.0.0", default-features = false, features = ["std"] } -tokio = { version = "1.17.0", default-features = false, features = ["macros"] } +tokio = { version = "1.17.0", default-features = false, features = ["rt-multi-thread", "macros"] } [package.metadata.docs.rs] # To build locally: @@ -44,6 +45,8 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [features] -default = ["revocation-bitmap"] +default = ["iota-client", "revocation-bitmap"] +# Enables the iota-client dependency and associated helper functions. +iota-client = ["dep:iota-client"] # Enables revocation with `RevocationBitmap2022`. revocation-bitmap = ["identity_did/revocation-bitmap"] diff --git a/identity_stardust/examples/create_did.rs b/identity_stardust/examples/create_did.rs index 4e18ecf0b7..153db9dce9 100644 --- a/identity_stardust/examples/create_did.rs +++ b/identity_stardust/examples/create_did.rs @@ -1,3 +1,8 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::str::FromStr; + use identity_core::crypto::KeyPair; use identity_core::crypto::KeyType; use identity_did::verification::MethodScope; @@ -14,11 +19,12 @@ use iota_client::block::output::BasicOutputBuilder; use iota_client::block::output::ByteCostConfig; use iota_client::block::output::Feature; use iota_client::block::output::Output; -use iota_client::constants::SHIMMER_TESTNET_BECH32_HRP; use iota_client::secret::mnemonic::MnemonicSecretManager; use iota_client::secret::SecretManager; use iota_client::Client; +use identity_stardust::NetworkName; +use identity_stardust::StardustDID; use identity_stardust::StardustDocument; use identity_stardust::StardustVerificationMethod; @@ -44,6 +50,7 @@ async fn main() -> anyhow::Result<()> { // let endpoint = "http://localhost:14265"; let endpoint = "https://api.alphanet.iotaledger.net"; let faucet_manual = "https://faucet.alphanet.iotaledger.net"; + let network_name = NetworkName::try_from("alpha")?; // =========================================================================== // Step 1: Create or load your wallet. @@ -65,8 +72,13 @@ async fn main() -> anyhow::Result<()> { .with_node_sync_disabled() .finish()?; + // Get the Bech32 human-readable part (HRP) of the protocol. + // E.g. "rms" for the Shimmer testnet. + let node_info = client.get_info().await?; + let network_hrp = node_info.node_info.protocol.bech32_hrp; + let address = client.get_addresses(&secret_manager).with_range(0..1).get_raw().await?[0]; - let address_bech32 = address.to_bech32(SHIMMER_TESTNET_BECH32_HRP); + let address_bech32 = address.to_bech32(network_hrp); println!("Wallet address: {address_bech32}"); println!("INTERACTION REQUIRED: request faucet funds to the above wallet from {faucet_manual}"); @@ -79,10 +91,10 @@ async fn main() -> anyhow::Result<()> { // =========================================================================== // Create an empty DID Document. - // All new Stardust DID Documents initially use a placeholder DID, - // "did:stardust:0x00000000000000000000000000000000". - - let document: StardustDocument = StardustDocument::new(); + // + // All new Stardust DID Documents initially use a placeholder DID for the network, + // "did:stardust:rms:0x0000000000000000000000000000000000000000000000000000000000000000". + let document: StardustDocument = StardustDocument::new(&network_name); println!("DID Document {:#}", document); @@ -118,7 +130,7 @@ async fn main() -> anyhow::Result<()> { let _ = client.retry_until_included(&block1.id(), None, None).await?; // Infer DID from the Alias Output block. - let did = StardustDocument::did_from_block(&block1)?; + let did: StardustDID = StardustDID::from_block(&block1, &network_name)?; println!("DID: {did}"); // =========================================================================== @@ -127,8 +139,8 @@ async fn main() -> anyhow::Result<()> { // iota.rs indexer example: // https://github.com/iotaledger/iota.rs/blob/f945ccf326829a418334942ae9cf53b8fab3dbe5/examples/indexer.rs - // Extract Alias ID from DID. - let alias_id: AliasId = StardustDocument::did_to_alias_id(&did)?; + // Extract Alias ID from the DID tag. + let alias_id: AliasId = AliasId::from_str(did.tag())?; println!("Alias ID: {alias_id}"); // Query Indexer INX Plugin for the Output of the Alias ID. @@ -139,7 +151,7 @@ async fn main() -> anyhow::Result<()> { println!("Output: {output:?}"); // The resolved DID Document replaces the placeholder DID with the correct one. - let resolved_document = StardustDocument::deserialize_from_output(&did, &output)?; + let resolved_document = StardustDocument::unpack_from_output(&did, &output)?; println!("Resolved Document: {resolved_document:#}"); let alias_output = match output { diff --git a/identity_stardust/src/did/mod.rs b/identity_stardust/src/did/mod.rs index 9a182e6015..1c1aed99df 100644 --- a/identity_stardust/src/did/mod.rs +++ b/identity_stardust/src/did/mod.rs @@ -1,7 +1,7 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -mod stardust_did; pub use stardust_did::StardustDID; -// TODO: Uncomment once the `document` module gets refactored to use the types from this module. -//pub use stardust_did::StardustDIDUrl; +pub use stardust_did::StardustDIDUrl; + +mod stardust_did; diff --git a/identity_stardust/src/did/stardust_did.rs b/identity_stardust/src/did/stardust_did.rs index 37bbb004b5..381e5942b0 100644 --- a/identity_stardust/src/did/stardust_did.rs +++ b/identity_stardust/src/did/stardust_did.rs @@ -6,21 +6,26 @@ use core::fmt::Debug; use core::fmt::Display; use core::fmt::Formatter; use core::str::FromStr; -use identity_core::common::KeyComparable; -use serde::Deserialize; -use serde::Serialize; +use identity_core::common::KeyComparable; use identity_did::did::BaseDIDUrl; use identity_did::did::CoreDID; use identity_did::did::DIDError; use identity_did::did::DIDUrl; use identity_did::did::DID; +use serde::Deserialize; +use serde::Serialize; use crate::NetworkName; pub type Result = std::result::Result; -// The length of an AliasID, which is a BLAKE2b-256 hash (32-bytes). +/// A DID URL conforming to the IOTA Stardust UTXO DID method specification. +/// +/// See [`DIDUrl`]. +pub type StardustDIDUrl = DIDUrl; + +// The length of an Alias ID, which is a BLAKE2b-256 hash (32-bytes). const TAG_BYTES_LEN: usize = 32; /// A DID conforming to the IOTA UTXO DID method specification. @@ -43,18 +48,12 @@ impl StardustDID { /// The default Tangle network (`"main"`). pub const DEFAULT_NETWORK: &'static str = "main"; - /// Converts an owned [`CoreDID`] to a [`StardustDID`]. - /// - /// # Errors - /// - /// Returns `Err` if the input does not conform to the [`StardustDID`] specification. - pub fn try_from_core(did: CoreDID) -> Result { - Self::check_validity(&did)?; - - Ok(Self(Self::normalize(did))) - } + // =========================================================================== + // Constructors + // =========================================================================== - /// Constructs a new [`StardustDID`] from a byte representation of the tag and the given network name. + /// Constructs a new [`StardustDID`] from a byte representation of the tag and the given + /// network name. /// /// See also [`StardustDID::placeholder`]. /// @@ -74,15 +73,6 @@ impl StardustDID { Self::parse(did).expect("DIDs constructed with new should be valid") } - /// Parses an [`StardustDID`] from the given `input`. - /// - /// # Errors - /// - /// Returns `Err` if the input does not conform to the [`StardustDID`] specification. - pub fn parse(input: impl AsRef) -> Result { - CoreDID::parse(input).and_then(Self::try_from_core) - } - /// Creates a new placeholder [`StardustDID`] with the given network name. /// /// # Example @@ -98,59 +88,74 @@ impl StardustDID { Self::new(&[0; 32], network_name) } - // Check if the tag matches a potential alias_id - fn check_tag_str(tag: &str) -> Result<()> { - prefix_hex::decode::<[u8; TAG_BYTES_LEN]>(tag) - .map_err(|_| DIDError::InvalidMethodId) - .map(|_| ()) - } - - // Normalizes the DID `method_id` by removing the default network segment if present. - // - // E.g. - // - `"did:stardust:main:123" -> "did:stardust:123"` is normalized - // - `"did:stardust:dev:123" -> "did:stardust:dev:123"` is unchanged - // TODO: Remove the lint once this bug in clippy has been fixed. Without to_owned a mutable reference will be aliased. - #[allow(clippy::unnecessary_to_owned)] - fn normalize(mut did: CoreDID) -> CoreDID { - let method_id = did.method_id(); - let (network, tag) = Self::denormalized_components(method_id); - if tag.len() == method_id.len() || network != Self::DEFAULT_NETWORK { - did - } else { - did - .set_method_id(tag.to_owned()) - .expect("normalizing a valid CoreDID should be Ok"); - did - } + /// Parses an [`StardustDID`] from the given `input`. + /// + /// # Errors + /// + /// Returns `Err` if the input does not conform to the [`StardustDID`] specification. + pub fn parse(input: impl AsRef) -> Result { + CoreDID::parse(input).and_then(Self::try_from_core) } - /// Checks if the given `DID` has a valid [`StardustDID`] network name. + /// Converts a [`CoreDID`] to a [`StardustDID`]. /// /// # Errors /// - /// Returns `Err` if the input is not a valid network name according to the [`StardustDID`] method specification. - pub fn check_network(did: &D) -> Result<()> { - let network_name = Self::denormalized_components(did.method_id()).0; - NetworkName::validate_network_name(network_name).map_err(|_| DIDError::Other("invalid network name")) + /// Returns `Err` if the input does not conform to the [`StardustDID`] specification. + pub fn try_from_core(did: CoreDID) -> Result { + Self::check_validity(&did)?; + + Ok(Self(Self::normalize(did))) + } + + // =========================================================================== + // Properties + // =========================================================================== + + /// Returns the IOTA `network` name of the `DID`. + pub fn network_str(&self) -> &str { + Self::denormalized_components(self.method_id()).0 } + /// Returns the tag of the `DID`, which is a hex-encoded Alias ID. + pub fn tag(&self) -> &str { + Self::denormalized_components(self.method_id()).1 + } + + // =========================================================================== + // Validation + // =========================================================================== + /// Checks if the given `DID` is syntactically valid according to the [`StardustDID`] method specification. /// /// # Errors /// /// Returns `Err` if the input is not a syntactically valid [`StardustDID`]. pub fn check_validity(did: &D) -> Result<()> { - Self::check_method(did).and_then(|_| Self::check_method_id(did)) + Self::check_method(did) + .and_then(|_| Self::check_tag(did)) + .and_then(|_| Self::check_network(did)) + } + + /// Returns a `bool` indicating if the given `DID` is valid according to the + /// [`StardustDID`] method specification. + /// + /// Equivalent to `StardustDID::check_validity(did).is_ok()`. + pub fn is_valid(did: &CoreDID) -> bool { + Self::check_validity(did).is_ok() } + // =========================================================================== + // Helpers + // =========================================================================== + /// Checks if the given `DID` has a valid [`StardustDID`] `method` (i.e. `"stardust"`). /// /// # Errors /// /// Returns `Err` if the input represents another method. // TODO: Change the naming in the docs once we remove the code for the current IOTA method. - pub fn check_method(did: &D) -> Result<()> { + fn check_method(did: &D) -> Result<()> { (did.method() == Self::METHOD) .then_some(()) .ok_or(DIDError::InvalidMethodName) @@ -161,24 +166,43 @@ impl StardustDID { /// # Errors /// /// Returns `Err` if the input does not have a [`StardustDID`] compliant method id. - pub fn check_method_id(did: &D) -> Result<()> { - let (network, tag) = Self::denormalized_components(did.method_id()); - NetworkName::validate_network_name(network) + fn check_tag(did: &D) -> Result<()> { + let (_, tag) = Self::denormalized_components(did.method_id()); + + // Implicitly catches if there are too many segments (:) in the DID too. + prefix_hex::decode::<[u8; TAG_BYTES_LEN]>(tag) .map_err(|_| DIDError::InvalidMethodId) - .and_then(|_| Self::check_tag_str(tag)) + .map(|_| ()) } - /// Returns a `bool` indicating if the given `DID` is valid according to the - /// [`StardustDID`] method specification. + /// Checks if the given `DID` has a valid [`StardustDID`] network name. /// - /// Equivalent to `Self::check_validity(did).is_ok()`. - pub fn is_valid(did: &CoreDID) -> bool { - Self::check_validity(did).is_ok() + /// # Errors + /// + /// Returns `Err` if the input is not a valid network name according to the [`StardustDID`] method specification. + fn check_network(did: &D) -> Result<()> { + let (network_name, _) = Self::denormalized_components(did.method_id()); + NetworkName::validate_network_name(network_name).map_err(|_| DIDError::Other("invalid network name")) } - /// Returns the IOTA `network` name of the `DID`. - pub fn network_str(&self) -> &str { - Self::denormalized_components(self.method_id()).0 + /// Normalizes the DID `method_id` by removing the default network segment if present. + /// + /// E.g. + /// - `"did:stardust:main:123" -> "did:stardust:123"` is normalized + /// - `"did:stardust:dev:123" -> "did:stardust:dev:123"` is unchanged + // TODO: Remove the lint once this bug in clippy has been fixed. Without to_owned a mutable reference will be aliased. + #[allow(clippy::unnecessary_to_owned)] + fn normalize(mut did: CoreDID) -> CoreDID { + let method_id = did.method_id(); + let (network, tag) = Self::denormalized_components(method_id); + if tag.len() == method_id.len() || network != Self::DEFAULT_NETWORK { + did + } else { + did + .set_method_id(tag.to_owned()) + .expect("normalizing a valid CoreDID should be Ok"); + did + } } /// foo:bar -> (foo,bar) @@ -193,18 +217,58 @@ impl StardustDID { // Self::DEFAULT_NETWORK is built from a static reference so unwrapping is fine .unwrap_or((Self::DEFAULT_NETWORK, input)) } +} - /// Returns the unique tag of the `DID`. - pub fn tag(&self) -> &str { - Self::denormalized_components(self.method_id()).1 +#[cfg(feature = "iota-client")] +mod stardust_did_iota_client { + use std::ops::Deref; + + use iota_client::block::output::AliasId; + use iota_client::block::output::Output; + use iota_client::block::output::OutputId; + use iota_client::block::payload::transaction::TransactionEssence; + use iota_client::block::payload::Payload; + use iota_client::block::Block; + + use super::*; + + impl StardustDID { + /// Extracts an Alias ID from a published block and returns a new DID from it. + /// + /// NOTE: if there are multiple Alias Outputs in the payload, this uses the first one. + // TODO: can hopefully remove from public API if the publishing logic is wrapped. + pub fn from_block(block: &Block, network: &NetworkName) -> crate::Result { + let payload: &Payload = block.payload().ok_or(DIDError::Other("block missing payload"))?; + // TODO: improve error handling messages. + let output_id: OutputId = get_alias_output_id_from_payload(payload)?; + let id: AliasId = AliasId::from(output_id); + Ok(StardustDID::new(id.deref(), network)) + } } - /// Replace the network name of this [`StardustDID`] leaving all other segments (did, method, tag) intact. - pub fn with_network_name(mut self, name: NetworkName) -> Self { - let new_method_id: String = format!("{}:{}", name, self.tag()); - // unwrap is fine as we are only replacing the network - self.0.set_method_id(new_method_id).unwrap(); - self + // helper function to get the output id for the first alias output + // TODO: improve error handling + fn get_alias_output_id_from_payload(payload: &Payload) -> crate::Result { + if let Payload::Transaction(tx_payload) = payload { + let TransactionEssence::Regular(regular) = tx_payload.essence(); + if let Some((index, _)) = regular + .outputs() + .iter() + .enumerate() + .find(|(_, item)| matches!(item, Output::Alias(_))) + { + Ok(OutputId::new( + tx_payload.id(), + index + .try_into() + .map_err(|_| DIDError::Other("output index exceeds u16"))?, + )?) + } else { + Err(DIDError::Other("no alias output in transaction essence"))? + } + } else { + Err(DIDError::Other("not a transaction payload"))? + } } } @@ -335,23 +399,18 @@ impl KeyComparable for StardustDID { #[cfg(test)] mod tests { - - use iota_client::block::output::AliasId; - use iota_client::block::output::OutputId; - use iota_client::block::output::OUTPUT_INDEX_RANGE; - use iota_client::block::payload::transaction::TransactionId; use once_cell::sync::Lazy; use proptest::strategy::Strategy; use proptest::*; use super::*; - const INITIAL_ALIAS_ID_STR: &str = "0x0000000000000000000000000000000000000000000000000000000000000000"; - // =========================================================================================================================== // Reusable constants and statics // =========================================================================================================================== + const PLACEHOLDER_TAG: &str = "0x0000000000000000000000000000000000000000000000000000000000000000"; + // obtained AliasID from a valid OutputID string // output_id copied from https://github.com/iotaledger/bee/blob/30cab4f02e9f5d72ffe137fd9eb09723b4f0fdb6/bee-block/tests/output_id.rs // value of AliasID computed from AliasId::from(OutputId).to_string() @@ -380,17 +439,13 @@ mod tests { "42foo", ]; - const INVALID_NETWORK_NAMES: [&str; 10] = [ - "Main", "fOo", "deV", "féta", "", " ", "foo ", " foo", "1234567", "foobar0", - ]; - static VALID_STARDUST_DID_STRINGS: Lazy> = Lazy::new(|| { let network_tag_to_did = |network, tag| format!("did:{}:{}:{}", StardustDID::METHOD, network, tag); let valid_strings: Vec = VALID_NETWORK_NAMES .iter() .flat_map(|network| { - [VALID_ALIAS_ID_STR, INITIAL_ALIAS_ID_STR] + [VALID_ALIAS_ID_STR, PLACEHOLDER_TAG] .iter() .map(move |tag| network_tag_to_did(network, tag)) }) @@ -456,6 +511,9 @@ mod tests { let mut check_network_executed: bool = false; + const INVALID_NETWORK_NAMES: [&str; 10] = [ + "Main", "fOo", "deV", "féta", "", " ", "foo ", " foo", "1234567", "foobar0", + ]; for network_name in INVALID_NETWORK_NAMES { let did_string: String = format!( "did:method:{}:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK", @@ -478,11 +536,11 @@ mod tests { } #[test] - fn valid_check_method_id() { + fn valid_check_tag() { for input in VALID_STARDUST_DID_STRINGS.iter() { let did_core: CoreDID = CoreDID::parse(input).unwrap(); assert!( - StardustDID::check_method_id(&did_core).is_ok(), + StardustDID::check_tag(&did_core).is_ok(), "test: valid_check_method_id failed on input {}", input ); @@ -495,15 +553,13 @@ mod tests { let did_other_core: CoreDID = CoreDID::parse(&did_other_string).unwrap(); let did_other_with_network_core: CoreDID = CoreDID::parse(&did_other_with_network).unwrap(); - assert!(StardustDID::check_method_id(&did_other_core).is_ok()); - assert!(StardustDID::check_method_id(&did_other_with_network_core).is_ok()); + assert!(StardustDID::check_tag(&did_other_core).is_ok()); + assert!(StardustDID::check_tag(&did_other_with_network_core).is_ok()); } #[test] - fn invalid_check_method_id() { + fn invalid_check_tag() { let invalid_method_id_strings = [ - // Invalid network name - format!("did:method:1234567:{}", VALID_ALIAS_ID_STR), // Too many segments format!("did:method:main:test:{}", VALID_ALIAS_ID_STR), // Tag is not prefixed @@ -519,10 +575,11 @@ mod tests { for input in invalid_method_id_strings { let did_core: CoreDID = CoreDID::parse(input).unwrap(); - assert!(matches!( - StardustDID::check_method_id(&did_core), - Err(DIDError::InvalidMethodId) - )); + assert!( + matches!(StardustDID::check_tag(&did_core), Err(DIDError::InvalidMethodId)), + "{}", + did_core + ); } } @@ -534,7 +591,7 @@ mod tests { fn placeholder_produces_a_did_with_expected_string_representation() { assert_eq!( StardustDID::placeholder(&NetworkName::try_from(StardustDID::DEFAULT_NETWORK).unwrap()).as_str(), - format!("did:{}:{}", StardustDID::METHOD, INITIAL_ALIAS_ID_STR) + format!("did:{}:{}", StardustDID::METHOD, PLACEHOLDER_TAG) ); for name in VALID_NETWORK_NAMES @@ -545,7 +602,7 @@ mod tests { let did: StardustDID = StardustDID::placeholder(&network_name); assert_eq!( did.as_str(), - format!("did:{}:{}:{}", StardustDID::METHOD, name, INITIAL_ALIAS_ID_STR) + format!("did:{}:{}:{}", StardustDID::METHOD, name, PLACEHOLDER_TAG) ); } } @@ -590,7 +647,7 @@ mod tests { // invalid network name (exceeded six characters) assert!(matches!( StardustDID::parse(format!("did:{}:1234567:{}", StardustDID::METHOD, valid_alias_id)), - Err(DIDError::InvalidMethodId) + Err(DIDError::Other(_)) )); // invalid network name (contains non ascii character é) @@ -612,7 +669,7 @@ mod tests { )); }; - execute_assertions(INITIAL_ALIAS_ID_STR); + execute_assertions(PLACEHOLDER_TAG); execute_assertions(VALID_ALIAS_ID_STR); } @@ -620,14 +677,20 @@ mod tests { // Test constructors with randomly generated input // =========================================================================================================================== - fn arbitrary_alias_id() -> impl Strategy { - (proptest::prelude::any::<[u8; 32]>(), OUTPUT_INDEX_RANGE).prop_map(|(bytes, idx)| { - let transaction_id: TransactionId = TransactionId::new(bytes); - let output_id: OutputId = OutputId::new(transaction_id, idx).unwrap(); - AliasId::from(output_id) - }) + #[cfg(feature = "iota-client")] + fn arbitrary_alias_id() -> impl Strategy { + ( + proptest::prelude::any::<[u8; 32]>(), + iota_client::block::output::OUTPUT_INDEX_RANGE, + ) + .prop_map(|(bytes, idx)| { + let transaction_id = iota_client::block::payload::transaction::TransactionId::new(bytes); + let output_id = iota_client::block::output::OutputId::new(transaction_id, idx).unwrap(); + iota_client::block::output::AliasId::from(output_id) + }) } + #[cfg(feature = "iota-client")] proptest! { #[test] fn property_based_valid_parse(alias_id in arbitrary_alias_id()) { @@ -636,6 +699,7 @@ mod tests { } } + #[cfg(feature = "iota-client")] proptest! { #[test] fn property_based_new(bytes in proptest::prelude::any::<[u8;32]>()) { @@ -646,12 +710,13 @@ mod tests { } } + #[cfg(feature = "iota-client")] proptest! { #[test] fn property_based_alias_id_string_representation_roundtrip(alias_id in arbitrary_alias_id()) { for network_name in VALID_NETWORK_NAMES.iter().map(|name| NetworkName::try_from(*name).unwrap()) { assert_eq!( - AliasId::from_str(StardustDID::new(&alias_id, &network_name).tag()).unwrap(), + iota_client::block::output::AliasId::from_str(StardustDID::new(&alias_id, &network_name).tag()).unwrap(), alias_id ); } @@ -753,7 +818,7 @@ mod tests { assert_eq!(did.network_str(), "custom"); }; - execute_assertions(INITIAL_ALIAS_ID_STR); + execute_assertions(PLACEHOLDER_TAG); execute_assertions(VALID_ALIAS_ID_STR); } @@ -785,38 +850,14 @@ mod tests { .unwrap(); assert_eq!(did.tag(), valid_alias_id); }; - execute_assertions(INITIAL_ALIAS_ID_STR); + execute_assertions(PLACEHOLDER_TAG); execute_assertions(VALID_ALIAS_ID_STR); } - // =========================================================================================================================== - // Test setters - // =========================================================================================================================== - - #[test] - fn replace_network_name() { - for did in VALID_STARDUST_DID_STRINGS.iter() { - let stardust_did: StardustDID = StardustDID::parse(did).unwrap(); - for name in VALID_NETWORK_NAMES { - let old_tag: String = stardust_did.tag().to_string(); - let network_name: NetworkName = NetworkName::try_from(name).unwrap(); - let transfromed: StardustDID = stardust_did.clone().with_network_name(network_name.clone()); - assert_eq!(old_tag, transfromed.tag()); - assert_eq!(transfromed.network_str(), name); - } - } - } - // =========================================================================================================================== // Test DIDUrl // =========================================================================================================================== - // TODO: Move `StardustDIDUrl` out of this test module once the `document` module gets refactored to use the types - // from this module. - /// A DID URL conforming to the IOTA Stardust UTXO DID method specification. - /// - /// See [`DIDUrl`]. - type StardustDIDUrl = DIDUrl; #[test] fn test_parse_did_url_valid() { let execute_assertions = |valid_alias_id: &str| { @@ -885,7 +926,7 @@ mod tests { )) .is_ok()); }; - execute_assertions(INITIAL_ALIAS_ID_STR); + execute_assertions(PLACEHOLDER_TAG); execute_assertions(VALID_ALIAS_ID_STR); } @@ -904,7 +945,7 @@ mod tests { assert_eq!(did_url.query(), Some("diff=true")); assert_eq!(did_url.fragment(), Some("foo")); }; - execute_assertions(INITIAL_ALIAS_ID_STR); + execute_assertions(PLACEHOLDER_TAG); execute_assertions(VALID_ALIAS_ID_STR); } } diff --git a/identity_stardust/src/document/mod.rs b/identity_stardust/src/document/mod.rs index 2a03721617..208dd3670a 100644 --- a/identity_stardust/src/document/mod.rs +++ b/identity_stardust/src/document/mod.rs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 pub use stardust_document::StardustCoreDocument; -pub use stardust_document::StardustDIDUrl; pub use stardust_document::StardustDocument; pub use stardust_document::StardustService; pub use stardust_document::StardustVerificationMethod; diff --git a/identity_stardust/src/document/stardust_document.rs b/identity_stardust/src/document/stardust_document.rs index c7829ba193..c61b0ab71c 100644 --- a/identity_stardust/src/document/stardust_document.rs +++ b/identity_stardust/src/document/stardust_document.rs @@ -4,7 +4,6 @@ use core::fmt; use core::fmt::Debug; use core::fmt::Display; -use std::str::FromStr; use identity_core::common::Object; use identity_core::common::OneOrSet; @@ -15,11 +14,6 @@ use identity_core::crypto::GetSignature; use identity_core::crypto::PrivateKey; use identity_core::crypto::ProofOptions; use identity_core::crypto::SetSignature; -use identity_core::utils::Base; -use identity_core::utils::BaseEncoding; -use identity_did::did::CoreDID; -use identity_did::did::DIDUrl; -use identity_did::did::DID; use identity_did::document::CoreDocument; use identity_did::document::Document; use identity_did::service::Service; @@ -31,29 +25,16 @@ use identity_did::verification::MethodScope; use identity_did::verification::MethodUriType; use identity_did::verification::TryMethod; use identity_did::verification::VerificationMethod; -use iota_client::block::output::AliasId; -use iota_client::block::output::Output; -use iota_client::block::output::OutputId; -use iota_client::block::payload::transaction::TransactionEssence; -use iota_client::block::payload::Payload; -use iota_client::block::Block; use serde::Deserialize; use serde::Serialize; -use crate::document::stardust_document_metadata::StardustDocumentMetadata; use crate::error::Result; -use crate::state_metadata::StateMetadataEncoding; -use crate::state_metadata::PLACEHOLDER_DID; +use crate::NetworkName; +use crate::StardustDID; +use crate::StardustDIDUrl; +use crate::StardustDocumentMetadata; use crate::StateMetadataDocument; - -// TODO: replace with StardustDID struct. -type StardustDID = CoreDID; - -/// A DID URL conforming to the IOTA DID method specification. -/// -/// See [`DIDUrl`]. -// TODO: move to `did` module. -pub type StardustDIDUrl = DIDUrl; +use crate::StateMetadataEncoding; /// A [`VerificationMethod`] adhering to the IOTA DID method specification. pub type StardustVerificationMethod = VerificationMethod; @@ -80,10 +61,12 @@ impl StardustDocument { // Constructors // =========================================================================== - /// Constructs an empty DID Document with a [`StardustDocument::placeholder_did`] identifier. + /// Constructs an empty DID Document with a [`StardustDID::placeholder`] identifier + /// for the given `network`. // TODO: always take Option or `new_with_options` for a particular network? - pub fn new() -> Self { - Self::new_with_id(Self::placeholder_did().clone()) + // TODO: store the network in the serialized state metadata? Currently it's lost during packing. + pub fn new(network: &NetworkName) -> Self { + Self::new_with_id(StardustDID::placeholder(network)) } /// Constructs an empty DID Document with the given identifier. @@ -101,13 +84,6 @@ impl StardustDocument { // Properties // =========================================================================== - /// Returns the placeholder DID of newly constructed DID Documents, - /// `"did:0:0"`. - // TODO: generalise to take network name? - pub fn placeholder_did() -> &'static StardustDID { - &PLACEHOLDER_DID - } - /// Returns the DID document identifier. pub fn id(&self) -> &StardustDID { self.document.id() @@ -311,63 +287,35 @@ impl StardustDocument { /// Deserializes the document from the state metadata bytes of an Alias Output. /// /// NOTE: `did` is required since it is omitted from the serialized DID Document and - /// cannot be inferred from the [`Output`]. It also indicates the network, which is not + /// cannot be inferred from the state metadata. It also indicates the network, which is not /// encoded in the `AliasId` alone. pub fn unpack(did: &StardustDID, state_metadata: &[u8]) -> Result { - StateMetadataDocument::unpack(state_metadata).map(|doc| doc.into_stardust_document(did)) - } - - /// Constructs a DID from an Alias ID. - /// - /// Uses the hex-encoding of the Alias ID as the DID tag. - // TODO: pass in optional network? Feature-gate to avoid iota-client hard dependency? - pub fn alias_id_to_did(id: &AliasId) -> Result { - // Manually encode to hex to avoid 0x prefix. - let hex: String = BaseEncoding::encode(id.as_slice(), Base::Base16Lower); - StardustDID::parse(format!("did:stardust:{hex}")).map_err(Into::into) + StateMetadataDocument::unpack(state_metadata).and_then(|doc| doc.into_stardust_document(did)) } +} - pub fn did_to_alias_id(did: &StardustDID) -> Result { - // TODO: just use 0x in the tag as well? - // Prepend 0x manually. - AliasId::from_str(&format!("0x{}", did.method_id())).map_err(Into::into) - } +#[cfg(feature = "iota-client")] +mod stardust_document_iota_client { + use iota_client::block::output::Output; - // TODO: can hopefully remove if the publishing logic is wrapped. - pub fn did_from_block(block: &Block) -> Result { - let id: AliasId = AliasId::from(get_alias_output_id_from_payload(block.payload().unwrap())); - Self::alias_id_to_did(&id) - } + use crate::Error; - /// Deserializes a JSON-encoded `StardustDocument` from an Alias Output block. - /// - /// NOTE: `did` is required since it is omitted from the serialized DID Document and - /// cannot be inferred from the [`Output`]. It also indicates the network, which is not - /// encoded in the `AliasId` alone. - // TODO: remove? Is `unpack` sufficient? - pub fn deserialize_from_output(did: &StardustDID, output: &Output) -> Result { - let document: &[u8] = match output { - Output::Alias(alias_output) => alias_output.state_metadata(), - _ => panic!("not an alias output"), - }; - Self::unpack(did, document) - } -} + use super::*; -// helper function to get the output id for the first alias output -// TODO: error handling -fn get_alias_output_id_from_payload(payload: &Payload) -> OutputId { - match payload { - Payload::Transaction(tx_payload) => { - let TransactionEssence::Regular(regular) = tx_payload.essence(); - for (index, output) in regular.outputs().iter().enumerate() { - if let Output::Alias(_alias_output) = output { - return OutputId::new(tx_payload.id(), index.try_into().unwrap()).unwrap(); - } - } - panic!("No alias output in transaction essence") + impl StardustDocument { + /// Deserializes a JSON-encoded `StardustDocument` from an Alias Output block. + /// + /// NOTE: `did` is required since it is omitted from the serialized DID Document and + /// cannot be inferred from the [`Output`]. It also indicates the network, which is not + /// encoded in the `AliasId` alone. + // TODO: remove? Is `unpack` sufficient? + pub fn unpack_from_output(did: &StardustDID, output: &Output) -> Result { + let document: &[u8] = match output { + Output::Alias(alias_output) => alias_output.state_metadata(), + _ => return Err(Error::InvalidStateMetadata("not an alias output")), + }; + Self::unpack(did, document) } - _ => panic!("No tx payload"), } } @@ -454,12 +402,6 @@ impl From<(StardustCoreDocument, StardustDocumentMetadata)> for StardustDocument } } -impl Default for StardustDocument { - fn default() -> Self { - Self::new() - } -} - impl Display for StardustDocument { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.fmt_json(f) @@ -472,18 +414,20 @@ impl TryMethod for StardustDocument { #[cfg(test)] mod tests { - use super::*; use identity_core::common::Timestamp; use identity_core::convert::FromJson; use identity_core::convert::ToJson; use identity_core::crypto::KeyPair; use identity_core::crypto::KeyType; + use identity_did::did::DID; use identity_did::verifiable::VerifiableProperties; use identity_did::verification::MethodData; use identity_did::verification::MethodType; + use super::*; + fn valid_did() -> StardustDID { - "did:stardust:0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "did:stardust:0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" .parse() .unwrap() } @@ -520,8 +464,12 @@ mod tests { #[test] fn test_new() { // VALID new(). - let doc1: StardustDocument = StardustDocument::new(); - assert_eq!(doc1.id(), StardustDocument::placeholder_did()); + let network: NetworkName = NetworkName::try_from("test").unwrap(); + let placeholder: StardustDID = StardustDID::placeholder(&network); + let doc1: StardustDocument = StardustDocument::new(&network); + assert_eq!(doc1.id().network_str(), network.as_ref()); + assert_eq!(doc1.id().tag(), placeholder.tag()); + assert_eq!(doc1.id(), &placeholder); assert_eq!(doc1.methods().count(), 0); assert!(doc1.service().is_empty()); @@ -565,7 +513,7 @@ mod tests { properties } - let mut document: StardustDocument = StardustDocument::new(); + let mut document: StardustDocument = StardustDocument::new_with_id(valid_did()); // Try sign using each type of verification relationship. for scope in [ @@ -624,7 +572,7 @@ mod tests { #[test] fn test_services() { // VALID: add one service. - let mut document: StardustDocument = StardustDocument::new(); + let mut document: StardustDocument = StardustDocument::new_with_id(valid_did()); let url1: StardustDIDUrl = document.id().to_url().join("#linked-domain").unwrap(); let service1: StardustService = Service::from_json(&format!( r#"{{ @@ -743,7 +691,7 @@ mod tests { #[test] fn test_json_fieldnames() { - let document: StardustDocument = StardustDocument::new(); + let document: StardustDocument = StardustDocument::new_with_id(valid_did()); let serialization: String = document.to_json().unwrap(); assert_eq!( serialization, diff --git a/identity_stardust/src/error.rs b/identity_stardust/src/error.rs index 67e7dc4666..d717dbeb03 100644 --- a/identity_stardust/src/error.rs +++ b/identity_stardust/src/error.rs @@ -5,6 +5,7 @@ pub type Result = core::result::Result; // TODO: replace all variants with specific errors? #[derive(Debug, thiserror::Error, strum::IntoStaticStr)] +#[non_exhaustive] pub enum Error { #[error("{0}")] CoreError(#[from] identity_core::Error), @@ -12,15 +13,17 @@ pub enum Error { CredError(#[from] identity_credential::Error), #[error("{0}")] InvalidDID(#[from] identity_did::did::DIDError), - #[error("invalid network name")] - InvalidNetworkName, #[error("{0}")] InvalidDoc(#[from] identity_did::Error), + #[cfg(feature = "iota-client")] #[error("{0}")] ClientError(#[from] iota_client::error::Error), + #[cfg(feature = "iota-client")] #[error("{0}")] - BeeError(#[from] iota_client::block::Error), + BlockError(#[from] iota_client::block::Error), + #[error("invalid network name")] + InvalidNetworkName, #[error("invalid state metadata {0}")] InvalidStateMetadata(&'static str), #[error("credential revocation error")] diff --git a/identity_stardust/src/lib.rs b/identity_stardust/src/lib.rs index 200fb656d5..eb97610549 100644 --- a/identity_stardust/src/lib.rs +++ b/identity_stardust/src/lib.rs @@ -4,16 +4,15 @@ #![forbid(unsafe_code)] #![allow(clippy::upper_case_acronyms)] -pub use self::error::Error; -pub use self::error::Result; - +pub use did::StardustDID; +pub use did::StardustDIDUrl; pub use document::*; +pub use network::NetworkName; pub use state_metadata::*; -pub use did::StardustDID; -// TODO: Uncomment once `document` has been refactored to use the types from the `did` module in this crate. -// pub use did::StardustDIDUrl; -pub use network::NetworkName; +pub use self::error::Error; +pub use self::error::Result; + mod did; mod document; mod error; diff --git a/identity_stardust/src/state_metadata/document.rs b/identity_stardust/src/state_metadata/document.rs index 1858e87543..b11292528a 100644 --- a/identity_stardust/src/state_metadata/document.rs +++ b/identity_stardust/src/state_metadata/document.rs @@ -12,6 +12,7 @@ use serde::Serialize; use crate::error::Result; use crate::Error; use crate::StardustCoreDocument; +use crate::StardustDID; use crate::StardustDocument; use crate::StardustDocumentMetadata; @@ -35,21 +36,22 @@ pub(crate) struct StateMetadataDocument { impl StateMetadataDocument { /// Transforms the document into a [`StardustDocument`] by replacing all placeholders with `original_did`. - pub fn into_stardust_document(self, original_did: &CoreDID) -> StardustDocument { + pub fn into_stardust_document(self, original_did: &StardustDID) -> Result { let Self { document, metadata } = self; - let core_document: StardustCoreDocument = document.map( + let core_document: StardustCoreDocument = document.try_map( // Replace placeholder identifiers. |did| { if did == PLACEHOLDER_DID.as_ref() { - original_did.clone() + Ok(original_did.clone()) } else { - did + // TODO: wrap error? + StardustDID::try_from_core(did) } }, // Do not modify properties. - |o| o, - ); - StardustDocument::from((core_document, metadata)) + Ok, + )?; + Ok(StardustDocument::from((core_document, metadata))) } /// Pack a [`StateMetadataDocument`] into bytes, suitable for inclusion in @@ -116,14 +118,14 @@ impl From for StateMetadataDocument { /// occurrences of its did with a placeholder. fn from(document: StardustDocument) -> Self { let StardustDocument { document, metadata } = document; - let id: CoreDID = document.id().clone(); + let id: StardustDID = document.id().clone(); let core_document: CoreDocument = document.map( // Replace self-referential identifiers with a placeholder, but not others. |did| { if did == id { PLACEHOLDER_DID.clone() } else { - did + CoreDID::from(did) } }, // Do not modify properties. @@ -143,11 +145,11 @@ mod tests { use identity_core::common::Url; use identity_core::crypto::KeyPair; use identity_core::crypto::KeyType; - use identity_did::did::CoreDID; use identity_did::did::DID; use identity_did::verification::MethodScope; use crate::state_metadata::PLACEHOLDER_DID; + use crate::StardustDID; use crate::StardustDocument; use crate::StardustService; use crate::StardustVerificationMethod; @@ -156,15 +158,15 @@ mod tests { struct TestSetup { document: StardustDocument, - did_self: CoreDID, - did_foreign: CoreDID, + did_self: StardustDID, + did_foreign: StardustDID, } fn test_document() -> TestSetup { let did_self = - CoreDID::parse("did:stardust:8036235b6b5939435a45d68bcea7890eef399209a669c8c263fac7f5089b2ec6").unwrap(); + StardustDID::parse("did:stardust:0x8036235b6b5939435a45d68bcea7890eef399209a669c8c263fac7f5089b2ec6").unwrap(); let did_foreign = - CoreDID::parse("did:stardust:71b709dff439f1ac9dd2b9c2e28db0807156b378e13bfa3605ce665aa0d0fdca").unwrap(); + StardustDID::parse("did:stardust:0x71b709dff439f1ac9dd2b9c2e28db0807156b378e13bfa3605ce665aa0d0fdca").unwrap(); let mut document: StardustDocument = StardustDocument::new_with_id(did_self.clone()); let keypair: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); @@ -251,7 +253,7 @@ mod tests { .unwrap() .id() .did(), - &did_foreign + did_foreign.as_ref() ); assert_eq!( @@ -263,7 +265,7 @@ mod tests { .unwrap() .id() .did(), - &did_foreign + did_foreign.as_ref() ); assert_eq!( @@ -279,11 +281,10 @@ mod tests { ); let controllers = state_metadata_doc.document.controller().unwrap(); - assert_eq!(controllers.get(0).unwrap(), &did_foreign); + assert_eq!(controllers.get(0).unwrap(), did_foreign.as_ref()); assert_eq!(controllers.get(1).unwrap(), PLACEHOLDER_DID.as_ref()); - let stardust_document = state_metadata_doc.into_stardust_document(&did_self); - + let stardust_document = state_metadata_doc.into_stardust_document(&did_self).unwrap(); assert_eq!(stardust_document, document); } From 526ec4728a5b3ee9527bf6f9300b3c515c76c0a7 Mon Sep 17 00:00:00 2001 From: cycraig Date: Tue, 26 Jul 2022 14:58:56 +0200 Subject: [PATCH 31/89] Change CI params to use all-features consistently (#959) --- .github/workflows/build-and-test.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 5e9f6034ff..b16cd38060 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -102,13 +102,13 @@ jobs: with: # Build the library, tests, and examples without running them to avoid recompilation in the run tests step command: build - args: --workspace --tests --examples --release + args: --workspace --tests --examples --all-features --release - name: Run tests uses: actions-rs/cargo@v1 with: command: test - args: --all --all-features --release + args: --workspace --all-features --release - name: Run Rust examples # run examples only on ubuntu for now @@ -116,7 +116,7 @@ jobs: run: | cargo read-manifest --manifest-path ./examples/Cargo.toml | \ jq -r '.targets[].name' | \ - parallel -k -j 4 --retries 3 cargo run --example {} --release + parallel -k -j 4 --retries 3 cargo run --example {} --all-features --release - name: Stop sccache uses: './.github/actions/rust/sccache/stop-sccache' @@ -159,13 +159,13 @@ jobs: uses: actions-rs/cargo@v1 with: command: build - args: --manifest-path ./identity_stardust/Cargo.toml --workspace --tests --examples --release + args: --manifest-path ./identity_stardust/Cargo.toml --workspace --tests --examples --all-features --release - name: Run Stardust tests uses: actions-rs/cargo@v1 with: command: test - args: --manifest-path ./identity_stardust/Cargo.toml --all --all-features --release + args: --manifest-path ./identity_stardust/Cargo.toml --workspace --all-features --release - name: Stop sccache uses: './.github/actions/rust/sccache/stop-sccache' From 35444e55e6ef66d26e5b4d749bd9fdca87b58fdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Ha=C3=9F?= Date: Tue, 26 Jul 2022 16:09:26 +0200 Subject: [PATCH 32/89] Update wiki DIDComm flowcharts, add banners (#956) (#962) * Update Images - Changed the styling of the flowcharts for didcomm - Added new banners - Changed Social Preview to show banner if there is one, else Identity Logo * Revert Change back: - Social Preview Image to Logo - Video size * Fix Flowchart Changed Issuance diagram: - "Presentation" to "Signing" - "Resolve DID" to "Sign verifiable credential" Changed Signing diagram: - "signed-acknowledgement" to "signing-acknowledgement" - "receive signed-acknowledgement" to "receive signing-acknowledgement" Co-authored-by: JSto91 <90569726+JSto91@users.noreply.github.com> --- documentation/docs/decentralized_identity.md | 2 + .../docs/getting_started/overview.md | 2 + documentation/docs/introduction.md | 2 +- .../specs/didcomm/protocols/authentication.md | 4 +- .../specs/didcomm/protocols/connection.md | 4 +- .../docs/specs/didcomm/protocols/issuance.md | 4 +- .../docs/specs/didcomm/protocols/post.md | 4 +- .../specs/didcomm/protocols/presentation.md | 4 +- .../didcomm/protocols/revocation-options.md | 4 +- .../specs/didcomm/protocols/revocation.md | 4 +- .../docs/specs/didcomm/protocols/signing.md | 4 +- .../specs/didcomm/protocols/termination.md | 4 +- .../Banner/banner_decentralized_identity.svg | 28 +++++++ .../static/img/Banner/banner_identity.svg | 83 +++++++++++++++++++ .../banner_identity_getting_started.svg | 35 ++++++++ .../img/didcomm/authentication.drawio.svg | 2 +- .../static/img/didcomm/connection.drawio.svg | 2 +- .../static/img/didcomm/issuance.drawio.svg | 2 +- .../static/img/didcomm/post.drawio.svg | 2 +- .../img/didcomm/presentation.drawio.svg | 2 +- .../img/didcomm/revocation-options.drawio.svg | 2 +- .../static/img/didcomm/revocation.drawio.svg | 2 +- .../static/img/didcomm/signing.drawio.svg | 3 +- .../static/img/didcomm/termination.drawio.svg | 2 +- 24 files changed, 179 insertions(+), 28 deletions(-) create mode 100644 documentation/static/img/Banner/banner_decentralized_identity.svg create mode 100644 documentation/static/img/Banner/banner_identity.svg create mode 100644 documentation/static/img/Banner/banner_identity_getting_started.svg diff --git a/documentation/docs/decentralized_identity.md b/documentation/docs/decentralized_identity.md index c75b5a5ac8..c511d4ffa1 100644 --- a/documentation/docs/decentralized_identity.md +++ b/documentation/docs/decentralized_identity.md @@ -10,6 +10,8 @@ keywords: --- # Introduction to Decentralized Identity +![IOTA Decentralized Identity](/img/Banner/banner_decentralized_identity.svg) + Decentralized Identity or Self-Sovereign Identity (SSI) defines a new method for identity management and authentication. It removes the centralized aspects and puts the Identity subject in full control over its own identity. Decentralized Identity provides a solution for the increasing amount of database breaches, the lack of trust in any digital setting, and the increasingly difficult to comply with privacy legislation, such as GDPR. There are three levels of privacy when interacting on the internet: full privacy, verifiable identities, and pseudonymity. With full privacy, neither parties, nor observers, can identify the interacting parties. With verifiable identities, parties can trust each other because they can both provide proof about their identities. With pseudonymity, both parties recognize each other through a pseudonymous identifier. Pseudonymity is the default setting of the internet. However, data harvesting platforms, like diff --git a/documentation/docs/getting_started/overview.md b/documentation/docs/getting_started/overview.md index aaabf754b3..a151cd5754 100644 --- a/documentation/docs/getting_started/overview.md +++ b/documentation/docs/getting_started/overview.md @@ -12,6 +12,8 @@ keywords: # Overview +![Identity getting started](/img/Banner/banner_identity_getting_started.svg) + Using the [standards proposed by W3C](https://www.w3.org/TR/did-core/), this section explains the IOTA Identity implementation. You can use this implementation to create a new digital identity for anyone or anything at any time. To do so, you must first generate a [Decentralized Identifier (DID)](../concepts/decentralized_identifiers/overview) that will serve as a reference to the [DID Document](../concepts/decentralized_identifiers/overview#did-documents). The DID Document contains public keys and other mechanisms to enable the subject to prove their association with the DID. However, you cannot tell much about the subject from a DID. You need to combine the DID with [Verifiable Credentials](../concepts/verifiable_credentials/overview). Verifiable Credentials are statements about the creator of the DID. They can be shared and verified online in a "Bring Your Own Identity" (BYOI) manner, and the DID creator remains in complete control of the process. diff --git a/documentation/docs/introduction.md b/documentation/docs/introduction.md index b8efb342f4..64c10d6de3 100644 --- a/documentation/docs/introduction.md +++ b/documentation/docs/introduction.md @@ -11,7 +11,7 @@ keywords: # IOTA Identity Framework Guide -![IOTA Identity](https://github.com/iotaledger/identity.rs/raw/dev/.meta/identity_banner.png) +![IOTA Identity](/img/Banner/banner_identity.svg) The IOTA Identity framework implements the most common standards and patterns for Decentralized Identity in both a DLT agnostic and `iota` method specification manner. It is designed to work for Identity for People, Organizations, Things, and Objects acting as a unifying-layer of trust between everyone and everything. diff --git a/documentation/docs/specs/didcomm/protocols/authentication.md b/documentation/docs/specs/didcomm/protocols/authentication.md index 1b8ac8d420..d5118c58af 100644 --- a/documentation/docs/specs/didcomm/protocols/authentication.md +++ b/documentation/docs/specs/didcomm/protocols/authentication.md @@ -31,10 +31,10 @@ This protocol allows two parties to mutually authenticate by disclosing and veri ### Interaction -
- ![AuthenticationDiagram](/img/didcomm/authentication.drawio.svg) +
+ For guidance on diagrams see the corresponding section in the overview.
diff --git a/documentation/docs/specs/didcomm/protocols/connection.md b/documentation/docs/specs/didcomm/protocols/connection.md index c5fb69f312..eed7936f3c 100644 --- a/documentation/docs/specs/didcomm/protocols/connection.md +++ b/documentation/docs/specs/didcomm/protocols/connection.md @@ -34,10 +34,10 @@ Allows establishment of a [DIDComm connection](https://identity.foundation/didco ### Interaction -
- ![ConnectionDiagram](/img/didcomm/connection.drawio.svg) +
+ For guidance on diagrams see the corresponding section in the overview.
diff --git a/documentation/docs/specs/didcomm/protocols/issuance.md b/documentation/docs/specs/didcomm/protocols/issuance.md index 0de2e779d8..90002fc871 100644 --- a/documentation/docs/specs/didcomm/protocols/issuance.md +++ b/documentation/docs/specs/didcomm/protocols/issuance.md @@ -33,10 +33,10 @@ Allows a [holder](#roles) to request a [verifiable credential][VC] from an [issu ### Interaction -
- ![IssuanceDiagram](/img/didcomm/issuance.drawio.svg) +
+ For guidance on diagrams see the corresponding section in the overview.
diff --git a/documentation/docs/specs/didcomm/protocols/post.md b/documentation/docs/specs/didcomm/protocols/post.md index ef9dc2ab7e..2dc1b24887 100644 --- a/documentation/docs/specs/didcomm/protocols/post.md +++ b/documentation/docs/specs/didcomm/protocols/post.md @@ -31,10 +31,10 @@ Allows the sending of a single message with arbitrary data. Multiple [post](#pos ### Interaction -
- ![PostDiagram](/img/didcomm/post.drawio.svg) +
+ For guidance on diagrams see the corresponding section in the overview.
diff --git a/documentation/docs/specs/didcomm/protocols/presentation.md b/documentation/docs/specs/didcomm/protocols/presentation.md index d14bb111a3..4cbc4a2d31 100644 --- a/documentation/docs/specs/didcomm/protocols/presentation.md +++ b/documentation/docs/specs/didcomm/protocols/presentation.md @@ -33,10 +33,10 @@ Allows presentation of one or more [verifiable credentials](https://www.w3.org/T ### Interaction -
- ![PresentationDiagram](/img/didcomm/presentation.drawio.svg) +
+ For guidance on diagrams see the corresponding section in the overview.
diff --git a/documentation/docs/specs/didcomm/protocols/revocation-options.md b/documentation/docs/specs/didcomm/protocols/revocation-options.md index 6d36214bb0..761eb1ed29 100644 --- a/documentation/docs/specs/didcomm/protocols/revocation-options.md +++ b/documentation/docs/specs/didcomm/protocols/revocation-options.md @@ -26,10 +26,10 @@ Allows discovery of available [`RevocationInfo`](./revocation#RevocationInfo) ty ### Interaction -
- ![RevocationOptionsDiagram](/img/didcomm/revocation-options.drawio.svg) +
+ For guidance on diagrams see the corresponding section in the overview.
diff --git a/documentation/docs/specs/didcomm/protocols/revocation.md b/documentation/docs/specs/didcomm/protocols/revocation.md index 7060333f59..07d378e496 100644 --- a/documentation/docs/specs/didcomm/protocols/revocation.md +++ b/documentation/docs/specs/didcomm/protocols/revocation.md @@ -29,10 +29,10 @@ Allows to request revocation of an issued [verifiable credential](https://www.w3 ### Interaction -
- ![RevocationDiagram](/img/didcomm/revocation.drawio.svg) +
+ For guidance on diagrams see the corresponding section in the overview.
diff --git a/documentation/docs/specs/didcomm/protocols/signing.md b/documentation/docs/specs/didcomm/protocols/signing.md index 0e6ffbd572..ae40647e6d 100644 --- a/documentation/docs/specs/didcomm/protocols/signing.md +++ b/documentation/docs/specs/didcomm/protocols/signing.md @@ -33,10 +33,10 @@ Allows a trusted-party to request the signing of an unsigned verifiable credenti ### Interaction -
- ![SigningDiagram](/img/didcomm/signing.drawio.svg) +
+ For guidance on diagrams see the corresponding section in the overview.
diff --git a/documentation/docs/specs/didcomm/protocols/termination.md b/documentation/docs/specs/didcomm/protocols/termination.md index a7a99f3acf..023bdf66a1 100644 --- a/documentation/docs/specs/didcomm/protocols/termination.md +++ b/documentation/docs/specs/didcomm/protocols/termination.md @@ -33,10 +33,10 @@ Indicates the graceful termination of a connection. It is expected that no recon ### Interaction -
- ![TerminationDiagram](/img/didcomm/termination.drawio.svg) +
+ For guidance on diagrams see the corresponding section in the overview.
diff --git a/documentation/static/img/Banner/banner_decentralized_identity.svg b/documentation/static/img/Banner/banner_decentralized_identity.svg new file mode 100644 index 0000000000..fb20b93cc4 --- /dev/null +++ b/documentation/static/img/Banner/banner_decentralized_identity.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/documentation/static/img/Banner/banner_identity.svg b/documentation/static/img/Banner/banner_identity.svg new file mode 100644 index 0000000000..61ffbf9bcc --- /dev/null +++ b/documentation/static/img/Banner/banner_identity.svg @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/documentation/static/img/Banner/banner_identity_getting_started.svg b/documentation/static/img/Banner/banner_identity_getting_started.svg new file mode 100644 index 0000000000..5379c9f080 --- /dev/null +++ b/documentation/static/img/Banner/banner_identity_getting_started.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/documentation/static/img/didcomm/authentication.drawio.svg b/documentation/static/img/didcomm/authentication.drawio.svg index 9df473c941..f48af5562b 100644 --- a/documentation/static/img/didcomm/authentication.drawio.svg +++ b/documentation/static/img/didcomm/authentication.drawio.svg @@ -1,4 +1,4 @@ -
Responder
Responder
receive
authentication-request
receive...
signed
authentication-response
signed...
no
no
receive
authentication-result
receive...
problem-report
proble...
yes
yes
Continue?
Continue?
Resolve DID
Resolve DID
yes
yes
no
no
Valid?
Valid?
problem-report
proble...
Requester
Requester
signed
authentication-request
signed...
Requester initiates
Requester initiates
receive
authentication-response
receive...
yes
yes
signed
authentication-result
signed...
problem-report
proble...
no
no
Continue?
Continue?
Resolve DID
Resolve DID
Viewer does not support full SVG 1.1
\ No newline at end of file +
Responder
Responder
receive
authentication-request
receive...
signed
authentication-response
signed...
no
no
receive
authentication-result
receive...
problem-report
proble...
yes
yes
Continue?
Continue?
Resolve DID
Resolve DID
yes
yes
no
no
Valid?
Valid?
problem-report
proble...
Requester
Requester
signed
authentication-request
signed...
Requester initiates
Requester initiates
receive
authentication-response
receive...
yes
yes
signed
authentication-result
signed...
problem-report
proble...
no
no
Continue?
Continue?
Resolve DID
Resolve DID
Text is not SVG - cannot display
\ No newline at end of file diff --git a/documentation/static/img/didcomm/connection.drawio.svg b/documentation/static/img/didcomm/connection.drawio.svg index 578fb3b5b9..407192651d 100644 --- a/documentation/static/img/didcomm/connection.drawio.svg +++ b/documentation/static/img/didcomm/connection.drawio.svg @@ -1,4 +1,4 @@ -
Invitee
Invitee
receive invitation
receive invitation
Alt: Invitee initiates 
using a public ServiceEndpoint
Alt: Invitee initiates...
yes 
yes 
Accept?
Accept?
connection
connection
Valid?
Valid?
Parse ServiceEndpoint
Parse ServiceEndpoint
Establish connection
Establish connection
Inviter
Inviter
invitation
invitation
Inviter initiates
Inviter initiates
receive connection
receive connection
yes
yes
no
no
Accept?
Accept?
problem-report
problem...
no
no
Viewer does not support full SVG 1.1
\ No newline at end of file +
Invitee
Invitee
receive invitation
receive invitation
connection
connection
Parse ServiceEndpoint
Parse ServiceEndpoint
Alt: Invitee initiates 
using a public ServiceEndpoint
Alt: Invitee initiates...
Accept?
Accept?
yes
yes
Establish connection
Establish connection
Inviter
Inviter
invitation
invitation
receive connection
receive connection
Accept?
Accept?
Inviter initiates
Inviter initiates
problem-report
proble...
no
no
yes
yes
no
no
Text is not SVG - cannot display
\ No newline at end of file diff --git a/documentation/static/img/didcomm/issuance.drawio.svg b/documentation/static/img/didcomm/issuance.drawio.svg index dcd2779dd4..598c410b82 100644 --- a/documentation/static/img/didcomm/issuance.drawio.svg +++ b/documentation/static/img/didcomm/issuance.drawio.svg @@ -1,4 +1,4 @@ -
Issuer
Issuer
receive
issuance-request
receive...
Alt: Issuer initiates
Alt: Issuer initiates
issuance
issuance
yes
yes
yes
yes
no
no
Accepted?
Accepted?
receive
issuance-response
receive...
Presentation
Presentation
yes 
yes 
Require
information?
Require...
issuance-offer
issuance-offer
receive
issuance-acknowledgement
receive...
Signing
Signing
Sign verifiable credential
Sign verifiable cred...
yes
yes
no
no
Delegate
signing?
Delegate...
no
no
yes
yes
Valid?
Valid?
problem-report
proble...
expiry
passed
expiry...
problem-report
proble...
yes
yes
no
no
Valid?
Valid?
problem-report
proble...
yes
yes
no
no
Valid?
Valid?
problem-report
proble...
Holder
Holder
issuance-request
issuance-request
Holder initiates
Holder initiates
receive
issuance-offer
receive...
Preview, validate claims
Preview, validate cl...
receive
issuance
receive...
issuance-response
issuance-response
yes
yes
Valid?
Valid?
problem-report
proble...
no
no
issuance-acknowledgement
issuance-acknowledge...
Store verifiable credential
Store verifiable cre...
Viewer does not support full SVG 1.1
\ No newline at end of file +
Issuer
Issuer
receive
issuance-request
receive...
Valid?
Valid?
Presentation
Presentation
receive
issuance-response
receive...
Valid?
Valid?
Accepted?
Accepted?
Signing
Signing
Sign verifiable
credential
Sign verifiable...
issuance
issuance
problem-report
proble...
receive
issuance-
aknowledgement
receive...
Valid?
Valid?
issuance-offer
issuance-offer
problem-report
proble...
yes
yes
yes
yes
no
no
no
no
yes
yes
problem-report
proble...
no
no
Delegate
signing?
Delegate...
yes
yes
no
no
yes
yes
no
no
expiry
passed
expiry...
problem-report
proble...
no
no
yes
yes
Alt: Issuer initiates
Alt: Issuer initiates
Require
information?
Require...
Holder
Holder
issuance-request
issuance-request
issuance-response
issuance-response
Preview, validate
claims
Preview, validate...
receive
issuance-offer
receive...
receive
issuance
receive...
issuance-
aknowledgement
issuance-...
Store verifiable
credential
Store verifiable...
Valid?
Valid?
problem-report
proble...
yes
yes
no
no
Holder initiates
Holder initiates
Text is not SVG - cannot display
\ No newline at end of file diff --git a/documentation/static/img/didcomm/post.drawio.svg b/documentation/static/img/didcomm/post.drawio.svg index 4995c919d7..1b9b35aaf6 100644 --- a/documentation/static/img/didcomm/post.drawio.svg +++ b/documentation/static/img/didcomm/post.drawio.svg @@ -1,4 +1,4 @@ -
Receiver
Receiver
receive post
receive post
yes 
yes 
Valid?
Valid?
no
no
problem-report
proble...
Sender
Sender
post
post
Sender initiates
Sender initiates
Viewer does not support full SVG 1.1
\ No newline at end of file +
Receiver
Receiver
receive post
receive post
problem-report
proble...
Valid?
Valid?
yes
yes
no
no
Sender
Sender
Sender initiates
Sender initiates
post
post
Text is not SVG - cannot display
\ No newline at end of file diff --git a/documentation/static/img/didcomm/presentation.drawio.svg b/documentation/static/img/didcomm/presentation.drawio.svg index 76ef9cab8d..1ca631ab98 100644 --- a/documentation/static/img/didcomm/presentation.drawio.svg +++ b/documentation/static/img/didcomm/presentation.drawio.svg @@ -1,4 +1,4 @@ -
Verifier
Verifier
receive
presentation-offer
receive...
presentation-request
presentation-request
Alt: Verifier initiates
Alt: Verifier initiates
Verify presentation and credentials.
Verify presentation...
presentation-result
presentation-result
no
no
yes
yes
Valid?
Valid?
receive presentation
receive presentation
problem-report
proble...
yes
yes
Continue?
Continue?
problem-report
proble...
no
no
Holder
Holder
presentation-offer
presentation-offer
Holder initiates
Holder initiates
yes
yes
no
no
Continue?
Continue?
receive
presentation-request
receive...
Select credentials, create verifiable presentation.
Select credentials,...
receive
presentation-result
receive...
presentation
presentation
problem-report
proble...
yes
yes
no
no
Valid?
Valid?
problem-report
proble...
Viewer does not support full SVG 1.1
\ No newline at end of file +
Verifier
Verifier
receive
presentation-offer
receive...
presentation-request
presentation-request
Continue?
Continue?
problem-report
proble...
receive
presentation
receive...
Verify presentation
and credentials
Verify presentation...
Valid?
Valid?
presentation-result
presentation-result
yes
yes
yes
yes
no
no
Alt: Verifier initiates
Alt: Verifier initiates
problem-report
proble...
no
no
Holder
Holder
presentation-offer
presentation-offer
receive
presentation-request
receive...
Continue?
Continue?
problem-report
proble...
Select credentials,
create verifiable
presentation
Select credentials,...
presentation
presentation
receive
presentation-result
receive...
Holder initiates
Holder initiates
Valid?
Valid?
yes
yes
yes
yes
no
no
problem-report
proble...
no
no
Text is not SVG - cannot display
\ No newline at end of file diff --git a/documentation/static/img/didcomm/revocation-options.drawio.svg b/documentation/static/img/didcomm/revocation-options.drawio.svg index 7bf07e4e72..2beee52da5 100644 --- a/documentation/static/img/didcomm/revocation-options.drawio.svg +++ b/documentation/static/img/didcomm/revocation-options.drawio.svg @@ -1,4 +1,4 @@ -
Revoker
Revoker
receive
revocation-options-request
receive...
revocation-options
revocation-options
no
no
yes
yes
Valid?
Valid?
problem-report
problem...
Trusted-Party
Trusted-Party
revocation-options-request
revocation-options-request
Trusted-Party initiates
Trusted-Party initiates
receive
revocation-options
receive...
Viewer does not support full SVG 1.1
\ No newline at end of file +
Trusted-Party
Trusted-Party
revocation-options-request
revocation-options-request
receive
revocation-options
receive...
Trusted-Party initiates
Trusted-Party initiates
Revoker
Revoker
receive
revocation-options-request
receive...
revocation-options
revocation-options
Valid?
Valid?
problem-report
proble...
no
no
yes
yes
Text is not SVG - cannot display
\ No newline at end of file diff --git a/documentation/static/img/didcomm/revocation.drawio.svg b/documentation/static/img/didcomm/revocation.drawio.svg index 691edeeb25..ea1c4af77d 100644 --- a/documentation/static/img/didcomm/revocation.drawio.svg +++ b/documentation/static/img/didcomm/revocation.drawio.svg @@ -1,4 +1,4 @@ -
Revoker
Revoker
receive
revocation-request
receive...
yes
yes
revocation-response
revocation-response
no
no
yes
yes
yes
yes
Valid?
Valid?
problem-report
proble...
no
no
Delegate?
Delegate?
Revocation
Revocation
Revoke key or credential
Revoke key or creden...
yes
yes
no
no
Require
information?
Require...
Presentation
Presentation
Trusted-Party
Trusted-Party
revocation-request
revocation-request
Trusted-Party initiates
Trusted-Party initiates
receive
revocation-response
receive...
Viewer does not support full SVG 1.1
\ No newline at end of file +
Trusted-Party
Trusted-Party
revocation-request
revocation-request
receive
revocation-response
receive...
Trusted-Party initiates
Trusted-Party initiates
Revoker
Revoker
receive
revocation-request
receive...
revocation-response
revocation-response
Valid?
Valid?
problem-report
proble...
Presentation
Presentation
Delegate
Delegate
Revocation
Revocation
Revoke key or
credential
Revoke key or...
no
no
yes
yes
yes
yes
no
no
no
no
yes
yes
Require
information?
Require...
Text is not SVG - cannot display
\ No newline at end of file diff --git a/documentation/static/img/didcomm/signing.drawio.svg b/documentation/static/img/didcomm/signing.drawio.svg index 51f653bb26..1bf599f4c5 100644 --- a/documentation/static/img/didcomm/signing.drawio.svg +++ b/documentation/static/img/didcomm/signing.drawio.svg @@ -1,3 +1,4 @@ + -
Issuer
Issuer
receive
signing-request
receive...
signing-response
signing-response
no
no
receive signing-acknowledgement
receive signing-ackn...
problem-report
problem...
yes
yes
Valid?
Valid?
Sign credential
Sign credential
Trusted-Party
Trusted-Party
signing-request
signing-request
Trusted-Party initiates
Trusted-Party initiates
yes
yes
receive
signing-response
receive...
yes
yes
signing-acknowledgement
signing-acknowledgem...
problem-report
problem...
no
no
Valid?
Valid?
Viewer does not support full SVG 1.1
\ No newline at end of file +
Issuer
Issuer
signing-response
signing-response
receive
signing-request
receive...
receive
signing-acknowledgement
receive...
Valid?
Valid?
Sign credential
Sign credential
problem-report
proble...
yes
yes
no
no
Trusted-Party
Trusted-Party
signing-request
signing-request
signing-
acknowledgement
signing-...
receive
signing-response
receive...
problem-report
proble...
Valid?
Valid?
Trusted-Party initiates
Trusted-Party initiates
no
no
yes
yes
Text is not SVG - cannot display
\ No newline at end of file diff --git a/documentation/static/img/didcomm/termination.drawio.svg b/documentation/static/img/didcomm/termination.drawio.svg index 62774860eb..7ca49efc51 100644 --- a/documentation/static/img/didcomm/termination.drawio.svg +++ b/documentation/static/img/didcomm/termination.drawio.svg @@ -1,4 +1,4 @@ -
Receiver
Receiver
receive
termination
receive...
termination-response
termination-response
yes
yes
please-ack?
please-ack?
Sender
Sender
termination
termination
Sender initiates
Sender initiates
receive
termination-response
receive...
yes
yes
no
no
please-ack?
please-ack?
no
no
Viewer does not support full SVG 1.1
\ No newline at end of file +
Receiver
Receiver
receive
termination
receive...
termination-response
termination-response
yes
yes
please-ack?
please-ac...
Sender
Sender
receive
termination-response
receive...
termination
termination
no
no
Sender initiates
Sender initiates
yes
yes
please-ack?
please-ac...
no
no
Text is not SVG - cannot display
\ No newline at end of file From 9443075df696f9d100ca0585a2279a3e5055a435 Mon Sep 17 00:00:00 2001 From: cycraig Date: Tue, 2 Aug 2022 10:40:36 +0200 Subject: [PATCH 33/89] Add initial Wasm Stardust bindings (#967) * Add missing DID trait methods to Wasm * Add unit test * Add WasmStardustDID, WasmStardustDIDUrl * Add WasmStardustDocument, WasmStardustDocumentMetadata, WasmStateMetadataEncoding * Add WasmStardustService * Add WasmStardustVerificationMethod * Remove parking_lot dependency, feature * Remove unnecessary mutable borrows, change Wasm*DID.join to clone internally * Add setMetadataPropertyUnchecked * Enable instant "wasm-bindgen" feature, add lint * Add Stardust unit tests --- bindings/wasm/Cargo.toml | 12 +- bindings/wasm/build/lints.js | 34 + bindings/wasm/docs/api-reference.md | 1480 ++++++++++++++--- .../wasm/src/account/wasm_account/account.rs | 7 +- bindings/wasm/src/account/wasm_account/mod.rs | 1 - bindings/wasm/src/common/timestamp.rs | 6 + bindings/wasm/src/common/types.rs | 6 + bindings/wasm/src/did/mod.rs | 2 +- bindings/wasm/src/did/wasm_did.rs | 84 +- bindings/wasm/src/did/wasm_did_url.rs | 8 +- bindings/wasm/src/did/wasm_document.rs | 76 +- .../wasm/src/did/wasm_document_metadata.rs | 11 +- bindings/wasm/src/did/wasm_service.rs | 97 +- .../wasm/src/did/wasm_verification_method.rs | 19 +- bindings/wasm/src/error.rs | 3 +- bindings/wasm/src/lib.rs | 5 +- bindings/wasm/src/revocation/bitmap.rs | 3 +- bindings/wasm/src/stardust/mod.rs | 18 + bindings/wasm/src/stardust/stardust_did.rs | 161 ++ .../wasm/src/stardust/stardust_did_url.rs | 106 ++ .../wasm/src/stardust/stardust_document.rs | 510 ++++++ .../stardust/stardust_document_metadata.rs | 45 + .../wasm/src/stardust/stardust_service.rs | 106 ++ .../stardust/stardust_verification_method.rs | 74 + .../src/stardust/state_metadata_encoding.rs | 30 + bindings/wasm/tests/credentials.ts | 1 - bindings/wasm/tests/stardust.ts | 189 +++ bindings/wasm/tests/wasm.rs | 24 +- identity_account_storage/Cargo.toml | 1 - identity_did/src/document/traits.rs | 2 +- .../src/verification/verification_method.rs | 16 +- .../src/document/stardust_document.rs | 9 +- 32 files changed, 2817 insertions(+), 329 deletions(-) create mode 100644 bindings/wasm/src/stardust/mod.rs create mode 100644 bindings/wasm/src/stardust/stardust_did.rs create mode 100644 bindings/wasm/src/stardust/stardust_did_url.rs create mode 100644 bindings/wasm/src/stardust/stardust_document.rs create mode 100644 bindings/wasm/src/stardust/stardust_document_metadata.rs create mode 100644 bindings/wasm/src/stardust/stardust_service.rs create mode 100644 bindings/wasm/src/stardust/stardust_verification_method.rs create mode 100644 bindings/wasm/src/stardust/state_metadata_encoding.rs create mode 100644 bindings/wasm/tests/stardust.ts diff --git a/bindings/wasm/Cargo.toml b/bindings/wasm/Cargo.toml index d43de28c08..1836dddda9 100644 --- a/bindings/wasm/Cargo.toml +++ b/bindings/wasm/Cargo.toml @@ -24,7 +24,7 @@ proc_typescript = { version = "0.1.0", path = "./proc_typescript" } serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", default-features = false } serde_repr = { version = "0.1", default-features = false } -wasm-bindgen = { version = "0.2", features = ["serde-serialize"] } +wasm-bindgen = { version = "0.2.82", features = ["serde-serialize"] } wasm-bindgen-futures = { version = "0.4", default-features = false } [dependencies.identity_iota] @@ -33,12 +33,18 @@ path = "../../identity_iota" default-features = false features = ["account", "storage-test-suite", "unstable-encryption", "revocation-bitmap"] +[dependencies.identity_stardust] +version = "=0.6.0" +path = "../../identity_stardust" +default-features = false +features = ["revocation-bitmap"] + [dev-dependencies] wasm-bindgen-test = { version = "0.3" } [target.'cfg(all(target_arch = "wasm32", not(target_os = "wasi")))'.dependencies] -getrandom = { version = "0.2", features = ["js"] } -parking_lot = { version = "0.11.2", features = ["wasm-bindgen"] } +getrandom = { version = "0.2", default-features = false, features = ["js"] } +instant = { version = "0.1", default-features = false, features = ["wasm-bindgen"] } [package.metadata.wasm-pack.profile.release] wasm-opt = true diff --git a/bindings/wasm/build/lints.js b/bindings/wasm/build/lints.js index 9402aff3d3..a98e5aafec 100644 --- a/bindings/wasm/build/lints.js +++ b/bindings/wasm/build/lints.js @@ -42,11 +42,45 @@ See: https://github.com/rustwasm/wasm-bindgen/pull/2677`); } } +/** Rejects any `imports['env']` occurrences, which cause failures at runtime. + * + * This is typically due to Wasm compatibility features not being enabled on crate dependencies. **/ +function lintImportEnv(content) { + if (content.includes("imports['env']") || content.includes("require('env')") || content.includes("from 'env'")) { + throw(`ERROR: generated Javascript should not include any imports for 'env', e.g.: + +- imports['env'] = require('env'); +- imports['env'] = __wbg_star0; +- import * as __wbg_star0 from 'env'; + +It causes runtime errors such as "Module not found: Error: Can't resolve 'env'". +This usually indicates a dependency trying to use or import non-Wasm-compatible code or libraries. + +Common problematic crates and the feature flag required to be compatible: +- parking_lot <= 0.11.2, "wasm-bindgen" (0.12.0 deprecated the "wasm-bindgen" feature). +- instant 0.1, "wasm-bindgen". +- getrandom 0.2, "js". + +SUGGESTION: Identify the problematic crate by comparing recent changes to Cargo.toml in this project and any +dependencies. Then, enable the relevant "js" or "wasm-bindgen" feature flag in Cargo.toml for that specific +dependency and version. + +E.g. (only add this to Cargo.toml if they appear in Cargo.lock, and the version must match Cargo.lock). +getrandom = { version = "0.2", default-features = false, features = ["js"] } +instant = { version = "0.1", default-features = false, features = ["wasm-bindgen"] } + +See: +- https://github.com/rustwasm/wasm-bindgen/issues/2160 +- https://github.com/rustwasm/wasm-pack/issues/743`); + } +} + /** Runs all custom lints on the generated code. Exits the process immediately with code 1 if any fail. **/ function lintAll(content) { try { lintBigInt(content); lintPtrNullWithoutFree(content); + lintImportEnv(content); } catch (err) { console.error("Custom lint failed!"); console.error(err); diff --git a/bindings/wasm/docs/api-reference.md b/bindings/wasm/docs/api-reference.md index b7c149eadb..eb8926723e 100644 --- a/bindings/wasm/docs/api-reference.md +++ b/bindings/wasm/docs/api-reference.md @@ -34,9 +34,11 @@ the configuration of previously built accounts.

CredentialValidator
DID
-
+

A DID conforming to the IOTA DID method specification.

+
DIDUrl
-
+

A DID URL conforming to the IOTA DID method specification.

+
DiffChainHistory
DiffMessage
@@ -130,6 +132,22 @@ with a DID subject.

Signature

A digital signature.

+
StardustDID
+

A DID conforming to the IOTA UTXO DID method specification.

+
+
StardustDIDUrl
+

A DID URL conforming to the IOTA Stardust UTXO DID method specification.

+
+
StardustDocument
+
+
StardustDocumentMetadata
+

Additional attributes related to an IOTA DID Document.

+
+
StardustService
+

A Service adhering to the IOTA UTXO DID method specification.

+
+
StardustVerificationMethod
+
StorageTestSuite

A test suite for the Storage interface.

This module contains a set of tests that a correct storage implementation @@ -156,6 +174,8 @@ See IVerifierOptions.

DIDMessageEncoding
+
StateMetadataEncoding
+
StatusCheck

Controls validation behaviour when checking whether or not a credential has been revoked by its credentialStatus.

@@ -198,10 +218,10 @@ This variant is the default used if no other variant is specified when construct
FirstError

Return after the first error occurs.

-
MethodRelationship
-
KeyType
+
MethodRelationship
+
## Functions @@ -224,8 +244,8 @@ publishing to the Tangle. * [Account](#Account) * [.createService(options)](#Account+createService) ⇒ Promise.<void> - * [.attachMethodRelationships(options)](#Account+attachMethodRelationships) ⇒ Promise.<void> * [.createMethod(options)](#Account+createMethod) ⇒ Promise.<void> + * [.attachMethodRelationships(options)](#Account+attachMethodRelationships) ⇒ Promise.<void> * [.detachMethodRelationships(options)](#Account+detachMethodRelationships) ⇒ Promise.<void> * [.did()](#Account+did) ⇒ [DID](#DID) * [.autopublish()](#Account+autopublish) ⇒ boolean @@ -244,9 +264,9 @@ publishing to the Tangle. * [.unrevokeCredentials(fragment, indices)](#Account+unrevokeCredentials) ⇒ Promise.<void> * [.encryptData(plaintext, associated_data, encryption_algorithm, cek_algorithm, public_key)](#Account+encryptData) ⇒ [Promise.<EncryptedData>](#EncryptedData) * [.decryptData(data, encryption_algorithm, cek_algorithm, fragment)](#Account+decryptData) ⇒ Promise.<Uint8Array> + * [.setAlsoKnownAs(options)](#Account+setAlsoKnownAs) ⇒ Promise.<void> * [.deleteMethod(options)](#Account+deleteMethod) ⇒ Promise.<void> * [.deleteService(options)](#Account+deleteService) ⇒ Promise.<void> - * [.setAlsoKnownAs(options)](#Account+setAlsoKnownAs) ⇒ Promise.<void> * [.setController(options)](#Account+setController) ⇒ Promise.<void> @@ -260,30 +280,30 @@ Adds a new Service to the DID Document. | --- | --- | | options | CreateServiceOptions | - - -### account.attachMethodRelationships(options) ⇒ Promise.<void> -Attach one or more verification relationships to a method. + -Note: the method must exist and be in the set of verification methods; -it cannot be an embedded method. +### account.createMethod(options) ⇒ Promise.<void> +Adds a new verification method to the DID document. **Kind**: instance method of [Account](#Account) | Param | Type | | --- | --- | -| options | AttachMethodRelationshipOptions | +| options | CreateMethodOptions | - + -### account.createMethod(options) ⇒ Promise.<void> -Adds a new verification method to the DID document. +### account.attachMethodRelationships(options) ⇒ Promise.<void> +Attach one or more verification relationships to a method. + +Note: the method must exist and be in the set of verification methods; +it cannot be an embedded method. **Kind**: instance method of [Account](#Account) | Param | Type | | --- | --- | -| options | CreateMethodOptions | +| options | AttachMethodRelationshipOptions | @@ -487,6 +507,17 @@ Returns the decrypted text. | cek_algorithm | [CekAlgorithm](#CekAlgorithm) | | fragment | string | + + +### account.setAlsoKnownAs(options) ⇒ Promise.<void> +Sets the `alsoKnownAs` property in the DID document. + +**Kind**: instance method of [Account](#Account) + +| Param | Type | +| --- | --- | +| options | SetAlsoKnownAsOptions | + ### account.deleteMethod(options) ⇒ Promise.<void> @@ -509,17 +540,6 @@ Deletes a Service if it exists. | --- | --- | | options | DeleteServiceOptions | - - -### account.setAlsoKnownAs(options) ⇒ Promise.<void> -Sets the `alsoKnownAs` property in the DID document. - -**Kind**: instance method of [Account](#Account) - -| Param | Type | -| --- | --- | -| options | SetAlsoKnownAsOptions | - ### account.setController(options) ⇒ Promise.<void> @@ -1327,14 +1347,20 @@ Fails if the issuer field is not a valid DID. ## DID +A DID conforming to the IOTA DID method specification. + **Kind**: global class * [DID](#DID) * [new DID(public_key, network)](#new_DID_new) * _instance_ - * [.networkName](#DID+networkName) ⇒ string * [.network()](#DID+network) ⇒ [Network](#Network) + * [.networkStr()](#DID+networkStr) ⇒ string * [.tag()](#DID+tag) ⇒ string + * [.scheme()](#DID+scheme) ⇒ string + * [.authority()](#DID+authority) ⇒ string + * [.method()](#DID+method) ⇒ string + * [.methodId()](#DID+methodId) ⇒ string * [.join(segment)](#DID+join) ⇒ [DIDUrl](#DIDUrl) * [.toUrl()](#DID+toUrl) ⇒ [DIDUrl](#DIDUrl) * [.intoUrl()](#DID+intoUrl) ⇒ [DIDUrl](#DIDUrl) @@ -1342,6 +1368,8 @@ Fails if the issuer field is not a valid DID. * [.toJSON()](#DID+toJSON) ⇒ any * [.clone()](#DID+clone) ⇒ [DID](#DID) * _static_ + * [.METHOD](#DID.METHOD) ⇒ string + * [.DEFAULT_NETWORK](#DID.DEFAULT_NETWORK) ⇒ string * [.parse(input)](#DID.parse) ⇒ [DID](#DID) * [.fromJSON(json)](#DID.fromJSON) ⇒ [DID](#DID) @@ -1356,16 +1384,16 @@ Creates a new `DID` from a public key. | public_key | Uint8Array | | network | string \| undefined | - - -### did.networkName ⇒ string -Returns the IOTA tangle network of the `DID`. - -**Kind**: instance property of [DID](#DID) ### did.network() ⇒ [Network](#Network) -Returns the IOTA tangle network of the `DID`. +Returns the Tangle network of the `DID`. + +**Kind**: instance method of [DID](#DID) + + +### did.networkStr() ⇒ string +Returns the Tangle network name of the `DID`. **Kind**: instance method of [DID](#DID) @@ -1373,6 +1401,46 @@ Returns the IOTA tangle network of the `DID`. ### did.tag() ⇒ string Returns a copy of the unique tag of the `DID`. +**Kind**: instance method of [DID](#DID) + + +### did.scheme() ⇒ string +Returns the `DID` scheme. + +E.g. +- `"did:example:12345678" -> "did"` +- `"did:iota:main:12345678" -> "did"` + +**Kind**: instance method of [DID](#DID) + + +### did.authority() ⇒ string +Returns the `DID` authority: the method name and method-id. + +E.g. +- `"did:example:12345678" -> "example:12345678"` +- `"did:iota:main:12345678" -> "iota:main:12345678"` + +**Kind**: instance method of [DID](#DID) + + +### did.method() ⇒ string +Returns the `DID` method name. + +E.g. +- `"did:example:12345678" -> "example"` +- `"did:iota:main:12345678" -> "iota"` + +**Kind**: instance method of [DID](#DID) + + +### did.methodId() ⇒ string +Returns the `DID` method-specific ID. + +E.g. +- `"did:example:12345678" -> "12345678"` +- `"did:iota:main:12345678" -> "main:12345678"` + **Kind**: instance method of [DID](#DID) @@ -1394,7 +1462,7 @@ Clones the `DID` into a `DIDUrl`. ### did.intoUrl() ⇒ [DIDUrl](#DIDUrl) -Converts the `DID` into a `DIDUrl`. +Converts the `DID` into a `DIDUrl`, consuming it. **Kind**: instance method of [DID](#DID) @@ -1415,6 +1483,18 @@ Serializes this to a JSON object. Deep clones the object. **Kind**: instance method of [DID](#DID) + + +### DID.METHOD ⇒ string +The IOTA DID method name (`"iota"`). + +**Kind**: static property of [DID](#DID) + + +### DID.DEFAULT\_NETWORK ⇒ string +The default Tangle network (`"main"`). + +**Kind**: static property of [DID](#DID) ### DID.parse(input) ⇒ [DID](#DID) @@ -1440,6 +1520,8 @@ Deserializes an instance from a JSON object. ## DIDUrl +A DID URL conforming to the IOTA DID method specification. + **Kind**: global class * [DIDUrl](#DIDUrl) @@ -1793,8 +1875,8 @@ Deserializes an instance from a JSON object. * [.defaultSigningMethod()](#Document+defaultSigningMethod) ⇒ [VerificationMethod](#VerificationMethod) * [.resolveMethod(query, scope)](#Document+resolveMethod) ⇒ [VerificationMethod](#VerificationMethod) \| undefined * [.resolveSigningMethod(query)](#Document+resolveSigningMethod) ⇒ [VerificationMethod](#VerificationMethod) - * [.attachMethodRelationship(did_url, relationship)](#Document+attachMethodRelationship) ⇒ boolean - * [.detachMethodRelationship(did_url, relationship)](#Document+detachMethodRelationship) ⇒ boolean + * [.attachMethodRelationship(didUrl, relationship)](#Document+attachMethodRelationship) ⇒ boolean + * [.detachMethodRelationship(didUrl, relationship)](#Document+detachMethodRelationship) ⇒ boolean * [.signSelf(key_pair, method_query)](#Document+signSelf) * [.signDocument(document, key_pair, method_query)](#Document+signDocument) * [.signCredential(credential, privateKey, methodQuery, options)](#Document+signCredential) ⇒ [Credential](#Credential) @@ -1813,6 +1895,7 @@ Deserializes an instance from a JSON object. * [.setMetadataUpdated(timestamp)](#Document+setMetadataUpdated) * [.metadataPreviousMessageId()](#Document+metadataPreviousMessageId) ⇒ string * [.setMetadataPreviousMessageId(value)](#Document+setMetadataPreviousMessageId) + * [.setMetadataPropertyUnchecked(key, value)](#Document+setMetadataPropertyUnchecked) * [.proof()](#Document+proof) ⇒ [Proof](#Proof) \| undefined * [.revokeCredentials(serviceQuery, indices)](#Document+revokeCredentials) * [.unrevokeCredentials(serviceQuery, indices)](#Document+unrevokeCredentials) @@ -1874,7 +1957,7 @@ Use `null` to remove all controllers. ### document.controller() ⇒ [Array.<DID>](#DID) -Returns a list of document controllers. +Returns a copy of the list of document controllers. **Kind**: instance method of [Document](#Document) @@ -1891,7 +1974,7 @@ Sets the `alsoKnownAs` property in the DID document. ### document.alsoKnownAs() ⇒ Array.<string> -Returns a set of the document's `alsoKnownAs`. +Returns a copy of the document's `alsoKnownAs` set. **Kind**: instance method of [Document](#Document) @@ -1969,7 +2052,7 @@ Returns a list of all [VerificationMethod](#VerificationMethod) in the DID Docum ### document.insertMethod(method, scope) -Adds a new Verification Method to the DID Document. +Adds a new `method` to the document in the given `scope`. **Kind**: instance method of [Document](#Document) @@ -2001,10 +2084,9 @@ Throws an error if no signing method is present. ### document.resolveMethod(query, scope) ⇒ [VerificationMethod](#VerificationMethod) \| undefined -Returns a copy of the first `VerificationMethod` with an `id` property -matching the provided `query`. - -Throws an error if the method is not found. +Returns a copy of the first verification method with an `id` property +matching the provided `query` and the verification relationship +specified by `scope`, if present. **Kind**: instance method of [Document](#Document) @@ -2026,7 +2108,7 @@ Attempts to resolve the given method query into a method capable of signing a do -### document.attachMethodRelationship(did_url, relationship) ⇒ boolean +### document.attachMethodRelationship(didUrl, relationship) ⇒ boolean Attaches the relationship to the given method, if the method exists. Note: The method needs to be in the set of verification methods, @@ -2036,19 +2118,19 @@ so it cannot be an embedded one. | Param | Type | | --- | --- | -| did_url | [DIDUrl](#DIDUrl) | +| didUrl | [DIDUrl](#DIDUrl) | | relationship | number | -### document.detachMethodRelationship(did_url, relationship) ⇒ boolean +### document.detachMethodRelationship(didUrl, relationship) ⇒ boolean Detaches the given relationship from the given method, if the method exists. **Kind**: instance method of [Document](#Document) | Param | Type | | --- | --- | -| did_url | [DIDUrl](#DIDUrl) | +| didUrl | [DIDUrl](#DIDUrl) | | relationship | number | @@ -2288,6 +2370,19 @@ Sets the previous integration chain message id. | --- | --- | | value | string | + + +### document.setMetadataPropertyUnchecked(key, value) +Sets a custom property in the document metadata. +If the value is set to `null`, the custom property will be removed. + +**Kind**: instance method of [Document](#Document) + +| Param | Type | +| --- | --- | +| key | string | +| value | any | + ### document.proof() ⇒ [Proof](#Proof) \| undefined @@ -2486,18 +2581,15 @@ Additional attributes related to an IOTA DID Document. * [DocumentMetadata](#DocumentMetadata) * _instance_ - * [.previousMessageId](#DocumentMetadata+previousMessageId) ⇒ string * [.created()](#DocumentMetadata+created) ⇒ [Timestamp](#Timestamp) \| undefined * [.updated()](#DocumentMetadata+updated) ⇒ [Timestamp](#Timestamp) \| undefined + * [.previousMessageId()](#DocumentMetadata+previousMessageId) ⇒ string + * [.properties()](#DocumentMetadata+properties) ⇒ Map.<string, any> * [.toJSON()](#DocumentMetadata+toJSON) ⇒ any * [.clone()](#DocumentMetadata+clone) ⇒ [DocumentMetadata](#DocumentMetadata) * _static_ * [.fromJSON(json)](#DocumentMetadata.fromJSON) ⇒ [DocumentMetadata](#DocumentMetadata) - - -### documentMetadata.previousMessageId ⇒ string -**Kind**: instance property of [DocumentMetadata](#DocumentMetadata) ### documentMetadata.created() ⇒ [Timestamp](#Timestamp) \| undefined @@ -2509,6 +2601,18 @@ Returns a copy of the timestamp of when the DID document was created. ### documentMetadata.updated() ⇒ [Timestamp](#Timestamp) \| undefined Returns a copy of the timestamp of the last DID document update. +**Kind**: instance method of [DocumentMetadata](#DocumentMetadata) + + +### documentMetadata.previousMessageId() ⇒ string +Returns a copy of the previous message identifier. + +**Kind**: instance method of [DocumentMetadata](#DocumentMetadata) + + +### documentMetadata.properties() ⇒ Map.<string, any> +Returns a copy of the custom metadata properties. + **Kind**: instance method of [DocumentMetadata](#DocumentMetadata) @@ -4481,7 +4585,7 @@ See: https://www.w3.org/TR/did-core/#services | Param | Type | | --- | --- | -| service | IService | +| service | IIotaService | @@ -4578,206 +4682,1190 @@ Deserializes an instance from a JSON object. | --- | --- | | json | any | - + -## StorageTestSuite -A test suite for the `Storage` interface. +## StardustDID +A DID conforming to the IOTA UTXO DID method specification. -This module contains a set of tests that a correct storage implementation -should pass. Note that not every edge case is tested. +**Kind**: global class -Tests usually rely on multiple interface methods being implemented, so they should only -be run on a fully implemented version. That's why there is not a single test case for every -interface method. +* [StardustDID](#StardustDID) + * [new StardustDID(bytes, network)](#new_StardustDID_new) + * _instance_ + * [.networkStr()](#StardustDID+networkStr) ⇒ string + * [.tag()](#StardustDID+tag) ⇒ string + * [.scheme()](#StardustDID+scheme) ⇒ string + * [.authority()](#StardustDID+authority) ⇒ string + * [.method()](#StardustDID+method) ⇒ string + * [.methodId()](#StardustDID+methodId) ⇒ string + * [.join(segment)](#StardustDID+join) ⇒ [StardustDIDUrl](#StardustDIDUrl) + * [.toUrl()](#StardustDID+toUrl) ⇒ [StardustDIDUrl](#StardustDIDUrl) + * [.intoUrl()](#StardustDID+intoUrl) ⇒ [StardustDIDUrl](#StardustDIDUrl) + * [.toString()](#StardustDID+toString) ⇒ string + * [.toJSON()](#StardustDID+toJSON) ⇒ any + * [.clone()](#StardustDID+clone) ⇒ [StardustDID](#StardustDID) + * _static_ + * [.METHOD](#StardustDID.METHOD) ⇒ string + * [.DEFAULT_NETWORK](#StardustDID.DEFAULT_NETWORK) ⇒ string + * [.placeholder(network)](#StardustDID.placeholder) ⇒ [StardustDID](#StardustDID) + * [.parse(input)](#StardustDID.parse) ⇒ [StardustDID](#StardustDID) + * [.fromJSON(json)](#StardustDID.fromJSON) ⇒ [StardustDID](#StardustDID) -**Kind**: global class + -* [StorageTestSuite](#StorageTestSuite) - * [.didCreateGenerateKeyTest(storage)](#StorageTestSuite.didCreateGenerateKeyTest) ⇒ Promise.<void> - * [.didCreatePrivateKeyTest(storage)](#StorageTestSuite.didCreatePrivateKeyTest) ⇒ Promise.<void> - * [.didListTest(storage)](#StorageTestSuite.didListTest) ⇒ Promise.<void> - * [.didPurgeTest(storage)](#StorageTestSuite.didPurgeTest) ⇒ Promise.<void> - * [.keyGenerateTest(storage)](#StorageTestSuite.keyGenerateTest) ⇒ Promise.<void> - * [.keyDeleteTest(storage)](#StorageTestSuite.keyDeleteTest) ⇒ Promise.<void> - * [.keyInsertTest(storage)](#StorageTestSuite.keyInsertTest) ⇒ Promise.<void> - * [.keySignEd25519Test(storage)](#StorageTestSuite.keySignEd25519Test) ⇒ Promise.<void> - * [.encryptionTest(alice_storage, bob_storage)](#StorageTestSuite.encryptionTest) ⇒ Promise.<void> +### new StardustDID(bytes, network) +Constructs a new `StardustDID` from a byte representation of the tag and the given +network name. - +See also [placeholder](#StardustDID.placeholder). -### StorageTestSuite.didCreateGenerateKeyTest(storage) ⇒ Promise.<void> -**Kind**: static method of [StorageTestSuite](#StorageTestSuite) | Param | Type | | --- | --- | -| storage | Storage | - - +| bytes | Uint8Array | +| network | string | -### StorageTestSuite.didCreatePrivateKeyTest(storage) ⇒ Promise.<void> -**Kind**: static method of [StorageTestSuite](#StorageTestSuite) + -| Param | Type | -| --- | --- | -| storage | Storage | +### did.networkStr() ⇒ string +Returns the Tangle network name of the `StardustDID`. - +**Kind**: instance method of [StardustDID](#StardustDID) + -### StorageTestSuite.didListTest(storage) ⇒ Promise.<void> -**Kind**: static method of [StorageTestSuite](#StorageTestSuite) +### did.tag() ⇒ string +Returns a copy of the unique tag of the `StardustDID`. -| Param | Type | -| --- | --- | -| storage | Storage | +**Kind**: instance method of [StardustDID](#StardustDID) + - +### did.scheme() ⇒ string +Returns the `DID` scheme. -### StorageTestSuite.didPurgeTest(storage) ⇒ Promise.<void> -**Kind**: static method of [StorageTestSuite](#StorageTestSuite) +E.g. +- `"did:example:12345678" -> "did"` +- `"did:iota:main:12345678" -> "did"` -| Param | Type | -| --- | --- | -| storage | Storage | +**Kind**: instance method of [StardustDID](#StardustDID) + - +### did.authority() ⇒ string +Returns the `DID` authority: the method name and method-id. -### StorageTestSuite.keyGenerateTest(storage) ⇒ Promise.<void> -**Kind**: static method of [StorageTestSuite](#StorageTestSuite) +E.g. +- `"did:example:12345678" -> "example:12345678"` +- `"did:iota:main:12345678" -> "iota:main:12345678"` -| Param | Type | -| --- | --- | -| storage | Storage | +**Kind**: instance method of [StardustDID](#StardustDID) + - +### did.method() ⇒ string +Returns the `DID` method name. -### StorageTestSuite.keyDeleteTest(storage) ⇒ Promise.<void> -**Kind**: static method of [StorageTestSuite](#StorageTestSuite) +E.g. +- `"did:example:12345678" -> "example"` +- `"did:iota:main:12345678" -> "iota"` -| Param | Type | -| --- | --- | -| storage | Storage | +**Kind**: instance method of [StardustDID](#StardustDID) + - +### did.methodId() ⇒ string +Returns the `DID` method-specific ID. -### StorageTestSuite.keyInsertTest(storage) ⇒ Promise.<void> -**Kind**: static method of [StorageTestSuite](#StorageTestSuite) +E.g. +- `"did:example:12345678" -> "12345678"` +- `"did:iota:main:12345678" -> "main:12345678"` -| Param | Type | -| --- | --- | -| storage | Storage | +**Kind**: instance method of [StardustDID](#StardustDID) + - +### did.join(segment) ⇒ [StardustDIDUrl](#StardustDIDUrl) +Construct a new `DIDUrl` by joining with a relative DID Url string. -### StorageTestSuite.keySignEd25519Test(storage) ⇒ Promise.<void> -**Kind**: static method of [StorageTestSuite](#StorageTestSuite) +**Kind**: instance method of [StardustDID](#StardustDID) | Param | Type | | --- | --- | -| storage | Storage | +| segment | string | - + -### StorageTestSuite.encryptionTest(alice_storage, bob_storage) ⇒ Promise.<void> -**Kind**: static method of [StorageTestSuite](#StorageTestSuite) +### did.toUrl() ⇒ [StardustDIDUrl](#StardustDIDUrl) +Clones the `DID` into a `DIDUrl`. -| Param | Type | -| --- | --- | -| alice_storage | Storage | -| bob_storage | Storage | +**Kind**: instance method of [StardustDID](#StardustDID) + - +### did.intoUrl() ⇒ [StardustDIDUrl](#StardustDIDUrl) +Converts the `DID` into a `DIDUrl`, consuming it. -## Timestamp -**Kind**: global class +**Kind**: instance method of [StardustDID](#StardustDID) + -* [Timestamp](#Timestamp) - * _instance_ - * [.toRFC3339()](#Timestamp+toRFC3339) ⇒ string - * [.checkedAdd(duration)](#Timestamp+checkedAdd) ⇒ [Timestamp](#Timestamp) \| undefined - * [.checkedSub(duration)](#Timestamp+checkedSub) ⇒ [Timestamp](#Timestamp) \| undefined - * [.toJSON()](#Timestamp+toJSON) ⇒ any - * _static_ - * [.parse(input)](#Timestamp.parse) ⇒ [Timestamp](#Timestamp) - * [.nowUTC()](#Timestamp.nowUTC) ⇒ [Timestamp](#Timestamp) - * [.fromJSON(json)](#Timestamp.fromJSON) ⇒ [Timestamp](#Timestamp) +### did.toString() ⇒ string +Returns the `DID` as a string. - +**Kind**: instance method of [StardustDID](#StardustDID) + -### timestamp.toRFC3339() ⇒ string -Returns the `Timestamp` as an RFC 3339 `String`. +### did.toJSON() ⇒ any +Serializes this to a JSON object. -**Kind**: instance method of [Timestamp](#Timestamp) - +**Kind**: instance method of [StardustDID](#StardustDID) + -### timestamp.checkedAdd(duration) ⇒ [Timestamp](#Timestamp) \| undefined -Computes `self + duration` +### did.clone() ⇒ [StardustDID](#StardustDID) +Deep clones the object. -Returns `null` if the operation leads to a timestamp not in the valid range for [RFC 3339](https://tools.ietf.org/html/rfc3339). +**Kind**: instance method of [StardustDID](#StardustDID) + -**Kind**: instance method of [Timestamp](#Timestamp) +### StardustDID.METHOD ⇒ string +The IOTA UTXO DID method name (`"stardust"`). -| Param | Type | -| --- | --- | -| duration | [Duration](#Duration) | +**Kind**: static property of [StardustDID](#StardustDID) + - +### StardustDID.DEFAULT\_NETWORK ⇒ string +The default Tangle network (`"main"`). -### timestamp.checkedSub(duration) ⇒ [Timestamp](#Timestamp) \| undefined -Computes `self - duration` +**Kind**: static property of [StardustDID](#StardustDID) + -Returns `null` if the operation leads to a timestamp not in the valid range for [RFC 3339](https://tools.ietf.org/html/rfc3339). +### StardustDID.placeholder(network) ⇒ [StardustDID](#StardustDID) +Creates a new placeholder [`StardustDID`] with the given network name. -**Kind**: instance method of [Timestamp](#Timestamp) +E.g. `did:stardust:smr:0x0000000000000000000000000000000000000000000000000000000000000000`. + +**Kind**: static method of [StardustDID](#StardustDID) | Param | Type | | --- | --- | -| duration | [Duration](#Duration) | - - +| network | string | -### timestamp.toJSON() ⇒ any -Serializes this to a JSON object. - -**Kind**: instance method of [Timestamp](#Timestamp) - + -### Timestamp.parse(input) ⇒ [Timestamp](#Timestamp) -Parses a `Timestamp` from the provided input string. +### StardustDID.parse(input) ⇒ [StardustDID](#StardustDID) +Parses a `StardustDID` from the input string. -**Kind**: static method of [Timestamp](#Timestamp) +**Kind**: static method of [StardustDID](#StardustDID) | Param | Type | | --- | --- | | input | string | - - -### Timestamp.nowUTC() ⇒ [Timestamp](#Timestamp) -Creates a new `Timestamp` with the current date and time. - -**Kind**: static method of [Timestamp](#Timestamp) - + -### Timestamp.fromJSON(json) ⇒ [Timestamp](#Timestamp) +### StardustDID.fromJSON(json) ⇒ [StardustDID](#StardustDID) Deserializes an instance from a JSON object. -**Kind**: static method of [Timestamp](#Timestamp) +**Kind**: static method of [StardustDID](#StardustDID) | Param | Type | | --- | --- | | json | any | - + + +## StardustDIDUrl +A DID URL conforming to the IOTA Stardust UTXO DID method specification. -## VerificationMethod +**Kind**: global class + +* [StardustDIDUrl](#StardustDIDUrl) + * _instance_ + * [.did()](#StardustDIDUrl+did) ⇒ [StardustDID](#StardustDID) + * [.urlStr()](#StardustDIDUrl+urlStr) ⇒ string + * [.fragment()](#StardustDIDUrl+fragment) ⇒ string \| undefined + * [.setFragment(value)](#StardustDIDUrl+setFragment) + * [.path()](#StardustDIDUrl+path) ⇒ string \| undefined + * [.setPath(value)](#StardustDIDUrl+setPath) + * [.query()](#StardustDIDUrl+query) ⇒ string \| undefined + * [.setQuery(value)](#StardustDIDUrl+setQuery) + * [.join(segment)](#StardustDIDUrl+join) ⇒ [StardustDIDUrl](#StardustDIDUrl) + * [.toString()](#StardustDIDUrl+toString) ⇒ string + * [.toJSON()](#StardustDIDUrl+toJSON) ⇒ any + * [.clone()](#StardustDIDUrl+clone) ⇒ [StardustDIDUrl](#StardustDIDUrl) + * _static_ + * [.parse(input)](#StardustDIDUrl.parse) ⇒ [StardustDIDUrl](#StardustDIDUrl) + * [.fromJSON(json)](#StardustDIDUrl.fromJSON) ⇒ [StardustDIDUrl](#StardustDIDUrl) + + + +### stardustDIDUrl.did() ⇒ [StardustDID](#StardustDID) +Return a copy of the `StardustDID` section of the `StardustDIDUrl`. + +**Kind**: instance method of [StardustDIDUrl](#StardustDIDUrl) + + +### stardustDIDUrl.urlStr() ⇒ string +Return a copy of the relative DID Url as a string, including only the path, query, and fragment. + +**Kind**: instance method of [StardustDIDUrl](#StardustDIDUrl) + + +### stardustDIDUrl.fragment() ⇒ string \| undefined +Returns a copy of the `StardustDIDUrl` method fragment, if any. Excludes the leading '#'. + +**Kind**: instance method of [StardustDIDUrl](#StardustDIDUrl) + + +### stardustDIDUrl.setFragment(value) +Sets the `fragment` component of the `StardustDIDUrl`. + +**Kind**: instance method of [StardustDIDUrl](#StardustDIDUrl) + +| Param | Type | +| --- | --- | +| value | string \| undefined | + + + +### stardustDIDUrl.path() ⇒ string \| undefined +Returns a copy of the `StardustDIDUrl` path. + +**Kind**: instance method of [StardustDIDUrl](#StardustDIDUrl) + + +### stardustDIDUrl.setPath(value) +Sets the `path` component of the `StardustDIDUrl`. + +**Kind**: instance method of [StardustDIDUrl](#StardustDIDUrl) + +| Param | Type | +| --- | --- | +| value | string \| undefined | + + + +### stardustDIDUrl.query() ⇒ string \| undefined +Returns a copy of the `StardustDIDUrl` method query, if any. Excludes the leading '?'. + +**Kind**: instance method of [StardustDIDUrl](#StardustDIDUrl) + + +### stardustDIDUrl.setQuery(value) +Sets the `query` component of the `StardustDIDUrl`. + +**Kind**: instance method of [StardustDIDUrl](#StardustDIDUrl) + +| Param | Type | +| --- | --- | +| value | string \| undefined | + + + +### stardustDIDUrl.join(segment) ⇒ [StardustDIDUrl](#StardustDIDUrl) +Append a string representing a path, query, and/or fragment, returning a new `StardustDIDUrl`. + +Must begin with a valid delimiter character: '/', '?', '#'. Overwrites the existing URL +segment and any following segments in order of path, query, then fragment. + +I.e. +- joining a path will clear the query and fragment. +- joining a query will clear the fragment. +- joining a fragment will only overwrite the fragment. + +**Kind**: instance method of [StardustDIDUrl](#StardustDIDUrl) + +| Param | Type | +| --- | --- | +| segment | string | + + + +### stardustDIDUrl.toString() ⇒ string +Returns the `StardustDIDUrl` as a string. + +**Kind**: instance method of [StardustDIDUrl](#StardustDIDUrl) + + +### stardustDIDUrl.toJSON() ⇒ any +Serializes this to a JSON object. + +**Kind**: instance method of [StardustDIDUrl](#StardustDIDUrl) + + +### stardustDIDUrl.clone() ⇒ [StardustDIDUrl](#StardustDIDUrl) +Deep clones the object. + +**Kind**: instance method of [StardustDIDUrl](#StardustDIDUrl) + + +### StardustDIDUrl.parse(input) ⇒ [StardustDIDUrl](#StardustDIDUrl) +Parses a `StardustDIDUrl` from the input string. + +**Kind**: static method of [StardustDIDUrl](#StardustDIDUrl) + +| Param | Type | +| --- | --- | +| input | string | + + + +### StardustDIDUrl.fromJSON(json) ⇒ [StardustDIDUrl](#StardustDIDUrl) +Deserializes an instance from a JSON object. + +**Kind**: static method of [StardustDIDUrl](#StardustDIDUrl) + +| Param | Type | +| --- | --- | +| json | any | + + + +## StardustDocument +**Kind**: global class + +* [StardustDocument](#StardustDocument) + * [new StardustDocument(network)](#new_StardustDocument_new) + * _instance_ + * [.id()](#StardustDocument+id) ⇒ [StardustDID](#StardustDID) + * [.controller()](#StardustDocument+controller) ⇒ [Array.<StardustDID>](#StardustDID) + * [.alsoKnownAs()](#StardustDocument+alsoKnownAs) ⇒ Array.<string> + * [.setAlsoKnownAs(urls)](#StardustDocument+setAlsoKnownAs) + * [.properties()](#StardustDocument+properties) ⇒ Map.<string, any> + * [.setPropertyUnchecked(key, value)](#StardustDocument+setPropertyUnchecked) + * [.service()](#StardustDocument+service) ⇒ [Array.<StardustService>](#StardustService) + * [.insertService(service)](#StardustDocument+insertService) ⇒ boolean + * [.removeService(did)](#StardustDocument+removeService) ⇒ boolean + * [.resolveService(query)](#StardustDocument+resolveService) ⇒ [StardustService](#StardustService) \| undefined + * [.methods()](#StardustDocument+methods) ⇒ [Array.<StardustVerificationMethod>](#StardustVerificationMethod) + * [.insertMethod(method, scope)](#StardustDocument+insertMethod) + * [.removeMethod(did)](#StardustDocument+removeMethod) + * [.resolveMethod(query, scope)](#StardustDocument+resolveMethod) ⇒ [StardustVerificationMethod](#StardustVerificationMethod) \| undefined + * [.attachMethodRelationship(didUrl, relationship)](#StardustDocument+attachMethodRelationship) ⇒ boolean + * [.detachMethodRelationship(didUrl, relationship)](#StardustDocument+detachMethodRelationship) ⇒ boolean + * [.signCredential(credential, privateKey, methodQuery, options)](#StardustDocument+signCredential) ⇒ [Credential](#Credential) + * [.signPresentation(presentation, privateKey, methodQuery, options)](#StardustDocument+signPresentation) ⇒ [Presentation](#Presentation) + * [.signData(data, privateKey, methodQuery, options)](#StardustDocument+signData) ⇒ any + * [.verifyData(data, options)](#StardustDocument+verifyData) ⇒ boolean + * [.pack()](#StardustDocument+pack) ⇒ Uint8Array + * [.packWithEncoding(encoding)](#StardustDocument+packWithEncoding) ⇒ Uint8Array + * [.metadata()](#StardustDocument+metadata) ⇒ [StardustDocumentMetadata](#StardustDocumentMetadata) + * [.metadataCreated()](#StardustDocument+metadataCreated) ⇒ [Timestamp](#Timestamp) \| undefined + * [.setMetadataCreated(timestamp)](#StardustDocument+setMetadataCreated) + * [.metadataUpdated()](#StardustDocument+metadataUpdated) ⇒ [Timestamp](#Timestamp) \| undefined + * [.setMetadataUpdated(timestamp)](#StardustDocument+setMetadataUpdated) + * [.setMetadataPropertyUnchecked(key, value)](#StardustDocument+setMetadataPropertyUnchecked) + * [.revokeCredentials(serviceQuery, indices)](#StardustDocument+revokeCredentials) + * [.unrevokeCredentials(serviceQuery, indices)](#StardustDocument+unrevokeCredentials) + * [.toJSON()](#StardustDocument+toJSON) ⇒ any + * [.clone()](#StardustDocument+clone) ⇒ [StardustDocument](#StardustDocument) + * _static_ + * [.newWithId(id)](#StardustDocument.newWithId) ⇒ [StardustDocument](#StardustDocument) + * [.unpack(did, stateMetadata)](#StardustDocument.unpack) ⇒ [StardustDocument](#StardustDocument) + * [.fromJSON(json)](#StardustDocument.fromJSON) ⇒ [StardustDocument](#StardustDocument) + + + +### new StardustDocument(network) +Constructs an empty DID Document with a [placeholder](#StardustDID.placeholder) identifier +for the given `network`. + + +| Param | Type | +| --- | --- | +| network | string | + + + +### stardustDocument.id() ⇒ [StardustDID](#StardustDID) +Returns a copy of the DID Document `id`. + +**Kind**: instance method of [StardustDocument](#StardustDocument) + + +### stardustDocument.controller() ⇒ [Array.<StardustDID>](#StardustDID) +Returns a copy of the list of document controllers. + +NOTE: controllers are determined by the `state_controller` unlock condition of the output +during resolution and are omitted when publishing. + +**Kind**: instance method of [StardustDocument](#StardustDocument) + + +### stardustDocument.alsoKnownAs() ⇒ Array.<string> +Returns a copy of the document's `alsoKnownAs` set. + +**Kind**: instance method of [StardustDocument](#StardustDocument) + + +### stardustDocument.setAlsoKnownAs(urls) +Sets the `alsoKnownAs` property in the DID document. + +**Kind**: instance method of [StardustDocument](#StardustDocument) + +| Param | Type | +| --- | --- | +| urls | string \| Array.<string> \| null | + + + +### stardustDocument.properties() ⇒ Map.<string, any> +Returns a copy of the custom DID Document properties. + +**Kind**: instance method of [StardustDocument](#StardustDocument) + + +### stardustDocument.setPropertyUnchecked(key, value) +Sets a custom property in the DID Document. +If the value is set to `null`, the custom property will be removed. + +### WARNING +This method can overwrite existing properties like `id` and result in an invalid document. + +**Kind**: instance method of [StardustDocument](#StardustDocument) + +| Param | Type | +| --- | --- | +| key | string | +| value | any | + + + +### stardustDocument.service() ⇒ [Array.<StardustService>](#StardustService) +Return a set of all [StardustService](#StardustService) in the document. + +**Kind**: instance method of [StardustDocument](#StardustDocument) + + +### stardustDocument.insertService(service) ⇒ boolean +Add a new [StardustService](#StardustService) to the document. + +Returns `true` if the service was added. + +**Kind**: instance method of [StardustDocument](#StardustDocument) + +| Param | Type | +| --- | --- | +| service | [StardustService](#StardustService) | + + + +### stardustDocument.removeService(did) ⇒ boolean +Remove a [StardustService](#StardustService) identified by the given [DIDUrl](#DIDUrl) from the document. + +Returns `true` if a service was removed. + +**Kind**: instance method of [StardustDocument](#StardustDocument) + +| Param | Type | +| --- | --- | +| did | [StardustDIDUrl](#StardustDIDUrl) | + + + +### stardustDocument.resolveService(query) ⇒ [StardustService](#StardustService) \| undefined +Returns the first [StardustService](#StardustService) with an `id` property matching the provided `query`, +if present. + +**Kind**: instance method of [StardustDocument](#StardustDocument) + +| Param | Type | +| --- | --- | +| query | [StardustDIDUrl](#StardustDIDUrl) \| string | + + + +### stardustDocument.methods() ⇒ [Array.<StardustVerificationMethod>](#StardustVerificationMethod) +Returns a list of all [StardustVerificationMethod](#StardustVerificationMethod) in the DID Document. + +**Kind**: instance method of [StardustDocument](#StardustDocument) + + +### stardustDocument.insertMethod(method, scope) +Adds a new `method` to the document in the given `scope`. + +**Kind**: instance method of [StardustDocument](#StardustDocument) + +| Param | Type | +| --- | --- | +| method | [StardustVerificationMethod](#StardustVerificationMethod) | +| scope | [MethodScope](#MethodScope) | + + + +### stardustDocument.removeMethod(did) +Removes all references to the specified Verification Method. + +**Kind**: instance method of [StardustDocument](#StardustDocument) + +| Param | Type | +| --- | --- | +| did | [StardustDIDUrl](#StardustDIDUrl) | + + + +### stardustDocument.resolveMethod(query, scope) ⇒ [StardustVerificationMethod](#StardustVerificationMethod) \| undefined +Returns a copy of the first verification method with an `id` property +matching the provided `query` and the verification relationship +specified by `scope`, if present. + +**Kind**: instance method of [StardustDocument](#StardustDocument) + +| Param | Type | +| --- | --- | +| query | [StardustDIDUrl](#StardustDIDUrl) \| string | +| scope | [MethodScope](#MethodScope) \| undefined | + + + +### stardustDocument.attachMethodRelationship(didUrl, relationship) ⇒ boolean +Attaches the relationship to the given method, if the method exists. + +Note: The method needs to be in the set of verification methods, +so it cannot be an embedded one. + +**Kind**: instance method of [StardustDocument](#StardustDocument) + +| Param | Type | +| --- | --- | +| didUrl | [StardustDIDUrl](#StardustDIDUrl) | +| relationship | number | + + + +### stardustDocument.detachMethodRelationship(didUrl, relationship) ⇒ boolean +Detaches the given relationship from the given method, if the method exists. + +**Kind**: instance method of [StardustDocument](#StardustDocument) + +| Param | Type | +| --- | --- | +| didUrl | [StardustDIDUrl](#StardustDIDUrl) | +| relationship | number | + + + +### stardustDocument.signCredential(credential, privateKey, methodQuery, options) ⇒ [Credential](#Credential) +Creates a signature for the given `Credential` with the specified DID Document +Verification Method. + +**Kind**: instance method of [StardustDocument](#StardustDocument) + +| Param | Type | +| --- | --- | +| credential | [Credential](#Credential) | +| privateKey | Uint8Array | +| methodQuery | [StardustDIDUrl](#StardustDIDUrl) \| string | +| options | [ProofOptions](#ProofOptions) | + + + +### stardustDocument.signPresentation(presentation, privateKey, methodQuery, options) ⇒ [Presentation](#Presentation) +Creates a signature for the given `Presentation` with the specified DID Document +Verification Method. + +**Kind**: instance method of [StardustDocument](#StardustDocument) + +| Param | Type | +| --- | --- | +| presentation | [Presentation](#Presentation) | +| privateKey | Uint8Array | +| methodQuery | [StardustDIDUrl](#StardustDIDUrl) \| string | +| options | [ProofOptions](#ProofOptions) | + + + +### stardustDocument.signData(data, privateKey, methodQuery, options) ⇒ any +Creates a signature for the given `data` with the specified DID Document +Verification Method. + +NOTE: use `signSelf` or `signDocument` for DID Documents. + +**Kind**: instance method of [StardustDocument](#StardustDocument) + +| Param | Type | +| --- | --- | +| data | any | +| privateKey | Uint8Array | +| methodQuery | [StardustDIDUrl](#StardustDIDUrl) \| string | +| options | [ProofOptions](#ProofOptions) | + + + +### stardustDocument.verifyData(data, options) ⇒ boolean +Verifies the authenticity of `data` using the target verification method. + +**Kind**: instance method of [StardustDocument](#StardustDocument) + +| Param | Type | +| --- | --- | +| data | any | +| options | [VerifierOptions](#VerifierOptions) | + + + +### stardustDocument.pack() ⇒ Uint8Array +Serializes the document for inclusion in an Alias Output's state metadata +with the default [StateMetadataEncoding](#StateMetadataEncoding). + +**Kind**: instance method of [StardustDocument](#StardustDocument) + + +### stardustDocument.packWithEncoding(encoding) ⇒ Uint8Array +Serializes the document for inclusion in an Alias Output's state metadata. + +**Kind**: instance method of [StardustDocument](#StardustDocument) + +| Param | Type | +| --- | --- | +| encoding | number | + + + +### stardustDocument.metadata() ⇒ [StardustDocumentMetadata](#StardustDocumentMetadata) +Returns a copy of the metadata associated with this document. + +NOTE: Copies all the metadata. See also `metadataCreated`, `metadataUpdated`, +`metadataPreviousMessageId`, `metadataProof` if only a subset of the metadata required. + +**Kind**: instance method of [StardustDocument](#StardustDocument) + + +### stardustDocument.metadataCreated() ⇒ [Timestamp](#Timestamp) \| undefined +Returns a copy of the timestamp of when the DID document was created. + +**Kind**: instance method of [StardustDocument](#StardustDocument) + + +### stardustDocument.setMetadataCreated(timestamp) +Sets the timestamp of when the DID document was created. + +**Kind**: instance method of [StardustDocument](#StardustDocument) + +| Param | Type | +| --- | --- | +| timestamp | [Timestamp](#Timestamp) \| undefined | + + + +### stardustDocument.metadataUpdated() ⇒ [Timestamp](#Timestamp) \| undefined +Returns a copy of the timestamp of the last DID document update. + +**Kind**: instance method of [StardustDocument](#StardustDocument) + + +### stardustDocument.setMetadataUpdated(timestamp) +Sets the timestamp of the last DID document update. + +**Kind**: instance method of [StardustDocument](#StardustDocument) + +| Param | Type | +| --- | --- | +| timestamp | [Timestamp](#Timestamp) \| undefined | + + + +### stardustDocument.setMetadataPropertyUnchecked(key, value) +Sets a custom property in the document metadata. +If the value is set to `null`, the custom property will be removed. + +**Kind**: instance method of [StardustDocument](#StardustDocument) + +| Param | Type | +| --- | --- | +| key | string | +| value | any | + + + +### stardustDocument.revokeCredentials(serviceQuery, indices) +If the document has a `RevocationBitmap` service identified by `serviceQuery`, +revoke all specified `indices`. + +**Kind**: instance method of [StardustDocument](#StardustDocument) + +| Param | Type | +| --- | --- | +| serviceQuery | [StardustDIDUrl](#StardustDIDUrl) \| string | +| indices | number \| Array.<number> | + + + +### stardustDocument.unrevokeCredentials(serviceQuery, indices) +If the document has a `RevocationBitmap` service identified by `serviceQuery`, +unrevoke all specified `indices`. + +**Kind**: instance method of [StardustDocument](#StardustDocument) + +| Param | Type | +| --- | --- | +| serviceQuery | [StardustDIDUrl](#StardustDIDUrl) \| string | +| indices | number \| Array.<number> | + + + +### stardustDocument.toJSON() ⇒ any +Serializes this to a JSON object. + +**Kind**: instance method of [StardustDocument](#StardustDocument) + + +### stardustDocument.clone() ⇒ [StardustDocument](#StardustDocument) +Deep clones the object. + +**Kind**: instance method of [StardustDocument](#StardustDocument) + + +### StardustDocument.newWithId(id) ⇒ [StardustDocument](#StardustDocument) +Constructs an empty DID Document with the given identifier. + +**Kind**: static method of [StardustDocument](#StardustDocument) + +| Param | Type | +| --- | --- | +| id | [StardustDID](#StardustDID) | + + + +### StardustDocument.unpack(did, stateMetadata) ⇒ [StardustDocument](#StardustDocument) +Deserializes the document from the state metadata bytes of an Alias Output. + +NOTE: `did` is required since it is omitted from the serialized DID Document and +cannot be inferred from the state metadata. It also indicates the network, which is not +encoded in the `AliasId` alone. + +**Kind**: static method of [StardustDocument](#StardustDocument) + +| Param | Type | +| --- | --- | +| did | [StardustDID](#StardustDID) | +| stateMetadata | Uint8Array | + + + +### StardustDocument.fromJSON(json) ⇒ [StardustDocument](#StardustDocument) +Deserializes an instance from a JSON object. + +**Kind**: static method of [StardustDocument](#StardustDocument) + +| Param | Type | +| --- | --- | +| json | any | + + + +## StardustDocumentMetadata +Additional attributes related to an IOTA DID Document. + +**Kind**: global class + +* [StardustDocumentMetadata](#StardustDocumentMetadata) + * _instance_ + * [.created()](#StardustDocumentMetadata+created) ⇒ [Timestamp](#Timestamp) \| undefined + * [.updated()](#StardustDocumentMetadata+updated) ⇒ [Timestamp](#Timestamp) \| undefined + * [.properties()](#StardustDocumentMetadata+properties) ⇒ Map.<string, any> + * [.toJSON()](#StardustDocumentMetadata+toJSON) ⇒ any + * [.clone()](#StardustDocumentMetadata+clone) ⇒ [StardustDocumentMetadata](#StardustDocumentMetadata) + * _static_ + * [.fromJSON(json)](#StardustDocumentMetadata.fromJSON) ⇒ [StardustDocumentMetadata](#StardustDocumentMetadata) + + + +### stardustDocumentMetadata.created() ⇒ [Timestamp](#Timestamp) \| undefined +Returns a copy of the timestamp of when the DID document was created. + +**Kind**: instance method of [StardustDocumentMetadata](#StardustDocumentMetadata) + + +### stardustDocumentMetadata.updated() ⇒ [Timestamp](#Timestamp) \| undefined +Returns a copy of the timestamp of the last DID document update. + +**Kind**: instance method of [StardustDocumentMetadata](#StardustDocumentMetadata) + + +### stardustDocumentMetadata.properties() ⇒ Map.<string, any> +Returns a copy of the custom metadata properties. + +**Kind**: instance method of [StardustDocumentMetadata](#StardustDocumentMetadata) + + +### stardustDocumentMetadata.toJSON() ⇒ any +Serializes this to a JSON object. + +**Kind**: instance method of [StardustDocumentMetadata](#StardustDocumentMetadata) + + +### stardustDocumentMetadata.clone() ⇒ [StardustDocumentMetadata](#StardustDocumentMetadata) +Deep clones the object. + +**Kind**: instance method of [StardustDocumentMetadata](#StardustDocumentMetadata) + + +### StardustDocumentMetadata.fromJSON(json) ⇒ [StardustDocumentMetadata](#StardustDocumentMetadata) +Deserializes an instance from a JSON object. + +**Kind**: static method of [StardustDocumentMetadata](#StardustDocumentMetadata) + +| Param | Type | +| --- | --- | +| json | any | + + + +## StardustService +A `Service` adhering to the IOTA UTXO DID method specification. + +**Kind**: global class + +* [StardustService](#StardustService) + * [new StardustService(service)](#new_StardustService_new) + * _instance_ + * [.id()](#StardustService+id) ⇒ [StardustDIDUrl](#StardustDIDUrl) + * [.type()](#StardustService+type) ⇒ Array.<string> + * [.serviceEndpoint()](#StardustService+serviceEndpoint) ⇒ string \| Array.<string> \| Map.<string, Array.<string>> + * [.properties()](#StardustService+properties) ⇒ Map.<string, any> + * [.toJSON()](#StardustService+toJSON) ⇒ any + * [.clone()](#StardustService+clone) ⇒ [StardustService](#StardustService) + * _static_ + * [.fromJSON(json)](#StardustService.fromJSON) ⇒ [StardustService](#StardustService) + + + +### new StardustService(service) + +| Param | Type | +| --- | --- | +| service | IStardustService | + + + +### stardustService.id() ⇒ [StardustDIDUrl](#StardustDIDUrl) +Returns a copy of the `Service` id. + +**Kind**: instance method of [StardustService](#StardustService) + + +### stardustService.type() ⇒ Array.<string> +Returns a copy of the `Service` type. + +**Kind**: instance method of [StardustService](#StardustService) + + +### stardustService.serviceEndpoint() ⇒ string \| Array.<string> \| Map.<string, Array.<string>> +Returns a copy of the `Service` endpoint. + +**Kind**: instance method of [StardustService](#StardustService) + + +### stardustService.properties() ⇒ Map.<string, any> +Returns a copy of the custom properties on the `Service`. + +**Kind**: instance method of [StardustService](#StardustService) + + +### stardustService.toJSON() ⇒ any +Serializes this to a JSON object. + +**Kind**: instance method of [StardustService](#StardustService) + + +### stardustService.clone() ⇒ [StardustService](#StardustService) +Deep clones the object. + +**Kind**: instance method of [StardustService](#StardustService) + + +### StardustService.fromJSON(json) ⇒ [StardustService](#StardustService) +Deserializes an instance from a JSON object. + +**Kind**: static method of [StardustService](#StardustService) + +| Param | Type | +| --- | --- | +| json | any | + + + +## StardustVerificationMethod +**Kind**: global class + +* [StardustVerificationMethod](#StardustVerificationMethod) + * [new StardustVerificationMethod(did, keyType, publicKey, fragment)](#new_StardustVerificationMethod_new) + * _instance_ + * [.id()](#StardustVerificationMethod+id) ⇒ [StardustDIDUrl](#StardustDIDUrl) + * [.controller()](#StardustVerificationMethod+controller) ⇒ [StardustDID](#StardustDID) + * [.setController(did)](#StardustVerificationMethod+setController) + * [.type()](#StardustVerificationMethod+type) ⇒ [MethodType](#MethodType) + * [.data()](#StardustVerificationMethod+data) ⇒ [MethodData](#MethodData) + * [.toJSON()](#StardustVerificationMethod+toJSON) ⇒ any + * [.clone()](#StardustVerificationMethod+clone) ⇒ [StardustVerificationMethod](#StardustVerificationMethod) + * _static_ + * [.fromJSON(json)](#StardustVerificationMethod.fromJSON) ⇒ [StardustVerificationMethod](#StardustVerificationMethod) + + + +### new StardustVerificationMethod(did, keyType, publicKey, fragment) +Creates a new `StardustVerificationMethod` from the given `did` and public key. + + +| Param | Type | +| --- | --- | +| did | [StardustDID](#StardustDID) | +| keyType | number | +| publicKey | Uint8Array | +| fragment | string | + + + +### stardustVerificationMethod.id() ⇒ [StardustDIDUrl](#StardustDIDUrl) +Returns a reference to the `StardustVerificationMethod` id. + +**Kind**: instance method of [StardustVerificationMethod](#StardustVerificationMethod) + + +### stardustVerificationMethod.controller() ⇒ [StardustDID](#StardustDID) +Returns a copy of the `controller` `DID` of the `StardustVerificationMethod`. + +**Kind**: instance method of [StardustVerificationMethod](#StardustVerificationMethod) + + +### stardustVerificationMethod.setController(did) +Sets the `controller` `DID` of the `StardustVerificationMethod`. + +**Kind**: instance method of [StardustVerificationMethod](#StardustVerificationMethod) + +| Param | Type | +| --- | --- | +| did | [StardustDID](#StardustDID) | + + + +### stardustVerificationMethod.type() ⇒ [MethodType](#MethodType) +Returns a copy of the `StardustVerificationMethod` type. + +**Kind**: instance method of [StardustVerificationMethod](#StardustVerificationMethod) + + +### stardustVerificationMethod.data() ⇒ [MethodData](#MethodData) +Returns a copy of the `StardustVerificationMethod` public key data. + +**Kind**: instance method of [StardustVerificationMethod](#StardustVerificationMethod) + + +### stardustVerificationMethod.toJSON() ⇒ any +Serializes this to a JSON object. + +**Kind**: instance method of [StardustVerificationMethod](#StardustVerificationMethod) + + +### stardustVerificationMethod.clone() ⇒ [StardustVerificationMethod](#StardustVerificationMethod) +Deep clones the object. + +**Kind**: instance method of [StardustVerificationMethod](#StardustVerificationMethod) + + +### StardustVerificationMethod.fromJSON(json) ⇒ [StardustVerificationMethod](#StardustVerificationMethod) +Deserializes an instance from a JSON object. + +**Kind**: static method of [StardustVerificationMethod](#StardustVerificationMethod) + +| Param | Type | +| --- | --- | +| json | any | + + + +## StorageTestSuite +A test suite for the `Storage` interface. + +This module contains a set of tests that a correct storage implementation +should pass. Note that not every edge case is tested. + +Tests usually rely on multiple interface methods being implemented, so they should only +be run on a fully implemented version. That's why there is not a single test case for every +interface method. + +**Kind**: global class + +* [StorageTestSuite](#StorageTestSuite) + * [.didCreateGenerateKeyTest(storage)](#StorageTestSuite.didCreateGenerateKeyTest) ⇒ Promise.<void> + * [.didCreatePrivateKeyTest(storage)](#StorageTestSuite.didCreatePrivateKeyTest) ⇒ Promise.<void> + * [.didListTest(storage)](#StorageTestSuite.didListTest) ⇒ Promise.<void> + * [.didPurgeTest(storage)](#StorageTestSuite.didPurgeTest) ⇒ Promise.<void> + * [.keyGenerateTest(storage)](#StorageTestSuite.keyGenerateTest) ⇒ Promise.<void> + * [.keyDeleteTest(storage)](#StorageTestSuite.keyDeleteTest) ⇒ Promise.<void> + * [.keyInsertTest(storage)](#StorageTestSuite.keyInsertTest) ⇒ Promise.<void> + * [.keySignEd25519Test(storage)](#StorageTestSuite.keySignEd25519Test) ⇒ Promise.<void> + * [.encryptionTest(alice_storage, bob_storage)](#StorageTestSuite.encryptionTest) ⇒ Promise.<void> + + + +### StorageTestSuite.didCreateGenerateKeyTest(storage) ⇒ Promise.<void> +**Kind**: static method of [StorageTestSuite](#StorageTestSuite) + +| Param | Type | +| --- | --- | +| storage | Storage | + + + +### StorageTestSuite.didCreatePrivateKeyTest(storage) ⇒ Promise.<void> +**Kind**: static method of [StorageTestSuite](#StorageTestSuite) + +| Param | Type | +| --- | --- | +| storage | Storage | + + + +### StorageTestSuite.didListTest(storage) ⇒ Promise.<void> +**Kind**: static method of [StorageTestSuite](#StorageTestSuite) + +| Param | Type | +| --- | --- | +| storage | Storage | + + + +### StorageTestSuite.didPurgeTest(storage) ⇒ Promise.<void> +**Kind**: static method of [StorageTestSuite](#StorageTestSuite) + +| Param | Type | +| --- | --- | +| storage | Storage | + + + +### StorageTestSuite.keyGenerateTest(storage) ⇒ Promise.<void> +**Kind**: static method of [StorageTestSuite](#StorageTestSuite) + +| Param | Type | +| --- | --- | +| storage | Storage | + + + +### StorageTestSuite.keyDeleteTest(storage) ⇒ Promise.<void> +**Kind**: static method of [StorageTestSuite](#StorageTestSuite) + +| Param | Type | +| --- | --- | +| storage | Storage | + + + +### StorageTestSuite.keyInsertTest(storage) ⇒ Promise.<void> +**Kind**: static method of [StorageTestSuite](#StorageTestSuite) + +| Param | Type | +| --- | --- | +| storage | Storage | + + + +### StorageTestSuite.keySignEd25519Test(storage) ⇒ Promise.<void> +**Kind**: static method of [StorageTestSuite](#StorageTestSuite) + +| Param | Type | +| --- | --- | +| storage | Storage | + + + +### StorageTestSuite.encryptionTest(alice_storage, bob_storage) ⇒ Promise.<void> +**Kind**: static method of [StorageTestSuite](#StorageTestSuite) + +| Param | Type | +| --- | --- | +| alice_storage | Storage | +| bob_storage | Storage | + + + +## Timestamp +**Kind**: global class + +* [Timestamp](#Timestamp) + * _instance_ + * [.toRFC3339()](#Timestamp+toRFC3339) ⇒ string + * [.checkedAdd(duration)](#Timestamp+checkedAdd) ⇒ [Timestamp](#Timestamp) \| undefined + * [.checkedSub(duration)](#Timestamp+checkedSub) ⇒ [Timestamp](#Timestamp) \| undefined + * [.toJSON()](#Timestamp+toJSON) ⇒ any + * _static_ + * [.parse(input)](#Timestamp.parse) ⇒ [Timestamp](#Timestamp) + * [.nowUTC()](#Timestamp.nowUTC) ⇒ [Timestamp](#Timestamp) + * [.fromJSON(json)](#Timestamp.fromJSON) ⇒ [Timestamp](#Timestamp) + + + +### timestamp.toRFC3339() ⇒ string +Returns the `Timestamp` as an RFC 3339 `String`. + +**Kind**: instance method of [Timestamp](#Timestamp) + + +### timestamp.checkedAdd(duration) ⇒ [Timestamp](#Timestamp) \| undefined +Computes `self + duration` + +Returns `null` if the operation leads to a timestamp not in the valid range for [RFC 3339](https://tools.ietf.org/html/rfc3339). + +**Kind**: instance method of [Timestamp](#Timestamp) + +| Param | Type | +| --- | --- | +| duration | [Duration](#Duration) | + + + +### timestamp.checkedSub(duration) ⇒ [Timestamp](#Timestamp) \| undefined +Computes `self - duration` + +Returns `null` if the operation leads to a timestamp not in the valid range for [RFC 3339](https://tools.ietf.org/html/rfc3339). + +**Kind**: instance method of [Timestamp](#Timestamp) + +| Param | Type | +| --- | --- | +| duration | [Duration](#Duration) | + + + +### timestamp.toJSON() ⇒ any +Serializes this to a JSON object. + +**Kind**: instance method of [Timestamp](#Timestamp) + + +### Timestamp.parse(input) ⇒ [Timestamp](#Timestamp) +Parses a `Timestamp` from the provided input string. + +**Kind**: static method of [Timestamp](#Timestamp) + +| Param | Type | +| --- | --- | +| input | string | + + + +### Timestamp.nowUTC() ⇒ [Timestamp](#Timestamp) +Creates a new `Timestamp` with the current date and time. + +**Kind**: static method of [Timestamp](#Timestamp) + + +### Timestamp.fromJSON(json) ⇒ [Timestamp](#Timestamp) +Deserializes an instance from a JSON object. + +**Kind**: static method of [Timestamp](#Timestamp) + +| Param | Type | +| --- | --- | +| json | any | + + + +## VerificationMethod **Kind**: global class * [VerificationMethod](#VerificationMethod) - * [new VerificationMethod(did, key_type, public_key, fragment)](#new_VerificationMethod_new) + * [new VerificationMethod(did, keyType, publicKey, fragment)](#new_VerificationMethod_new) * _instance_ * [.id()](#VerificationMethod+id) ⇒ [DIDUrl](#DIDUrl) * [.controller()](#VerificationMethod+controller) ⇒ [DID](#DID) - * [.SetController(did)](#VerificationMethod+SetController) + * [.setController(did)](#VerificationMethod+setController) * [.type()](#VerificationMethod+type) ⇒ [MethodType](#MethodType) * [.data()](#VerificationMethod+data) ⇒ [MethodData](#MethodData) * [.toJSON()](#VerificationMethod+toJSON) ⇒ any @@ -4787,33 +5875,33 @@ Deserializes an instance from a JSON object. -### new VerificationMethod(did, key_type, public_key, fragment) -Creates a new `VerificationMethod` object from the given `did` and public key. +### new VerificationMethod(did, keyType, publicKey, fragment) +Creates a new `VerificationMethod` from the given `did` and public key. | Param | Type | | --- | --- | | did | [DID](#DID) | -| key_type | number | -| public_key | Uint8Array | +| keyType | number | +| publicKey | Uint8Array | | fragment | string | ### verificationMethod.id() ⇒ [DIDUrl](#DIDUrl) -Returns a copy of the `id` `DIDUrl` of the `VerificationMethod` object. +Returns a copy of the `id` `DIDUrl` of the `VerificationMethod`. **Kind**: instance method of [VerificationMethod](#VerificationMethod) ### verificationMethod.controller() ⇒ [DID](#DID) -Returns a copy of the `controller` `DID` of the `VerificationMethod` object. +Returns a copy of the `controller` `DID` of the `VerificationMethod`. **Kind**: instance method of [VerificationMethod](#VerificationMethod) - + -### verificationMethod.SetController(did) -Sets the `controller` `DID` of the `VerificationMethod` object. +### verificationMethod.setController(did) +Sets the `controller` `DID` of the `VerificationMethod`. **Kind**: instance method of [VerificationMethod](#VerificationMethod) @@ -4983,6 +6071,10 @@ This is possible because Ed25519 is birationally equivalent to Curve25519 used b ## DIDMessageEncoding **Kind**: global variable + + +## StateMetadataEncoding +**Kind**: global variable ## StatusCheck @@ -5060,15 +6152,15 @@ Return all errors that occur during validation. ## FirstError Return after the first error occurs. -**Kind**: global variable - - -## MethodRelationship **Kind**: global variable ## KeyType **Kind**: global variable + + +## MethodRelationship +**Kind**: global variable ## start() diff --git a/bindings/wasm/src/account/wasm_account/account.rs b/bindings/wasm/src/account/wasm_account/account.rs index 5a55917ab5..0d9582ce34 100644 --- a/bindings/wasm/src/account/wasm_account/account.rs +++ b/bindings/wasm/src/account/wasm_account/account.rs @@ -31,6 +31,7 @@ use crate::account::types::WasmCekAlgorithm; use crate::account::types::WasmEncryptedData; use crate::account::types::WasmEncryptionAlgorithm; use crate::common::PromiseVoid; +use crate::common::UOneOrManyNumber; use crate::credential::WasmCredential; use crate::credential::WasmPresentation; use crate::crypto::WasmProofOptions; @@ -467,9 +468,3 @@ extern "C" { #[wasm_bindgen(typescript_type = "Promise")] pub type PromiseAccount; } - -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(typescript_type = "number | number[]")] - pub type UOneOrManyNumber; -} diff --git a/bindings/wasm/src/account/wasm_account/mod.rs b/bindings/wasm/src/account/wasm_account/mod.rs index 8ad8a420bc..3fd26934fa 100644 --- a/bindings/wasm/src/account/wasm_account/mod.rs +++ b/bindings/wasm/src/account/wasm_account/mod.rs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 pub use self::account::PromiseAccount; -pub use self::account::UOneOrManyNumber; pub use self::account::WasmAccount; pub use self::account_builder::WasmAccountBuilder; diff --git a/bindings/wasm/src/common/timestamp.rs b/bindings/wasm/src/common/timestamp.rs index 56093e062c..8c396ec9cd 100644 --- a/bindings/wasm/src/common/timestamp.rs +++ b/bindings/wasm/src/common/timestamp.rs @@ -8,6 +8,12 @@ use wasm_bindgen::prelude::*; use crate::error::Result; use crate::error::WasmResult; +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(typescript_type = "Timestamp | undefined")] + pub type OptionTimestamp; +} + #[wasm_bindgen(js_name = Timestamp, inspectable)] pub struct WasmTimestamp(pub(crate) Timestamp); diff --git a/bindings/wasm/src/common/types.rs b/bindings/wasm/src/common/types.rs index fc9bfc56bb..8d52f0e49e 100644 --- a/bindings/wasm/src/common/types.rs +++ b/bindings/wasm/src/common/types.rs @@ -20,6 +20,12 @@ extern "C" { #[wasm_bindgen(typescript_type = "Map")] pub type MapStringAny; + + #[wasm_bindgen(typescript_type = "number | number[]")] + pub type UOneOrManyNumber; + + #[wasm_bindgen(typescript_type = "string | string[] | null")] + pub type OptionOneOrManyString; } impl TryFrom for MapStringAny { diff --git a/bindings/wasm/src/did/mod.rs b/bindings/wasm/src/did/mod.rs index 37910196ea..5d52195ef8 100644 --- a/bindings/wasm/src/did/mod.rs +++ b/bindings/wasm/src/did/mod.rs @@ -19,7 +19,7 @@ pub use self::wasm_resolved_document::DocumentOrResolvedDocument; pub use self::wasm_resolved_document::PromiseArrayResolvedDocument; pub use self::wasm_resolved_document::PromiseResolvedDocument; pub use self::wasm_resolved_document::WasmResolvedDocument; -pub(crate) use self::wasm_service::service_endpoint_to_js_value; +pub use self::wasm_service::IService; pub use self::wasm_service::UServiceEndpoint; pub use self::wasm_service::WasmService; pub use self::wasm_verification_method::WasmVerificationMethod; diff --git a/bindings/wasm/src/did/wasm_did.rs b/bindings/wasm/src/did/wasm_did.rs index 87b1931a1a..4c8c15c283 100644 --- a/bindings/wasm/src/did/wasm_did.rs +++ b/bindings/wasm/src/did/wasm_did.rs @@ -10,12 +10,30 @@ use crate::error::Result; use crate::error::WasmResult; use crate::tangle::WasmNetwork; +/// A DID conforming to the IOTA DID method specification. +/// /// @typicalname did #[wasm_bindgen(js_name = DID, inspectable)] pub struct WasmDID(pub(crate) IotaDID); #[wasm_bindgen(js_class = DID)] impl WasmDID { + /// The IOTA DID method name (`"iota"`). + #[wasm_bindgen(getter = METHOD)] + pub fn static_method() -> String { + IotaDID::METHOD.to_owned() + } + + /// The default Tangle network (`"main"`). + #[wasm_bindgen(getter = DEFAULT_NETWORK)] + pub fn static_default_network() -> String { + IotaDID::DEFAULT_NETWORK.to_owned() + } + + // =========================================================================== + // Constructors + // =========================================================================== + /// Creates a new `DID` from a public key. #[wasm_bindgen(constructor)] pub fn new(public_key: &[u8], network: Option) -> Result { @@ -38,28 +56,76 @@ impl WasmDID { IotaDID::parse(input).wasm_result().map(Self) } - /// Returns the IOTA tangle network of the `DID`. + // =========================================================================== + // Properties + // =========================================================================== + + /// Returns the Tangle network of the `DID`. #[wasm_bindgen] pub fn network(&self) -> Result { self.0.network().map(Into::into).wasm_result() } - /// Returns the IOTA tangle network of the `DID`. - #[wasm_bindgen(getter = networkName)] - pub fn network_name(&self) -> String { - self.0.network_str().into() + /// Returns the Tangle network name of the `DID`. + #[wasm_bindgen(js_name = networkStr)] + pub fn network_str(&self) -> String { + self.0.network_str().to_owned() } /// Returns a copy of the unique tag of the `DID`. #[wasm_bindgen] pub fn tag(&self) -> String { - self.0.tag().into() + self.0.tag().to_owned() + } + + // =========================================================================== + // DID trait + // =========================================================================== + + /// Returns the `DID` scheme. + /// + /// E.g. + /// - `"did:example:12345678" -> "did"` + /// - `"did:iota:main:12345678" -> "did"` + #[wasm_bindgen] + pub fn scheme(&self) -> String { + self.0.scheme().to_owned() + } + + /// Returns the `DID` authority: the method name and method-id. + /// + /// E.g. + /// - `"did:example:12345678" -> "example:12345678"` + /// - `"did:iota:main:12345678" -> "iota:main:12345678"` + #[wasm_bindgen] + pub fn authority(&self) -> String { + self.0.authority().to_owned() + } + + /// Returns the `DID` method name. + /// + /// E.g. + /// - `"did:example:12345678" -> "example"` + /// - `"did:iota:main:12345678" -> "iota"` + #[wasm_bindgen] + pub fn method(&self) -> String { + self.0.method().to_owned() + } + + /// Returns the `DID` method-specific ID. + /// + /// E.g. + /// - `"did:example:12345678" -> "12345678"` + /// - `"did:iota:main:12345678" -> "main:12345678"` + #[wasm_bindgen(js_name = methodId)] + pub fn method_id(&self) -> String { + self.0.method_id().to_owned() } /// Construct a new `DIDUrl` by joining with a relative DID Url string. #[wasm_bindgen] - pub fn join(self, segment: &str) -> Result { - self.0.join(segment).wasm_result().map(WasmDIDUrl) + pub fn join(&self, segment: &str) -> Result { + self.0.clone().join(segment).wasm_result().map(WasmDIDUrl) } /// Clones the `DID` into a `DIDUrl`. @@ -68,7 +134,7 @@ impl WasmDID { WasmDIDUrl::from(self.0.to_url()) } - /// Converts the `DID` into a `DIDUrl`. + /// Converts the `DID` into a `DIDUrl`, consuming it. #[wasm_bindgen(js_name = intoUrl)] pub fn into_url(self) -> WasmDIDUrl { WasmDIDUrl::from(self.0.into_url()) diff --git a/bindings/wasm/src/did/wasm_did_url.rs b/bindings/wasm/src/did/wasm_did_url.rs index 353677671b..8ae1af32ea 100644 --- a/bindings/wasm/src/did/wasm_did_url.rs +++ b/bindings/wasm/src/did/wasm_did_url.rs @@ -1,8 +1,6 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use identity_iota::did::DIDUrl; -use identity_iota::iota_core::IotaDID; use identity_iota::iota_core::IotaDIDUrl; use wasm_bindgen::prelude::*; @@ -10,6 +8,8 @@ use crate::did::WasmDID; use crate::error::Result; use crate::error::WasmResult; +/// A DID URL conforming to the IOTA DID method specification. +/// /// @typicalname didUrl #[wasm_bindgen(js_name = DIDUrl, inspectable)] pub struct WasmDIDUrl(pub(crate) IotaDIDUrl); @@ -19,7 +19,7 @@ impl WasmDIDUrl { /// Parses a `DIDUrl` from the input string. #[wasm_bindgen] pub fn parse(input: &str) -> Result { - IotaDIDUrl::parse(input).map(WasmDIDUrl::from).wasm_result() + IotaDIDUrl::parse(input).map(WasmDIDUrl).wasm_result() } /// Return a copy of the `DID` section of the `DIDUrl`. @@ -101,7 +101,7 @@ impl From for WasmDIDUrl { } } -impl From for DIDUrl { +impl From for IotaDIDUrl { fn from(wasm_did_url: WasmDIDUrl) -> Self { wasm_did_url.0 } diff --git a/bindings/wasm/src/did/wasm_document.rs b/bindings/wasm/src/did/wasm_document.rs index ea278f2dc4..4cf66955bd 100644 --- a/bindings/wasm/src/did/wasm_document.rs +++ b/bindings/wasm/src/did/wasm_document.rs @@ -23,21 +23,23 @@ use identity_iota::iota_core::NetworkName; use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; -use crate::account::wasm_account::UOneOrManyNumber; use crate::common::ArrayString; use crate::common::MapStringAny; +use crate::common::OptionOneOrManyString; +use crate::common::OptionTimestamp; +use crate::common::UOneOrManyNumber; use crate::common::WasmTimestamp; use crate::credential::WasmCredential; use crate::credential::WasmPresentation; use crate::crypto::WasmKeyPair; use crate::crypto::WasmProof; use crate::crypto::WasmProofOptions; -use crate::did::wasm_method_relationship::WasmMethodRelationship; use crate::did::RefMethodScope; use crate::did::WasmDID; use crate::did::WasmDIDUrl; use crate::did::WasmDiffMessage; use crate::did::WasmDocumentMetadata; +use crate::did::WasmMethodRelationship; use crate::did::WasmMethodScope; use crate::did::WasmMethodType; use crate::did::WasmService; @@ -108,7 +110,7 @@ impl WasmDocument { /// Note: Duplicates will be ignored. /// Use `null` to remove all controllers. #[wasm_bindgen(js_name = setController)] - pub fn set_controller(&mut self, controllers: &UOneOrManyDID) -> Result<()> { + pub fn set_controller(&mut self, controllers: &OptionOneOrManyDID) -> Result<()> { let controllers: Option> = controllers.into_serde().wasm_result()?; let controller_set: Option> = if let Some(controllers) = controllers.map(OneOrMany::into_vec) { if controllers.is_empty() { @@ -123,7 +125,7 @@ impl WasmDocument { Ok(()) } - /// Returns a list of document controllers. + /// Returns a copy of the list of document controllers. #[wasm_bindgen] pub fn controller(&self) -> ArrayDID { match self.0.controller() { @@ -140,7 +142,7 @@ impl WasmDocument { /// Sets the `alsoKnownAs` property in the DID document. #[wasm_bindgen(js_name = setAlsoKnownAs)] - pub fn set_also_known_as(&mut self, urls: &UOneOrManyUrl) -> Result<()> { + pub fn set_also_known_as(&mut self, urls: &OptionOneOrManyString) -> Result<()> { let urls: Option> = urls.into_serde().wasm_result()?; let mut urls_set: OrderedSet = OrderedSet::new(); if let Some(urls) = urls { @@ -152,7 +154,7 @@ impl WasmDocument { Ok(()) } - /// Returns a set of the document's `alsoKnownAs`. + /// Returns a copy of the document's `alsoKnownAs` set. #[wasm_bindgen(js_name = alsoKnownAs)] pub fn also_known_as(&self) -> ArrayString { self @@ -186,12 +188,8 @@ impl WasmDocument { /// Returns a copy of the custom DID Document properties. #[wasm_bindgen] - pub fn properties(&mut self) -> Result { - let properties_map = js_sys::Map::new(); - for (key, value) in self.0.properties().iter() { - properties_map.set(&JsValue::from(key), &JsValue::from_serde(&value).wasm_result()?); - } - Ok(properties_map.unchecked_into::()) + pub fn properties(&self) -> Result { + MapStringAny::try_from(self.0.properties()) } // =========================================================================== @@ -252,7 +250,7 @@ impl WasmDocument { .collect::() .unchecked_into::() } - /// Adds a new Verification Method to the DID Document. + /// Adds a new `method` to the document in the given `scope`. #[wasm_bindgen(js_name = insertMethod)] pub fn insert_method(&mut self, method: &WasmVerificationMethod, scope: &WasmMethodScope) -> Result<()> { self.0.insert_method(method.0.clone(), scope.0).wasm_result()?; @@ -279,10 +277,9 @@ impl WasmDocument { .wasm_result() } - /// Returns a copy of the first `VerificationMethod` with an `id` property - /// matching the provided `query`. - /// - /// Throws an error if the method is not found. + /// Returns a copy of the first verification method with an `id` property + /// matching the provided `query` and the verification relationship + /// specified by `scope`, if present. #[wasm_bindgen(js_name = resolveMethod)] pub fn resolve_method( &self, @@ -292,15 +289,8 @@ impl WasmDocument { let method_query: String = query.into_serde().wasm_result()?; let method_scope: Option = scope.map(|js| js.into_serde().wasm_result()).transpose()?; - let method: Option<&IotaVerificationMethod> = if let Some(scope) = method_scope { - self.0.resolve_method(&method_query, Some(scope)) - } else { - self.0.resolve_method(&method_query, None) - }; - match method { - None => Ok(None), - Some(method) => Ok(Some(WasmVerificationMethod(method.clone()))), - } + let method: Option<&IotaVerificationMethod> = self.0.resolve_method(&method_query, method_scope); + Ok(method.cloned().map(WasmVerificationMethod)) } /// Attempts to resolve the given method query into a method capable of signing a document update. @@ -316,28 +306,30 @@ impl WasmDocument { /// /// Note: The method needs to be in the set of verification methods, /// so it cannot be an embedded one. + #[allow(non_snake_case)] #[wasm_bindgen(js_name = attachMethodRelationship)] pub fn attach_method_relationship( &mut self, - did_url: &WasmDIDUrl, + didUrl: &WasmDIDUrl, relationship: WasmMethodRelationship, ) -> Result { self .0 - .attach_method_relationship(&did_url.0, relationship.into()) + .attach_method_relationship(&didUrl.0, relationship.into()) .wasm_result() } /// Detaches the given relationship from the given method, if the method exists. + #[allow(non_snake_case)] #[wasm_bindgen(js_name = detachMethodRelationship)] pub fn detach_method_relationship( &mut self, - did_url: &WasmDIDUrl, + didUrl: &WasmDIDUrl, relationship: WasmMethodRelationship, ) -> Result { self .0 - .detach_method_relationship(&did_url.0, relationship.into()) + .detach_method_relationship(&didUrl.0, relationship.into()) .wasm_result() } @@ -632,6 +624,22 @@ impl WasmDocument { Ok(()) } + /// Sets a custom property in the document metadata. + /// If the value is set to `null`, the custom property will be removed. + #[wasm_bindgen(js_name = setMetadataPropertyUnchecked)] + pub fn set_metadata_property_unchecked(&mut self, key: String, value: &JsValue) -> Result<()> { + let value: Option = value.into_serde().wasm_result()?; + match value { + Some(value) => { + self.0.metadata.properties.insert(key, value); + } + None => { + self.0.metadata.properties.remove(&key); + } + } + Ok(()) + } + /// Returns a copy of the proof. #[wasm_bindgen] pub fn proof(&self) -> Option { @@ -681,11 +689,8 @@ extern "C" { #[wasm_bindgen(typescript_type = "DIDUrl | string")] pub type UDIDUrlQuery; - #[wasm_bindgen(typescript_type = "string | string[] | null")] - pub type UOneOrManyUrl; - #[wasm_bindgen(typescript_type = "DID | DID[] | null")] - pub type UOneOrManyDID; + pub type OptionOneOrManyDID; #[wasm_bindgen(typescript_type = "DID[]")] pub type ArrayDID; @@ -695,7 +700,4 @@ extern "C" { #[wasm_bindgen(typescript_type = "VerificationMethod[]")] pub type ArrayVerificationMethods; - - #[wasm_bindgen(typescript_type = "Timestamp | undefined")] - pub type OptionTimestamp; } diff --git a/bindings/wasm/src/did/wasm_document_metadata.rs b/bindings/wasm/src/did/wasm_document_metadata.rs index 27f3987b79..bea7838efb 100644 --- a/bindings/wasm/src/did/wasm_document_metadata.rs +++ b/bindings/wasm/src/did/wasm_document_metadata.rs @@ -4,7 +4,9 @@ use identity_iota::iota_core::IotaDocumentMetadata; use wasm_bindgen::prelude::*; +use crate::common::MapStringAny; use crate::common::WasmTimestamp; +use crate::error::Result; // ============================================================================= // ============================================================================= @@ -29,10 +31,17 @@ impl WasmDocumentMetadata { self.0.updated.map(WasmTimestamp::from) } - #[wasm_bindgen(getter = previousMessageId)] + /// Returns a copy of the previous message identifier. + #[wasm_bindgen(js_name = previousMessageId)] pub fn previous_message_id(&self) -> String { self.0.previous_message_id.to_string() } + + /// Returns a copy of the custom metadata properties. + #[wasm_bindgen] + pub fn properties(&self) -> Result { + MapStringAny::try_from(&self.0.properties) + } } impl_wasm_json!(WasmDocumentMetadata, DocumentMetadata); diff --git a/bindings/wasm/src/did/wasm_service.rs b/bindings/wasm/src/did/wasm_service.rs index 3cc11c92b5..5de7cd44c7 100644 --- a/bindings/wasm/src/did/wasm_service.rs +++ b/bindings/wasm/src/did/wasm_service.rs @@ -25,11 +25,13 @@ pub struct WasmService(pub(crate) IotaService); #[wasm_bindgen(js_class = Service)] impl WasmService { #[wasm_bindgen(constructor)] - pub fn new(service: IService) -> Result { + pub fn new(service: IIotaService) -> Result { let id: IotaDIDUrl = service.id().into_serde().wasm_result()?; + + let base_service: &IService = service.as_ref(); let types: OneOrMany = service.type_().into_serde().wasm_result()?; - let service_endpoint: ServiceEndpoint = deserialize_map_or_any(&service.service_endpoint())?; - let properties: Option = deserialize_map_or_any(&service.properties())?; + let service_endpoint: ServiceEndpoint = deserialize_map_or_any(&base_service.service_endpoint())?; + let properties: Option = deserialize_map_or_any(&base_service.properties())?; IotaService::builder(properties.unwrap_or_default()) .id(id) @@ -62,7 +64,7 @@ impl WasmService { /// Returns a copy of the `Service` endpoint. #[wasm_bindgen(js_name = serviceEndpoint)] pub fn service_endpoint(&self) -> UServiceEndpoint { - service_endpoint_to_js_value(self.0.service_endpoint()) + UServiceEndpoint::from(self.0.service_endpoint()) } /// Returns a copy of the custom properties on the `Service`. @@ -81,29 +83,37 @@ impl From for WasmService { } } -pub(crate) fn service_endpoint_to_js_value(endpoint: &ServiceEndpoint) -> UServiceEndpoint { - match endpoint { - // string - ServiceEndpoint::One(url) => JsValue::from_str(url.as_str()).unchecked_into::(), - // string[] - ServiceEndpoint::Set(set) => set - .iter() - .map(|url| JsValue::from_str(url.as_str())) - .collect::() - .unchecked_into::(), - // Map - ServiceEndpoint::Map(map) => { - let js_map: js_sys::Map = js_sys::Map::new(); - for (key, urls) in map.into_iter() { - js_map.set( - &JsValue::from_str(key.as_str()), - &urls - .iter() - .map(|url| JsValue::from_str(url.as_str())) - .collect::(), - ); +impl From for UServiceEndpoint { + fn from(endpoint: ServiceEndpoint) -> Self { + UServiceEndpoint::from(&endpoint) + } +} + +impl From<&ServiceEndpoint> for UServiceEndpoint { + fn from(endpoint: &ServiceEndpoint) -> Self { + match endpoint { + // string + ServiceEndpoint::One(url) => JsValue::from_str(url.as_str()).unchecked_into::(), + // string[] + ServiceEndpoint::Set(set) => set + .iter() + .map(|url| JsValue::from_str(url.as_str())) + .collect::() + .unchecked_into::(), + // Map + ServiceEndpoint::Map(map) => { + let js_map: js_sys::Map = js_sys::Map::new(); + for (key, urls) in map.into_iter() { + js_map.set( + &JsValue::from_str(key.as_str()), + &urls + .iter() + .map(|url| JsValue::from_str(url.as_str())) + .collect::(), + ); + } + js_map.unchecked_into::() } - js_map.unchecked_into::() } } } @@ -116,11 +126,31 @@ extern "C" { #[wasm_bindgen] extern "C" { - #[wasm_bindgen(typescript_type = "IService")] - pub type IService; + #[wasm_bindgen(typescript_type = "IIotaService", extends = IService)] + pub type IIotaService; #[wasm_bindgen(method, getter)] - pub fn id(this: &IService) -> JsValue; + pub fn id(this: &IIotaService) -> JsValue; +} + +#[wasm_bindgen(typescript_custom_section)] +const I_IOTA_SERVICE: &'static str = r#" +/** + * Holds options to create a new `IotaService`. + */ +interface IIotaService extends IService { + /** + * Identifier of the service. + * + * Must be a valid DIDUrl with a fragment. + */ + readonly id: DIDUrl | string; +}"#; + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(typescript_type = "IService")] + pub type IService; #[wasm_bindgen(method, getter, js_name = type)] pub fn type_(this: &IService) -> JsValue; @@ -135,16 +165,9 @@ extern "C" { #[wasm_bindgen(typescript_custom_section)] const I_SERVICE: &'static str = r#" /** - * Holds options to create a new `Service`. + * Base `Service` properties. */ interface IService { - /** - * Identifier of the service. - * - * Must be a valid DIDUrl with a fragment. - */ - readonly id: DIDUrl | string; - /** * Type of service. * diff --git a/bindings/wasm/src/did/wasm_verification_method.rs b/bindings/wasm/src/did/wasm_verification_method.rs index 8f48858e03..b6351a51ec 100644 --- a/bindings/wasm/src/did/wasm_verification_method.rs +++ b/bindings/wasm/src/did/wasm_verification_method.rs @@ -18,34 +18,35 @@ pub struct WasmVerificationMethod(pub(crate) IotaVerificationMethod); #[wasm_bindgen(js_class = VerificationMethod)] impl WasmVerificationMethod { - /// Creates a new `VerificationMethod` object from the given `did` and public key. + /// Creates a new `VerificationMethod` from the given `did` and public key. + #[allow(non_snake_case)] #[wasm_bindgen(constructor)] pub fn new( did: &WasmDID, - key_type: WasmKeyType, - public_key: Vec, + keyType: WasmKeyType, + publicKey: Vec, fragment: String, ) -> Result { - let public_key: PublicKey = PublicKey::from(public_key); - IotaVerificationMethod::new(did.0.clone(), key_type.into(), &public_key, &fragment) + let public_key: PublicKey = PublicKey::from(publicKey); + IotaVerificationMethod::new(did.0.clone(), keyType.into(), &public_key, &fragment) .map(Self) .wasm_result() } - /// Returns a copy of the `id` `DIDUrl` of the `VerificationMethod` object. + /// Returns a copy of the `id` `DIDUrl` of the `VerificationMethod`. #[wasm_bindgen] pub fn id(&self) -> WasmDIDUrl { WasmDIDUrl::from(self.0.id().clone()) } - /// Returns a copy of the `controller` `DID` of the `VerificationMethod` object. + /// Returns a copy of the `controller` `DID` of the `VerificationMethod`. #[wasm_bindgen] pub fn controller(&self) -> WasmDID { WasmDID::from(self.0.controller().clone()) } - /// Sets the `controller` `DID` of the `VerificationMethod` object. - #[wasm_bindgen(js_name = SetController)] + /// Sets the `controller` `DID` of the `VerificationMethod`. + #[wasm_bindgen(js_name = setController)] pub fn set_controller(&mut self, did: &WasmDID) { *self.0.controller_mut() = did.0.clone(); } diff --git a/bindings/wasm/src/error.rs b/bindings/wasm/src/error.rs index ed0cbf80cb..512f44b942 100644 --- a/bindings/wasm/src/error.rs +++ b/bindings/wasm/src/error.rs @@ -97,7 +97,8 @@ impl_wasm_error_from!( identity_iota::did::Error, identity_iota::did::DIDError, identity_iota::iota_core::Error, - identity_iota::credential::ValidationError + identity_iota::credential::ValidationError, + identity_stardust::Error ); // Similar to `impl_wasm_error_from`, but uses the types name instead of requiring/calling Into &'static str diff --git a/bindings/wasm/src/lib.rs b/bindings/wasm/src/lib.rs index 38ac329906..64fd474c6e 100644 --- a/bindings/wasm/src/lib.rs +++ b/bindings/wasm/src/lib.rs @@ -17,16 +17,15 @@ use wasm_bindgen::prelude::*; #[macro_use] mod macros; -#[macro_use] -pub mod error; - pub mod account; pub mod chain; pub mod common; pub mod credential; pub mod crypto; pub mod did; +pub mod error; pub mod revocation; +pub mod stardust; pub mod tangle; /// Initializes the console error panic hook for better error messages diff --git a/bindings/wasm/src/revocation/bitmap.rs b/bindings/wasm/src/revocation/bitmap.rs index bb6b0a4867..efef9111f9 100644 --- a/bindings/wasm/src/revocation/bitmap.rs +++ b/bindings/wasm/src/revocation/bitmap.rs @@ -7,7 +7,6 @@ use identity_iota::did::RevocationBitmap; use identity_iota::did::ServiceEndpoint; use wasm_bindgen::prelude::*; -use crate::did::service_endpoint_to_js_value; use crate::did::UServiceEndpoint; use crate::error::Result; use crate::error::WasmError; @@ -65,7 +64,7 @@ impl WasmRevocationBitmap { /// Return the bitmap as a data url embedded in a service endpoint. #[wasm_bindgen(js_name = toEndpoint)] pub fn to_enpdoint(&self) -> Result { - Ok(service_endpoint_to_js_value(&self.0.to_endpoint().wasm_result()?)) + self.0.to_endpoint().map(UServiceEndpoint::from).wasm_result() } /// Construct a `RevocationBitmap` from a data `url`. diff --git a/bindings/wasm/src/stardust/mod.rs b/bindings/wasm/src/stardust/mod.rs new file mode 100644 index 0000000000..1f7eb0d5c6 --- /dev/null +++ b/bindings/wasm/src/stardust/mod.rs @@ -0,0 +1,18 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +pub use stardust_did::WasmStardustDID; +pub use stardust_did_url::WasmStardustDIDUrl; +pub use stardust_document::WasmStardustDocument; +pub use stardust_document_metadata::WasmStardustDocumentMetadata; +pub use stardust_service::WasmStardustService; +pub use stardust_verification_method::WasmStardustVerificationMethod; +pub use state_metadata_encoding::WasmStateMetadataEncoding; + +mod stardust_did; +mod stardust_did_url; +mod stardust_document; +mod stardust_document_metadata; +mod stardust_service; +mod stardust_verification_method; +mod state_metadata_encoding; diff --git a/bindings/wasm/src/stardust/stardust_did.rs b/bindings/wasm/src/stardust/stardust_did.rs new file mode 100644 index 0000000000..559d96a831 --- /dev/null +++ b/bindings/wasm/src/stardust/stardust_did.rs @@ -0,0 +1,161 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use identity_iota::did::DIDError; +use identity_iota::did::DID; +use identity_stardust::NetworkName; +use identity_stardust::StardustDID; +use wasm_bindgen::prelude::*; + +use crate::error::Result; +use crate::error::WasmResult; +use crate::stardust::WasmStardustDIDUrl; + +/// A DID conforming to the IOTA UTXO DID method specification. +/// +/// @typicalname did +#[wasm_bindgen(js_name = StardustDID, inspectable)] +pub struct WasmStardustDID(pub(crate) StardustDID); + +#[wasm_bindgen(js_class = StardustDID)] +impl WasmStardustDID { + /// The IOTA UTXO DID method name (`"stardust"`). + // TODO: This will be changed to `iota` in the future. + #[wasm_bindgen(getter = METHOD)] + pub fn static_method() -> String { + StardustDID::METHOD.to_owned() + } + + /// The default Tangle network (`"main"`). + #[wasm_bindgen(getter = DEFAULT_NETWORK)] + pub fn static_default_network() -> String { + StardustDID::DEFAULT_NETWORK.to_owned() + } + + // =========================================================================== + // Constructors + // =========================================================================== + + /// Constructs a new `StardustDID` from a byte representation of the tag and the given + /// network name. + /// + /// See also {@link StardustDID.placeholder}. + #[wasm_bindgen(constructor)] + pub fn new(bytes: &[u8], network: String) -> Result { + let network_name: NetworkName = NetworkName::try_from(network).wasm_result()?; + let tag_bytes: &[u8; 32] = bytes + .try_into() + .map_err(|_| DIDError::Other("invalid bytes length for StardustDID tag, expected 32")) + .wasm_result()?; + Ok(Self::from(StardustDID::new(tag_bytes, &network_name))) + } + + /// Creates a new placeholder [`StardustDID`] with the given network name. + /// + /// E.g. `did:stardust:smr:0x0000000000000000000000000000000000000000000000000000000000000000`. + #[wasm_bindgen] + pub fn placeholder(network: String) -> Result { + let network_name: NetworkName = NetworkName::try_from(network).wasm_result()?; + Ok(Self::from(StardustDID::placeholder(&network_name))) + } + + /// Parses a `StardustDID` from the input string. + #[wasm_bindgen] + pub fn parse(input: &str) -> Result { + StardustDID::parse(input).map(Self).wasm_result() + } + + // =========================================================================== + // Properties + // =========================================================================== + + /// Returns the Tangle network name of the `StardustDID`. + #[wasm_bindgen(js_name = networkStr)] + pub fn network_str(&self) -> String { + self.0.network_str().to_owned() + } + + /// Returns a copy of the unique tag of the `StardustDID`. + #[wasm_bindgen] + pub fn tag(&self) -> String { + self.0.tag().to_owned() + } + + // =========================================================================== + // DID trait + // =========================================================================== + + /// Returns the `DID` scheme. + /// + /// E.g. + /// - `"did:example:12345678" -> "did"` + /// - `"did:iota:main:12345678" -> "did"` + #[wasm_bindgen] + pub fn scheme(&self) -> String { + self.0.scheme().to_owned() + } + + /// Returns the `DID` authority: the method name and method-id. + /// + /// E.g. + /// - `"did:example:12345678" -> "example:12345678"` + /// - `"did:iota:main:12345678" -> "iota:main:12345678"` + #[wasm_bindgen] + pub fn authority(&self) -> String { + self.0.authority().to_owned() + } + + /// Returns the `DID` method name. + /// + /// E.g. + /// - `"did:example:12345678" -> "example"` + /// - `"did:iota:main:12345678" -> "iota"` + #[wasm_bindgen] + pub fn method(&self) -> String { + self.0.method().to_owned() + } + + /// Returns the `DID` method-specific ID. + /// + /// E.g. + /// - `"did:example:12345678" -> "12345678"` + /// - `"did:iota:main:12345678" -> "main:12345678"` + #[wasm_bindgen(js_name = methodId)] + pub fn method_id(&self) -> String { + self.0.method_id().to_owned() + } + + /// Construct a new `DIDUrl` by joining with a relative DID Url string. + #[wasm_bindgen] + pub fn join(&self, segment: &str) -> Result { + self.0.clone().join(segment).wasm_result().map(WasmStardustDIDUrl) + } + + /// Clones the `DID` into a `DIDUrl`. + #[wasm_bindgen(js_name = toUrl)] + pub fn to_url(&self) -> WasmStardustDIDUrl { + WasmStardustDIDUrl::from(self.0.to_url()) + } + + /// Converts the `DID` into a `DIDUrl`, consuming it. + #[wasm_bindgen(js_name = intoUrl)] + pub fn into_url(self) -> WasmStardustDIDUrl { + WasmStardustDIDUrl::from(self.0.into_url()) + } + + /// Returns the `DID` as a string. + #[allow(clippy::inherent_to_string)] + #[wasm_bindgen(js_name = toString)] + pub fn to_string(&self) -> String { + self.0.to_string() + } +} + +impl_wasm_json!(WasmStardustDID, StardustDID); +impl_wasm_clone!(WasmStardustDID, StardustDID); + +impl From for WasmStardustDID { + fn from(did: StardustDID) -> Self { + Self(did) + } +} diff --git a/bindings/wasm/src/stardust/stardust_did_url.rs b/bindings/wasm/src/stardust/stardust_did_url.rs new file mode 100644 index 0000000000..e05ae5a111 --- /dev/null +++ b/bindings/wasm/src/stardust/stardust_did_url.rs @@ -0,0 +1,106 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use identity_stardust::StardustDIDUrl; +use wasm_bindgen::prelude::*; + +use crate::error::Result; +use crate::error::WasmResult; +use crate::stardust::WasmStardustDID; + +/// A DID URL conforming to the IOTA Stardust UTXO DID method specification. +#[wasm_bindgen(js_name = StardustDIDUrl, inspectable)] +pub struct WasmStardustDIDUrl(pub(crate) StardustDIDUrl); + +#[wasm_bindgen(js_class = StardustDIDUrl)] +impl WasmStardustDIDUrl { + /// Parses a `StardustDIDUrl` from the input string. + #[wasm_bindgen] + pub fn parse(input: &str) -> Result { + StardustDIDUrl::parse(input).map(WasmStardustDIDUrl).wasm_result() + } + + /// Return a copy of the `StardustDID` section of the `StardustDIDUrl`. + #[wasm_bindgen] + pub fn did(&self) -> WasmStardustDID { + WasmStardustDID::from(self.0.did().clone()) + } + + /// Return a copy of the relative DID Url as a string, including only the path, query, and fragment. + #[wasm_bindgen(js_name = urlStr)] + pub fn url_str(&self) -> String { + self.0.url().to_string() + } + + /// Returns a copy of the `StardustDIDUrl` method fragment, if any. Excludes the leading '#'. + #[wasm_bindgen] + pub fn fragment(&self) -> Option { + self.0.fragment().map(str::to_owned) + } + + /// Sets the `fragment` component of the `StardustDIDUrl`. + #[wasm_bindgen(js_name = setFragment)] + pub fn set_fragment(&mut self, value: Option) -> Result<()> { + self.0.set_fragment(value.as_deref()).wasm_result() + } + + /// Returns a copy of the `StardustDIDUrl` path. + #[wasm_bindgen] + pub fn path(&self) -> Option { + self.0.path().map(str::to_owned) + } + + /// Sets the `path` component of the `StardustDIDUrl`. + #[wasm_bindgen(js_name = setPath)] + pub fn set_path(&mut self, value: Option) -> Result<()> { + self.0.set_path(value.as_deref()).wasm_result() + } + + /// Returns a copy of the `StardustDIDUrl` method query, if any. Excludes the leading '?'. + #[wasm_bindgen] + pub fn query(&self) -> Option { + self.0.query().map(str::to_owned) + } + + /// Sets the `query` component of the `StardustDIDUrl`. + #[wasm_bindgen(js_name = setQuery)] + pub fn set_query(&mut self, value: Option) -> Result<()> { + self.0.set_query(value.as_deref()).wasm_result() + } + + /// Append a string representing a path, query, and/or fragment, returning a new `StardustDIDUrl`. + /// + /// Must begin with a valid delimiter character: '/', '?', '#'. Overwrites the existing URL + /// segment and any following segments in order of path, query, then fragment. + /// + /// I.e. + /// - joining a path will clear the query and fragment. + /// - joining a query will clear the fragment. + /// - joining a fragment will only overwrite the fragment. + #[wasm_bindgen] + pub fn join(&self, segment: &str) -> Result { + self.0.join(segment).map(WasmStardustDIDUrl::from).wasm_result() + } + + /// Returns the `StardustDIDUrl` as a string. + #[allow(clippy::inherent_to_string)] + #[wasm_bindgen(js_name = toString)] + pub fn to_string(&self) -> String { + self.0.to_string() + } +} + +impl_wasm_json!(WasmStardustDIDUrl, StardustDIDUrl); +impl_wasm_clone!(WasmStardustDIDUrl, StardustDIDUrl); + +impl From for WasmStardustDIDUrl { + fn from(did_url: StardustDIDUrl) -> Self { + Self(did_url) + } +} + +impl From for StardustDIDUrl { + fn from(wasm_did_url: WasmStardustDIDUrl) -> Self { + wasm_did_url.0 + } +} diff --git a/bindings/wasm/src/stardust/stardust_document.rs b/bindings/wasm/src/stardust/stardust_document.rs new file mode 100644 index 0000000000..9595ff6713 --- /dev/null +++ b/bindings/wasm/src/stardust/stardust_document.rs @@ -0,0 +1,510 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use identity_iota::core::OneOrMany; +use identity_iota::core::OrderedSet; +use identity_iota::core::Timestamp; +use identity_iota::core::Url; +use identity_iota::credential::Credential; +use identity_iota::credential::Presentation; +use identity_iota::crypto::PrivateKey; +use identity_iota::crypto::ProofOptions; +use identity_iota::did::verifiable::VerifiableProperties; +use identity_iota::did::Document; +use identity_iota::did::MethodScope; +use identity_stardust::NetworkName; +use identity_stardust::StardustDID; +use identity_stardust::StardustDocument; +use identity_stardust::StardustVerificationMethod; +use identity_stardust::StateMetadataEncoding; +use wasm_bindgen::prelude::*; +use wasm_bindgen::JsCast; + +use crate::common::ArrayString; +use crate::common::MapStringAny; +use crate::common::OptionOneOrManyString; +use crate::common::OptionTimestamp; +use crate::common::UOneOrManyNumber; +use crate::common::WasmTimestamp; +use crate::credential::WasmCredential; +use crate::credential::WasmPresentation; +use crate::crypto::WasmProofOptions; +use crate::did::RefMethodScope; +use crate::did::WasmMethodRelationship; +use crate::did::WasmMethodScope; +use crate::did::WasmVerifierOptions; +use crate::error::Result; +use crate::error::WasmResult; +use crate::stardust::WasmStardustDID; +use crate::stardust::WasmStardustDIDUrl; +use crate::stardust::WasmStardustDocumentMetadata; +use crate::stardust::WasmStardustService; +use crate::stardust::WasmStardustVerificationMethod; +use crate::stardust::WasmStateMetadataEncoding; + +// ============================================================================= +// ============================================================================= + +#[wasm_bindgen(js_name = StardustDocument, inspectable)] +pub struct WasmStardustDocument(pub(crate) StardustDocument); + +#[wasm_bindgen(js_class = StardustDocument)] +impl WasmStardustDocument { + // =========================================================================== + // Constructors + // =========================================================================== + + /// Constructs an empty DID Document with a {@link StardustDID.placeholder} identifier + /// for the given `network`. + #[wasm_bindgen(constructor)] + pub fn new(network: String) -> Result { + let network_name: NetworkName = NetworkName::try_from(network).wasm_result()?; + Ok(WasmStardustDocument::from(StardustDocument::new(&network_name))) + } + + /// Constructs an empty DID Document with the given identifier. + #[wasm_bindgen(js_name = newWithId)] + pub fn new_with_id(id: &WasmStardustDID) -> WasmStardustDocument { + let did: StardustDID = id.0.clone(); + WasmStardustDocument::from(StardustDocument::new_with_id(did)) + } + + // =========================================================================== + // Properties + // =========================================================================== + + /// Returns a copy of the DID Document `id`. + #[wasm_bindgen] + pub fn id(&self) -> WasmStardustDID { + WasmStardustDID::from(self.0.id().clone()) + } + + /// Returns a copy of the list of document controllers. + /// + /// NOTE: controllers are determined by the `state_controller` unlock condition of the output + /// during resolution and are omitted when publishing. + #[wasm_bindgen] + pub fn controller(&self) -> ArrayStardustDID { + match self.0.controller() { + Some(controllers) => controllers + .iter() + .cloned() + .map(WasmStardustDID::from) + .map(JsValue::from) + .collect::() + .unchecked_into::(), + None => js_sys::Array::new().unchecked_into::(), + } + } + + /// Returns a copy of the document's `alsoKnownAs` set. + #[wasm_bindgen(js_name = alsoKnownAs)] + pub fn also_known_as(&self) -> ArrayString { + self + .0 + .also_known_as() + .iter() + .map(|url| url.to_string()) + .map(JsValue::from) + .collect::() + .unchecked_into::() + } + + /// Sets the `alsoKnownAs` property in the DID document. + #[wasm_bindgen(js_name = setAlsoKnownAs)] + pub fn set_also_known_as(&mut self, urls: &OptionOneOrManyString) -> Result<()> { + let urls: Option> = urls.into_serde().wasm_result()?; + let mut urls_set: OrderedSet = OrderedSet::new(); + if let Some(urls) = urls { + for url in urls.into_vec() { + urls_set.append(Url::parse(url).wasm_result()?); + } + } + *self.0.also_known_as_mut() = urls_set; + Ok(()) + } + + /// Returns a copy of the custom DID Document properties. + #[wasm_bindgen] + pub fn properties(&self) -> Result { + MapStringAny::try_from(self.0.properties()) + } + + /// Sets a custom property in the DID Document. + /// If the value is set to `null`, the custom property will be removed. + /// + /// ### WARNING + /// This method can overwrite existing properties like `id` and result in an invalid document. + #[wasm_bindgen(js_name = setPropertyUnchecked)] + pub fn set_property_unchecked(&mut self, key: String, value: &JsValue) -> Result<()> { + let value: Option = value.into_serde().wasm_result()?; + match value { + Some(value) => { + self.0.properties_mut().insert(key, value); + } + None => { + self.0.properties_mut().remove(&key); + } + } + Ok(()) + } + + // =========================================================================== + // Services + // =========================================================================== + + /// Return a set of all {@link StardustService} in the document. + #[wasm_bindgen] + pub fn service(&self) -> ArrayStardustService { + self + .0 + .service() + .iter() + .cloned() + .map(WasmStardustService) + .map(JsValue::from) + .collect::() + .unchecked_into::() + } + + /// Add a new {@link StardustService} to the document. + /// + /// Returns `true` if the service was added. + #[wasm_bindgen(js_name = insertService)] + pub fn insert_service(&mut self, service: &WasmStardustService) -> bool { + self.0.insert_service(service.0.clone()) + } + + /// Remove a {@link StardustService} identified by the given {@link DIDUrl} from the document. + /// + /// Returns `true` if a service was removed. + #[wasm_bindgen(js_name = removeService)] + pub fn remove_service(&mut self, did: &WasmStardustDIDUrl) -> bool { + self.0.remove_service(&did.0) + } + + /// Returns the first {@link StardustService} with an `id` property matching the provided `query`, + /// if present. + #[wasm_bindgen(js_name = resolveService)] + pub fn resolve_service(&self, query: &UStardustDIDUrlQuery) -> Option { + let service_query: String = query.into_serde().ok()?; + self + .0 + .resolve_service(&service_query) + .cloned() + .map(WasmStardustService::from) + } + + // =========================================================================== + // Verification Methods + // =========================================================================== + + /// Returns a list of all {@link StardustVerificationMethod} in the DID Document. + #[wasm_bindgen] + pub fn methods(&self) -> ArrayStardustVerificationMethods { + self + .0 + .methods() + .cloned() + .map(WasmStardustVerificationMethod::from) + .map(JsValue::from) + .collect::() + .unchecked_into::() + } + + /// Adds a new `method` to the document in the given `scope`. + #[wasm_bindgen(js_name = insertMethod)] + pub fn insert_method(&mut self, method: &WasmStardustVerificationMethod, scope: &WasmMethodScope) -> Result<()> { + self.0.insert_method(method.0.clone(), scope.0).wasm_result()?; + Ok(()) + } + + /// Removes all references to the specified Verification Method. + #[wasm_bindgen(js_name = removeMethod)] + pub fn remove_method(&mut self, did: &WasmStardustDIDUrl) -> Result<()> { + self.0.remove_method(&did.0).wasm_result() + } + + /// Returns a copy of the first verification method with an `id` property + /// matching the provided `query` and the verification relationship + /// specified by `scope`, if present. + #[wasm_bindgen(js_name = resolveMethod)] + pub fn resolve_method( + &self, + query: &UStardustDIDUrlQuery, + scope: Option, + ) -> Result> { + let method_query: String = query.into_serde().wasm_result()?; + let method_scope: Option = scope.map(|js| js.into_serde().wasm_result()).transpose()?; + + let method: Option<&StardustVerificationMethod> = self.0.resolve_method(&method_query, method_scope); + Ok(method.cloned().map(WasmStardustVerificationMethod)) + } + + /// Attaches the relationship to the given method, if the method exists. + /// + /// Note: The method needs to be in the set of verification methods, + /// so it cannot be an embedded one. + #[allow(non_snake_case)] + #[wasm_bindgen(js_name = attachMethodRelationship)] + pub fn attach_method_relationship( + &mut self, + didUrl: &WasmStardustDIDUrl, + relationship: WasmMethodRelationship, + ) -> Result { + self + .0 + .attach_method_relationship(&didUrl.0, relationship.into()) + .wasm_result() + } + + /// Detaches the given relationship from the given method, if the method exists. + #[allow(non_snake_case)] + #[wasm_bindgen(js_name = detachMethodRelationship)] + pub fn detach_method_relationship( + &mut self, + didUrl: &WasmStardustDIDUrl, + relationship: WasmMethodRelationship, + ) -> Result { + self + .0 + .detach_method_relationship(&didUrl.0, relationship.into()) + .wasm_result() + } + + // =========================================================================== + // Signatures + // =========================================================================== + + /// Creates a signature for the given `Credential` with the specified DID Document + /// Verification Method. + #[allow(non_snake_case)] + #[wasm_bindgen(js_name = signCredential)] + pub fn sign_credential( + &self, + credential: &WasmCredential, + privateKey: Vec, + methodQuery: &UStardustDIDUrlQuery, + options: &WasmProofOptions, + ) -> Result { + let mut data: Credential = credential.0.clone(); + let private_key: PrivateKey = privateKey.into(); + let method_query: String = methodQuery.into_serde().wasm_result()?; + let options: ProofOptions = options.0.clone(); + + self + .0 + .sign_data(&mut data, &private_key, &method_query, options) + .wasm_result()?; + Ok(WasmCredential::from(data)) + } + + /// Creates a signature for the given `Presentation` with the specified DID Document + /// Verification Method. + #[allow(non_snake_case)] + #[wasm_bindgen(js_name = signPresentation)] + pub fn sign_presentation( + &self, + presentation: &WasmPresentation, + privateKey: Vec, + methodQuery: &UStardustDIDUrlQuery, + options: &WasmProofOptions, + ) -> Result { + let mut data: Presentation = presentation.0.clone(); + let private_key: PrivateKey = privateKey.into(); + let method_query: String = methodQuery.into_serde().wasm_result()?; + let options: ProofOptions = options.0.clone(); + + self + .0 + .sign_data(&mut data, &private_key, &method_query, options) + .wasm_result()?; + Ok(WasmPresentation::from(data)) + } + + /// Creates a signature for the given `data` with the specified DID Document + /// Verification Method. + /// + /// NOTE: use `signSelf` or `signDocument` for DID Documents. + #[allow(non_snake_case)] + #[wasm_bindgen(js_name = signData)] + pub fn sign_data( + &self, + data: &JsValue, + privateKey: Vec, + methodQuery: &UStardustDIDUrlQuery, + options: &WasmProofOptions, + ) -> Result { + let mut data: VerifiableProperties = data.into_serde().wasm_result()?; + let private_key: PrivateKey = privateKey.into(); + let method_query: String = methodQuery.into_serde().wasm_result()?; + let options: ProofOptions = options.0.clone(); + + self + .0 + .sign_data(&mut data, &private_key, &method_query, options) + .wasm_result()?; + + JsValue::from_serde(&data).wasm_result() + } + + // =========================================================================== + // Verification + // =========================================================================== + + /// Verifies the authenticity of `data` using the target verification method. + #[wasm_bindgen(js_name = verifyData)] + pub fn verify_data(&self, data: &JsValue, options: &WasmVerifierOptions) -> Result { + let data: VerifiableProperties = data.into_serde().wasm_result()?; + Ok(self.0.verify_data(&data, &options.0).is_ok()) + } + + // =========================================================================== + // Publishing + // =========================================================================== + + /// Serializes the document for inclusion in an Alias Output's state metadata + /// with the default {@link StateMetadataEncoding}. + #[wasm_bindgen] + pub fn pack(&self) -> Result> { + self.0.clone().pack().wasm_result() + } + + /// Serializes the document for inclusion in an Alias Output's state metadata. + #[wasm_bindgen(js_name = packWithEncoding)] + pub fn pack_with_encoding(&self, encoding: WasmStateMetadataEncoding) -> Result> { + self + .0 + .clone() + .pack_with_encoding(StateMetadataEncoding::from(encoding)) + .wasm_result() + } + + /// Deserializes the document from the state metadata bytes of an Alias Output. + /// + /// NOTE: `did` is required since it is omitted from the serialized DID Document and + /// cannot be inferred from the state metadata. It also indicates the network, which is not + /// encoded in the `AliasId` alone. + #[allow(non_snake_case)] + #[wasm_bindgen] + pub fn unpack(did: &WasmStardustDID, stateMetadata: &[u8]) -> Result { + StardustDocument::unpack(&did.0, stateMetadata) + .map(WasmStardustDocument) + .wasm_result() + } + + // TODO: unpack_from_output/unpackFromOutput ? Feature-gated method, do we need an equivalent? + + // =========================================================================== + // Metadata + // =========================================================================== + + /// Returns a copy of the metadata associated with this document. + /// + /// NOTE: Copies all the metadata. See also `metadataCreated`, `metadataUpdated`, + /// `metadataPreviousMessageId`, `metadataProof` if only a subset of the metadata required. + #[wasm_bindgen] + pub fn metadata(&self) -> WasmStardustDocumentMetadata { + WasmStardustDocumentMetadata::from(self.0.metadata.clone()) + } + + /// Returns a copy of the timestamp of when the DID document was created. + #[wasm_bindgen(js_name = metadataCreated)] + pub fn metadata_created(&self) -> Option { + self.0.metadata.created.map(WasmTimestamp::from) + } + + /// Sets the timestamp of when the DID document was created. + #[wasm_bindgen(js_name = setMetadataCreated)] + pub fn set_metadata_created(&mut self, timestamp: OptionTimestamp) -> Result<()> { + let timestamp: Option = timestamp.into_serde().wasm_result()?; + self.0.metadata.created = timestamp; + Ok(()) + } + + /// Returns a copy of the timestamp of the last DID document update. + #[wasm_bindgen(js_name = metadataUpdated)] + pub fn metadata_updated(&self) -> Option { + self.0.metadata.updated.map(WasmTimestamp::from) + } + + /// Sets the timestamp of the last DID document update. + #[wasm_bindgen(js_name = setMetadataUpdated)] + pub fn set_metadata_updated(&mut self, timestamp: OptionTimestamp) -> Result<()> { + let timestamp: Option = timestamp.into_serde().wasm_result()?; + self.0.metadata.updated = timestamp; + Ok(()) + } + + /// Sets a custom property in the document metadata. + /// If the value is set to `null`, the custom property will be removed. + #[wasm_bindgen(js_name = setMetadataPropertyUnchecked)] + pub fn set_metadata_property_unchecked(&mut self, key: String, value: &JsValue) -> Result<()> { + let value: Option = value.into_serde().wasm_result()?; + match value { + Some(value) => { + self.0.metadata.properties.insert(key, value); + } + None => { + self.0.metadata.properties.remove(&key); + } + } + Ok(()) + } + + // =========================================================================== + // Revocation + // =========================================================================== + + /// If the document has a `RevocationBitmap` service identified by `serviceQuery`, + /// revoke all specified `indices`. + #[wasm_bindgen(js_name = revokeCredentials)] + #[allow(non_snake_case)] + pub fn revoke_credentials(&mut self, serviceQuery: &UStardustDIDUrlQuery, indices: UOneOrManyNumber) -> Result<()> { + let query: String = serviceQuery.into_serde().wasm_result()?; + let indices: OneOrMany = indices.into_serde().wasm_result()?; + + self.0.revoke_credentials(&query, indices.as_slice()).wasm_result() + } + + /// If the document has a `RevocationBitmap` service identified by `serviceQuery`, + /// unrevoke all specified `indices`. + #[wasm_bindgen(js_name = unrevokeCredentials)] + #[allow(non_snake_case)] + pub fn unrevoke_credentials(&mut self, serviceQuery: &UStardustDIDUrlQuery, indices: UOneOrManyNumber) -> Result<()> { + let query: String = serviceQuery.into_serde().wasm_result()?; + let indices: OneOrMany = indices.into_serde().wasm_result()?; + + self.0.unrevoke_credentials(&query, indices.as_slice()).wasm_result() + } +} + +impl_wasm_json!(WasmStardustDocument, StardustDocument); +impl_wasm_clone!(WasmStardustDocument, StardustDocument); + +impl From for WasmStardustDocument { + fn from(document: StardustDocument) -> Self { + Self(document) + } +} + +impl From for StardustDocument { + fn from(wasm_document: WasmStardustDocument) -> Self { + wasm_document.0 + } +} + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(typescript_type = "StardustDIDUrl | string")] + pub type UStardustDIDUrlQuery; + + #[wasm_bindgen(typescript_type = "StardustDID[]")] + pub type ArrayStardustDID; + + #[wasm_bindgen(typescript_type = "StardustService[]")] + pub type ArrayStardustService; + + #[wasm_bindgen(typescript_type = "StardustVerificationMethod[]")] + pub type ArrayStardustVerificationMethods; +} diff --git a/bindings/wasm/src/stardust/stardust_document_metadata.rs b/bindings/wasm/src/stardust/stardust_document_metadata.rs new file mode 100644 index 0000000000..e154420509 --- /dev/null +++ b/bindings/wasm/src/stardust/stardust_document_metadata.rs @@ -0,0 +1,45 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use identity_stardust::StardustDocumentMetadata; +use wasm_bindgen::prelude::*; + +use crate::common::MapStringAny; +use crate::common::WasmTimestamp; +use crate::error::Result; + +/// Additional attributes related to an IOTA DID Document. +#[wasm_bindgen(js_name = StardustDocumentMetadata, inspectable)] +pub struct WasmStardustDocumentMetadata(pub(crate) StardustDocumentMetadata); + +// NOTE: these properties are read-only (no setters) to prevent bugs where a clone of the metadata +// is updated instead of the actual instance in the document. +#[wasm_bindgen(js_class = StardustDocumentMetadata)] +impl WasmStardustDocumentMetadata { + /// Returns a copy of the timestamp of when the DID document was created. + #[wasm_bindgen] + pub fn created(&self) -> Option { + self.0.created.map(WasmTimestamp::from) + } + + /// Returns a copy of the timestamp of the last DID document update. + #[wasm_bindgen] + pub fn updated(&self) -> Option { + self.0.updated.map(WasmTimestamp::from) + } + + /// Returns a copy of the custom metadata properties. + #[wasm_bindgen] + pub fn properties(&self) -> Result { + MapStringAny::try_from(&self.0.properties) + } +} + +impl_wasm_json!(WasmStardustDocumentMetadata, StardustDocumentMetadata); +impl_wasm_clone!(WasmStardustDocumentMetadata, StardustDocumentMetadata); + +impl From for WasmStardustDocumentMetadata { + fn from(metadata: StardustDocumentMetadata) -> Self { + Self(metadata) + } +} diff --git a/bindings/wasm/src/stardust/stardust_service.rs b/bindings/wasm/src/stardust/stardust_service.rs new file mode 100644 index 0000000000..68978766eb --- /dev/null +++ b/bindings/wasm/src/stardust/stardust_service.rs @@ -0,0 +1,106 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use identity_iota::core::OneOrMany; +use identity_iota::did::ServiceEndpoint; +use identity_stardust::StardustDIDUrl; +use identity_stardust::StardustService; +use wasm_bindgen::prelude::*; +use wasm_bindgen::JsCast; + +use crate::common::deserialize_map_or_any; +use crate::common::ArrayString; +use crate::common::MapStringAny; +use crate::did::IService; +use crate::did::UServiceEndpoint; +use crate::error::Result; +use crate::error::WasmResult; +use crate::stardust::WasmStardustDIDUrl; + +/// A `Service` adhering to the IOTA UTXO DID method specification. +#[wasm_bindgen(js_name = StardustService, inspectable)] +pub struct WasmStardustService(pub(crate) StardustService); + +#[wasm_bindgen(js_class = StardustService)] +impl WasmStardustService { + #[wasm_bindgen(constructor)] + pub fn new(service: IStardustService) -> Result { + let id: StardustDIDUrl = service.id().into_serde().wasm_result()?; + + let base_service: &IService = service.as_ref(); + let types: OneOrMany = service.type_().into_serde().wasm_result()?; + let service_endpoint: ServiceEndpoint = deserialize_map_or_any(&base_service.service_endpoint())?; + let properties: Option = deserialize_map_or_any(&base_service.properties())?; + + StardustService::builder(properties.unwrap_or_default()) + .id(id) + .types(types) + .service_endpoint(service_endpoint) + .build() + .map(WasmStardustService) + .wasm_result() + } + + /// Returns a copy of the `Service` id. + #[wasm_bindgen] + pub fn id(&self) -> WasmStardustDIDUrl { + WasmStardustDIDUrl::from(self.0.id().clone()) + } + + /// Returns a copy of the `Service` type. + #[wasm_bindgen(js_name = type)] + pub fn type_(&self) -> ArrayString { + self + .0 + .type_() + .iter() + .cloned() + .map(JsValue::from) + .collect::() + .unchecked_into::() + } + + /// Returns a copy of the `Service` endpoint. + #[wasm_bindgen(js_name = serviceEndpoint)] + pub fn service_endpoint(&self) -> UServiceEndpoint { + UServiceEndpoint::from(self.0.service_endpoint()) + } + + /// Returns a copy of the custom properties on the `Service`. + #[wasm_bindgen] + pub fn properties(&self) -> Result { + MapStringAny::try_from(self.0.properties()) + } +} + +impl_wasm_json!(WasmStardustService, StardustService); +impl_wasm_clone!(WasmStardustService, StardustService); + +impl From for WasmStardustService { + fn from(service: StardustService) -> Self { + Self(service) + } +} + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(typescript_type = "IStardustService", extends = IService)] + pub type IStardustService; + + #[wasm_bindgen(method, getter)] + pub fn id(this: &IStardustService) -> JsValue; +} + +#[wasm_bindgen(typescript_custom_section)] +const I_STARDUST_SERVICE: &'static str = r#" +/** + * Holds options to create a new `StardustService`. + */ +interface IStardustService extends IService { + /** + * Identifier of the service. + * + * Must be a valid DIDUrl with a fragment. + */ + readonly id: StardustDIDUrl | string; +}"#; diff --git a/bindings/wasm/src/stardust/stardust_verification_method.rs b/bindings/wasm/src/stardust/stardust_verification_method.rs new file mode 100644 index 0000000000..47c3d7b9d9 --- /dev/null +++ b/bindings/wasm/src/stardust/stardust_verification_method.rs @@ -0,0 +1,74 @@ +// Copyright 2020-2022 Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use identity_iota::crypto::PublicKey; +use identity_stardust::StardustVerificationMethod; +use wasm_bindgen::prelude::*; + +use crate::crypto::WasmKeyType; +use crate::did::WasmMethodData; +use crate::did::WasmMethodType; +use crate::error::Result; +use crate::error::WasmResult; +use crate::stardust::WasmStardustDID; +use crate::stardust::WasmStardustDIDUrl; + +#[wasm_bindgen(js_name = StardustVerificationMethod, inspectable)] +pub struct WasmStardustVerificationMethod(pub(crate) StardustVerificationMethod); + +#[wasm_bindgen(js_class = StardustVerificationMethod)] +impl WasmStardustVerificationMethod { + /// Creates a new `StardustVerificationMethod` from the given `did` and public key. + #[allow(non_snake_case)] + #[wasm_bindgen(constructor)] + pub fn new( + did: &WasmStardustDID, + keyType: WasmKeyType, + publicKey: Vec, + fragment: String, + ) -> Result { + let public_key: PublicKey = PublicKey::from(publicKey); + StardustVerificationMethod::new(did.0.clone(), keyType.into(), &public_key, &fragment) + .map(Self) + .wasm_result() + } + + /// Returns a reference to the `StardustVerificationMethod` id. + #[wasm_bindgen] + pub fn id(&self) -> WasmStardustDIDUrl { + WasmStardustDIDUrl::from(self.0.id().clone()) + } + + /// Returns a copy of the `controller` `DID` of the `StardustVerificationMethod`. + #[wasm_bindgen] + pub fn controller(&self) -> WasmStardustDID { + WasmStardustDID::from(self.0.controller().clone()) + } + + /// Sets the `controller` `DID` of the `StardustVerificationMethod`. + #[wasm_bindgen(js_name = setController)] + pub fn set_controller(&mut self, did: &WasmStardustDID) { + *self.0.controller_mut() = did.0.clone(); + } + + /// Returns a copy of the `StardustVerificationMethod` type. + #[wasm_bindgen(js_name = type)] + pub fn type_(&self) -> WasmMethodType { + WasmMethodType::from(self.0.type_()) + } + + /// Returns a copy of the `StardustVerificationMethod` public key data. + #[wasm_bindgen] + pub fn data(&self) -> WasmMethodData { + WasmMethodData::from(self.0.data().clone()) + } +} + +impl_wasm_json!(WasmStardustVerificationMethod, StardustVerificationMethod); +impl_wasm_clone!(WasmStardustVerificationMethod, StardustVerificationMethod); + +impl From for WasmStardustVerificationMethod { + fn from(method: StardustVerificationMethod) -> Self { + Self(method) + } +} diff --git a/bindings/wasm/src/stardust/state_metadata_encoding.rs b/bindings/wasm/src/stardust/state_metadata_encoding.rs new file mode 100644 index 0000000000..eb019851da --- /dev/null +++ b/bindings/wasm/src/stardust/state_metadata_encoding.rs @@ -0,0 +1,30 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use identity_stardust::StateMetadataEncoding; +use serde_repr::Deserialize_repr; +use serde_repr::Serialize_repr; +use wasm_bindgen::prelude::*; + +#[wasm_bindgen(js_name = StateMetadataEncoding)] +#[derive(Serialize_repr, Deserialize_repr)] +#[repr(u8)] +pub enum WasmStateMetadataEncoding { + Json = 0, +} + +impl From for WasmStateMetadataEncoding { + fn from(encoding: StateMetadataEncoding) -> Self { + match encoding { + StateMetadataEncoding::Json => Self::Json, + } + } +} + +impl From for StateMetadataEncoding { + fn from(encoding: WasmStateMetadataEncoding) -> Self { + match encoding { + WasmStateMetadataEncoding::Json => Self::Json, + } + } +} diff --git a/bindings/wasm/tests/credentials.ts b/bindings/wasm/tests/credentials.ts index d6a6bc454f..9b155f712a 100644 --- a/bindings/wasm/tests/credentials.ts +++ b/bindings/wasm/tests/credentials.ts @@ -181,7 +181,6 @@ describe('CredentialValidator, PresentationValidator', function () { degreeType: "BachelorDegree", GPA: "4.0" }; - console.log(issuerDID.toString()); const credential = new Credential({ id: "https://example.edu/credentials/3732", type: "UniversityDegreeCredential", diff --git a/bindings/wasm/tests/stardust.ts b/bindings/wasm/tests/stardust.ts new file mode 100644 index 0000000000..a332e97c45 --- /dev/null +++ b/bindings/wasm/tests/stardust.ts @@ -0,0 +1,189 @@ +export {}; + +const assert = require('assert'); +const { + Duration, + KeyType, + MethodScope, + MethodType, + MethodRelationship, + StardustDID, + StardustDocument, + StardustService, + StardustVerificationMethod, + Timestamp, +} = require("../node"); + +const aliasIdBytes = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32]); +const aliasIdHex = "0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"; +const networkName = "smr"; + +describe('StardustDID', function () { + describe('#constructor', function () { + it('should work', () => { + const did = new StardustDID(aliasIdBytes, networkName); + assert.deepStrictEqual(did.toString(), "did:" + StardustDID.METHOD + ":" + networkName + ":" + aliasIdHex); + assert.deepStrictEqual(did.tag(), aliasIdHex); + assert.deepStrictEqual(did.method(), StardustDID.METHOD); + assert.deepStrictEqual(did.networkStr(), networkName); + assert.deepStrictEqual(did.authority(), StardustDID.METHOD + ":" + networkName + ":" + aliasIdHex); + assert.deepStrictEqual(did.methodId(), networkName + ":" + aliasIdHex); + assert.deepStrictEqual(did.scheme(), "did"); + }); + }); + describe('#placeholder()', function () { + it('should be zeroes', () => { + const expectedTag = "0x0000000000000000000000000000000000000000000000000000000000000000"; + const did = StardustDID.placeholder(networkName); + assert.deepStrictEqual(did.toString(), "did:" + StardustDID.METHOD + ":" + networkName + ":" + expectedTag); + assert.deepStrictEqual(did.tag(), expectedTag); + assert.deepStrictEqual(did.method(), StardustDID.METHOD); + assert.deepStrictEqual(did.networkStr(), networkName); + assert.deepStrictEqual(did.authority(), StardustDID.METHOD + ":" + networkName + ":" + expectedTag); + assert.deepStrictEqual(did.methodId(), networkName + ":" + expectedTag); + assert.deepStrictEqual(did.scheme(), "did"); + }); + }); +}); + +describe('StardustDocument', function () { + describe('#constructors', function () { + it('new should generate a placeholder', () => { + const doc = new StardustDocument(networkName); + assert.deepStrictEqual(doc.id().toString(), StardustDID.placeholder(networkName).toString()); + }); + it('newWithId should work', () => { + const did = new StardustDID(aliasIdBytes, networkName); + const doc = StardustDocument.newWithId(did); + assert.deepStrictEqual(doc.id().toString(), did.toString()); + }); + }); + describe('#insert/resolve/removeMethod', function () { + it('should work', async () => { + const doc = new StardustDocument(networkName); + const fragment = "new-method-1"; + const scope = MethodScope.AssertionMethod(); + const method = new StardustVerificationMethod(doc.id(), KeyType.Ed25519, aliasIdBytes, fragment); + + // Add. + doc.insertMethod(method, scope); + // Resolve. + const resolved = doc.resolveMethod(fragment, scope); + assert.deepStrictEqual(resolved.id().fragment(), fragment); + assert.deepStrictEqual(resolved.type().toString(), MethodType.Ed25519VerificationKey2018().toString()); + assert.deepStrictEqual(resolved.controller().toString(), doc.id().toString()); + assert.deepStrictEqual(resolved.data().tryDecode(), aliasIdBytes); + assert.deepStrictEqual(resolved.toJSON(), method.toJSON()); + assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.VerificationMethod()), undefined); + // List. + const list = doc.methods(); + assert.deepStrictEqual(list.length, 1); + assert.deepStrictEqual(list[0].toJSON(), resolved.toJSON()); + // Remove. + doc.removeMethod(resolved.id()); + assert.deepStrictEqual(doc.resolveMethod(fragment), undefined); + assert.deepStrictEqual(doc.resolveMethod(fragment, scope), undefined); + assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.VerificationMethod()), undefined); + assert.deepStrictEqual(doc.methods().length, 0); + }); + }); + describe('#attach/detachMethodRelationship', function () { + it('should work', async () => { + const doc = new StardustDocument(networkName); + const fragment = "new-method-1"; + const method = new StardustVerificationMethod(doc.id(), KeyType.Ed25519, aliasIdBytes, fragment); + doc.insertMethod(method, MethodScope.VerificationMethod()); + assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.VerificationMethod()).toJSON(), method.toJSON()); + + // Attach. + doc.attachMethodRelationship(method.id(), MethodRelationship.Authentication); + assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.VerificationMethod()).toJSON(), method.toJSON()); + assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.Authentication()).toJSON(), method.toJSON()); + assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.AssertionMethod()), undefined); + assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.CapabilityInvocation()), undefined); + assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.CapabilityDelegation()), undefined); + assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.KeyAgreement()), undefined); + + // Detach. + doc.detachMethodRelationship(method.id(), MethodRelationship.Authentication); + assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.VerificationMethod()).toJSON(), method.toJSON()); + assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.Authentication()), undefined); + assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.AssertionMethod()), undefined); + assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.CapabilityInvocation()), undefined); + assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.CapabilityDelegation()), undefined); + assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.KeyAgreement()), undefined); + }); + }); + describe('#insert/resolve/removeService', function () { + it('should work', async () => { + const doc = new StardustDocument(networkName); + + // Add. + const fragment1 = "new-service-1"; + const service = new StardustService({ + id: doc.id().toUrl().join('#' + fragment1), + type: ["LinkedDomains", "ExampleType"], + serviceEndpoint: ["https://example.com/", "https://iota.org/"], + }); + doc.insertService(service); + // Resolve. + const resolved = doc.resolveService(fragment1); + assert.deepStrictEqual(resolved.id().fragment(), fragment1); + assert.deepStrictEqual(resolved.type(), ["LinkedDomains", "ExampleType"]); + assert.deepStrictEqual(resolved.serviceEndpoint(), ["https://example.com/", "https://iota.org/"]); + assert.deepStrictEqual(resolved.toJSON(), service.toJSON()); + // List. + const list = doc.service(); + assert.deepStrictEqual(list.length, 1); + assert.deepStrictEqual(list[0].toJSON(), resolved.toJSON()); + // Remove + const remove = doc.removeService(resolved.id()); + assert.deepStrictEqual(remove, true); + assert.deepStrictEqual(doc.resolveService(fragment1), undefined); + assert.deepStrictEqual(doc.service().length, 0); + }); + }); + describe('#metadata', function () { + it('should work', () => { + const doc = new StardustDocument(networkName); + const previousCreated = doc.metadataCreated(); + const previousUpdated = doc.metadataUpdated(); + + // Created. + const created = Timestamp.nowUTC().checkedAdd(Duration.seconds(1)); + doc.setMetadataCreated(created); + assert.deepStrictEqual(doc.metadataCreated().toRFC3339(), created.toRFC3339()); + assert.deepStrictEqual(doc.metadata().created().toRFC3339(), created.toRFC3339()); + assert.notDeepStrictEqual(doc.metadataCreated().toRFC3339(), previousCreated.toRFC3339()); + assert.deepStrictEqual(doc.metadataUpdated().toRFC3339(), previousUpdated.toRFC3339()); + // Updated. + const updated = Timestamp.nowUTC().checkedAdd(Duration.seconds(42)); + doc.setMetadataUpdated(updated); + assert.deepStrictEqual(doc.metadataUpdated().toRFC3339(), updated.toRFC3339()); + assert.deepStrictEqual(doc.metadata().updated().toRFC3339(), updated.toRFC3339()); + assert.notDeepStrictEqual(doc.metadataUpdated().toRFC3339(), previousUpdated.toRFC3339()); + assert.deepStrictEqual(doc.metadataCreated().toRFC3339(), created.toRFC3339()); + // Properties. + assert.deepStrictEqual(doc.metadata().properties(), new Map()); + const properties = new Map() + properties.set("custom1", "asdf"); + properties.set("custom2", 1234); + doc.setMetadataPropertyUnchecked("custom1", "asdf"); + doc.setMetadataPropertyUnchecked("custom2", 1234); + assert.deepStrictEqual(doc.metadata().properties(), properties); + }); + }); + describe('#properties', function () { + it('should work', () => { + const doc = new StardustDocument(networkName); + assert.deepStrictEqual(doc.properties(), new Map()); + + const properties = new Map() + properties.set("custom1", "asdf"); + properties.set("custom2", 1234); + doc.setPropertyUnchecked("custom1", "asdf"); + doc.setPropertyUnchecked("custom2", 1234); + assert.deepStrictEqual(doc.properties(), properties); + }); + }); +}); diff --git a/bindings/wasm/tests/wasm.rs b/bindings/wasm/tests/wasm.rs index 58c88af649..e5cc782f86 100644 --- a/bindings/wasm/tests/wasm.rs +++ b/bindings/wasm/tests/wasm.rs @@ -51,16 +51,30 @@ fn test_did() { let key = WasmKeyPair::new(WasmKeyType::Ed25519).unwrap(); let did = WasmDID::new(&key.public(), None).unwrap(); - assert_eq!(did.network_name(), "main"); + assert_eq!(did.network_str(), "main"); let parsed = WasmDID::parse(&did.to_string()).unwrap(); - assert_eq!(did.to_string(), parsed.to_string()); let base58 = WasmDID::new(&key.public(), Some("dev".to_owned())).unwrap(); assert_eq!(base58.tag(), did.tag()); - assert_eq!(base58.network_name(), "dev"); + assert_eq!(base58.network_str(), "dev"); +} + +#[wasm_bindgen_test] +fn test_did_methods() { + let tag = "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV"; + let did_str = format!("did:iota:dev:{tag}"); + let did = WasmDID::parse(&did_str).unwrap(); + + assert_eq!(did.to_string(), did_str); + assert_eq!(did.tag(), tag); + assert_eq!(did.network_str(), "dev"); + assert_eq!(did.scheme(), "did"); + assert_eq!(did.method(), "iota"); + assert_eq!(did.method_id(), format!("dev:{tag}")); + assert_eq!(did.authority(), format!("iota:dev:{tag}")); } #[wasm_bindgen_test] @@ -93,7 +107,7 @@ fn test_did_url() { fn test_document_new() { let keypair: WasmKeyPair = WasmKeyPair::new(WasmKeyType::Ed25519).unwrap(); let document: WasmDocument = WasmDocument::new(&keypair, None, None).unwrap(); - assert_eq!(document.id().network_name(), "main"); + assert_eq!(document.id().network_str(), "main"); assert!(document.default_signing_method().is_ok()); } @@ -261,7 +275,7 @@ fn test_document_network() { let keypair: WasmKeyPair = WasmKeyPair::new(WasmKeyType::Ed25519).unwrap(); let document: WasmDocument = WasmDocument::new(&keypair, Some("dev".to_owned()), None).unwrap(); - assert_eq!(document.id().network_name(), "dev"); + assert_eq!(document.id().network_str(), "dev"); } #[wasm_bindgen_test] diff --git a/identity_account_storage/Cargo.toml b/identity_account_storage/Cargo.toml index ebdf6211e8..46a8b3c30e 100644 --- a/identity_account_storage/Cargo.toml +++ b/identity_account_storage/Cargo.toml @@ -22,7 +22,6 @@ identity_iota_core = { version = "=0.6.0", path = "../identity_iota_core", defau iota-crypto = { version = "0.12.1", default-features = false, features = ["hmac", "pbkdf", "sha", "std", "aes-gcm", "aes-kw"] } iota_stronghold = { version = "0.6.4", default-features = false, features = ["std"], optional = true } once_cell = { version = "1.7", default-features = false, features = ["std"], optional = true } -parking_lot = { version = "0.12" } rand = { version = "0.8", default-features = false, features = ["std", "std_rng"], optional = true } seahash = { version = "4.1.0", default-features = false } serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] } diff --git a/identity_did/src/document/traits.rs b/identity_did/src/document/traits.rs index 8451786ae2..60a3e270e3 100644 --- a/identity_did/src/document/traits.rs +++ b/identity_did/src/document/traits.rs @@ -32,7 +32,7 @@ pub trait Document { Q: Into>; /// Returns the first [`VerificationMethod`] with an `id` property matching the - /// provided `query` and the verification relationship specified by `scope` if present. + /// provided `query` and the verification relationship specified by `scope`, if present. fn resolve_method<'query, 'me, Q>( &'me self, query: Q, diff --git a/identity_did/src/verification/verification_method.rs b/identity_did/src/verification/verification_method.rs index bf6ded5071..8bdcc93fac 100644 --- a/identity_did/src/verification/verification_method.rs +++ b/identity_did/src/verification/verification_method.rs @@ -108,42 +108,42 @@ where Ok(()) } - /// Returns a reference to the verification `Method` controller. + /// Returns a reference to the `VerificationMethod` controller. pub fn controller(&self) -> &D { &self.controller } - /// Returns a mutable reference to the verification `Method` controller. + /// Returns a mutable reference to the `VerificationMethod` controller. pub fn controller_mut(&mut self) -> &mut D { &mut self.controller } - /// Returns a reference to the verification `Method` type. + /// Returns a reference to the `VerificationMethod` type. pub fn type_(&self) -> MethodType { self.type_ } - /// Returns a mutable reference to the verification `Method` type. + /// Returns a mutable reference to the `VerificationMethod` type. pub fn type_mut(&mut self) -> &mut MethodType { &mut self.type_ } - /// Returns a reference to the verification `Method` data. + /// Returns a reference to the `VerificationMethod` data. pub fn data(&self) -> &MethodData { &self.data } - /// Returns a mutable reference to the verification `Method` data. + /// Returns a mutable reference to the `VerificationMethod` data. pub fn data_mut(&mut self) -> &mut MethodData { &mut self.data } - /// Returns a reference to the custom verification `Method` properties. + /// Returns a reference to the custom `VerificationMethod` properties. pub fn properties(&self) -> &T { &self.properties } - /// Returns a mutable reference to the custom verification `Method` properties. + /// Returns a mutable reference to the custom `VerificationMethod` properties. pub fn properties_mut(&mut self) -> &mut T { &mut self.properties } diff --git a/identity_stardust/src/document/stardust_document.rs b/identity_stardust/src/document/stardust_document.rs index c61b0ab71c..abb5e7574e 100644 --- a/identity_stardust/src/document/stardust_document.rs +++ b/identity_stardust/src/document/stardust_document.rs @@ -36,16 +36,16 @@ use crate::StardustDocumentMetadata; use crate::StateMetadataDocument; use crate::StateMetadataEncoding; -/// A [`VerificationMethod`] adhering to the IOTA DID method specification. +/// A [`VerificationMethod`] adhering to the IOTA UTXO DID method specification. pub type StardustVerificationMethod = VerificationMethod; -/// A [`Service`] adhering to the IOTA DID method specification. +/// A [`Service`] adhering to the IOTA UTXO DID method specification. pub type StardustService = Service; -/// A [`CoreDocument`] whose fields adhere to the IOTA DID method specification. +/// A [`CoreDocument`] whose fields adhere to the IOTA UTXO DID method specification. pub type StardustCoreDocument = CoreDocument; -/// A DID Document adhering to the IOTA DID method specification. +/// A DID Document adhering to the IOTA UTXO DID method specification. /// /// This extends [`CoreDocument`]. #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] @@ -271,7 +271,6 @@ impl StardustDocument { // =========================================================================== // Publishing // =========================================================================== - // TODO: clean up and feature-gate certain methods to avoid hard dependency on iota-client? /// Serializes the document for inclusion in an Alias Output's state metadata /// with the default [`StateMetadataEncoding`]. From ba4d4e2fc0218547848395c133bf7d49af18d1ac Mon Sep 17 00:00:00 2001 From: Philipp Date: Tue, 2 Aug 2022 15:31:20 +0300 Subject: [PATCH 34/89] Add Stardust Client Extension Trait (#963) * Implement basic publish and resolve * Fix conflicts * Adapt to new structure * Fix potentially buggy state * Implement documents_from_block * Add reworked create_did example * Add manipulate DID example * Add destroy_did example * Rename old create_did example * Rename new create_did example * Add resolve_did example * Document `StardustClientExt` * Rework API approach * Make async-trait optional, fix default-features * Add network mismatch check during resolution * Make `RentStructure` optional * Add output destroy test * Make previous amount the default for updates * Fix deletion test * Document bee-api-types issue * Cleanup errors * Deduplicate resolution logic * Expose `network_name` method * Revert resolution example to earlier state * Rename `publish_did` -> `publish_did_output` * Remove old create_did example * Use mostly "Alias Output" capitalization * Implement deactivation, update delete example * Improve docs, naming * Improve error names * Specialize more errors * Mention intended trait usage * Rename examples * Add `resolve_alias_output` method * Fix docs, address some TODOs * Fix delete test * Document more error cases * Set `deactivated = true` when resolving such a doc * Address review comments * Convert panics to errors * Run Stardust Rust examples * Remove tests that are duplicated in examples * Rename methods for increased consistency * Remove `IssuerFeature` * Fix `deactivated` serialization * Specify manifest-path for examples * Fix delete example assertion --- .github/workflows/build-and-test.yml | 9 + identity_stardust/Cargo.toml | 5 +- identity_stardust/examples/create_did.rs | 239 ------------ identity_stardust/examples/ex0_create_did.rs | 125 +++++++ identity_stardust/examples/ex1_update_did.rs | 62 ++++ identity_stardust/examples/ex2_resolve_did.rs | 29 ++ .../examples/ex3_deactivate_did.rs | 44 +++ identity_stardust/examples/ex4_delete_did.rs | 39 ++ identity_stardust/src/client/client_ext.rs | 349 ++++++++++++++++++ identity_stardust/src/client/mod.rs | 6 + identity_stardust/src/did/stardust_did.rs | 64 +--- .../src/document/stardust_document.rs | 27 +- .../document/stardust_document_metadata.rs | 3 + identity_stardust/src/error.rs | 36 +- identity_stardust/src/lib.rs | 4 + identity_stardust/src/network/network_name.rs | 2 +- .../src/state_metadata/document.rs | 6 +- 17 files changed, 707 insertions(+), 342 deletions(-) delete mode 100644 identity_stardust/examples/create_did.rs create mode 100644 identity_stardust/examples/ex0_create_did.rs create mode 100644 identity_stardust/examples/ex1_update_did.rs create mode 100644 identity_stardust/examples/ex2_resolve_did.rs create mode 100644 identity_stardust/examples/ex3_deactivate_did.rs create mode 100644 identity_stardust/examples/ex4_delete_did.rs create mode 100644 identity_stardust/src/client/client_ext.rs create mode 100644 identity_stardust/src/client/mod.rs diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index b16cd38060..a9ccc1352a 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -167,6 +167,15 @@ jobs: command: test args: --manifest-path ./identity_stardust/Cargo.toml --workspace --all-features --release + - name: Run Stardust Rust examples + # run examples only on ubuntu for now + if: matrix.os == 'ubuntu-latest' + run: | + cargo read-manifest --manifest-path ./identity_stardust/Cargo.toml | \ + jq -r '.targets[].name' | \ + awk '$1 ~ /ex.*/' | \ + parallel -k -j 4 --retries 3 cargo run --manifest-path ./identity_stardust/Cargo.toml --example {} --all-features --release + - name: Stop sccache uses: './.github/actions/rust/sccache/stop-sccache' with: diff --git a/identity_stardust/Cargo.toml b/identity_stardust/Cargo.toml index 0731992276..2149ea3ec6 100644 --- a/identity_stardust/Cargo.toml +++ b/identity_stardust/Cargo.toml @@ -17,6 +17,7 @@ identity_core = { version = "=0.6.0", path = "../identity_core", default-feature identity_credential = { version = "=0.6.0", path = "../identity_credential", default-features = false } identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } +async-trait = { version = "0.1.56", default-features = false, optional = true } num-derive = { version = "0.3", default-features = false } num-traits = { version = "0.2", default-features = false, features = ["std"] } once_cell = { version = "1", default-features = false, features = ["std"] } @@ -27,7 +28,7 @@ thiserror = { version = "1.0", default-features = false } [dependencies.iota-client] git = "https://github.com/iotaledger/iota.rs" -rev = "2a35c7affe15034fd1b82884966269882bffb23b" # develop branch, 2022-07-18 +rev = "a582bfa882793fe21db2055c4f7878ebc531877a" # develop branch, 2022-07-27 features = ["tls"] default-features = false optional = true @@ -47,6 +48,6 @@ rustdoc-args = ["--cfg", "docsrs"] [features] default = ["iota-client", "revocation-bitmap"] # Enables the iota-client dependency and associated helper functions. -iota-client = ["dep:iota-client"] +iota-client = ["dep:iota-client", "dep:async-trait"] # Enables revocation with `RevocationBitmap2022`. revocation-bitmap = ["identity_did/revocation-bitmap"] diff --git a/identity_stardust/examples/create_did.rs b/identity_stardust/examples/create_did.rs deleted file mode 100644 index 153db9dce9..0000000000 --- a/identity_stardust/examples/create_did.rs +++ /dev/null @@ -1,239 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::str::FromStr; - -use identity_core::crypto::KeyPair; -use identity_core::crypto::KeyType; -use identity_did::verification::MethodScope; -use iota_client::block::output::feature::IssuerFeature; -use iota_client::block::output::feature::MetadataFeature; -use iota_client::block::output::feature::SenderFeature; -use iota_client::block::output::unlock_condition::AddressUnlockCondition; -use iota_client::block::output::unlock_condition::GovernorAddressUnlockCondition; -use iota_client::block::output::unlock_condition::StateControllerAddressUnlockCondition; -use iota_client::block::output::unlock_condition::UnlockCondition; -use iota_client::block::output::AliasId; -use iota_client::block::output::AliasOutputBuilder; -use iota_client::block::output::BasicOutputBuilder; -use iota_client::block::output::ByteCostConfig; -use iota_client::block::output::Feature; -use iota_client::block::output::Output; -use iota_client::secret::mnemonic::MnemonicSecretManager; -use iota_client::secret::SecretManager; -use iota_client::Client; - -use identity_stardust::NetworkName; -use identity_stardust::StardustDID; -use identity_stardust::StardustDocument; -use identity_stardust::StardustVerificationMethod; - -// PROBLEMS SO FAR: -// 1) Alias Id is inferred from the block, so we have to use a placeholder DID for creation. -// 2) Cannot get an Output Id back from an Alias Id (hash of Output Id), need to use Indexer API. -// 3) The Output response from the Indexer is an Output, not a Block, so cannot infer Alias ID from it (fine since we -// use the ID to retrieve the Output in the first place). The OutputDto conversion is annoying too. -// 4) The pieces needed to publish an update are fragmented (Output ID for input, amount, document), bit annoying to -// reconstruct. Use a holder struct like Holder { AliasOutput, StardustDocument } with convenience functions? -// 5) Inferred fields such as the controller and governor need to reflect in the (JSON) Document but excluded from the -// StardustDocument serialization when published. Handle with a separate `pack` function like before? - -/// Demonstrate how to embed a DID Document in an Alias Output. -/// -/// iota.rs alias example: -/// https://github.com/iotaledger/iota.rs/blob/f945ccf326829a418334942ae9cf53b8fab3dbe5/examples/outputs/alias.rs -/// -/// iota.js mint-nft example: -/// https://github.com/iotaledger/iota.js/blob/79a71d3a2ad03be5bd6148689d083947f3b98476/packages/iota/examples/mint-nft/src/index.ts -#[tokio::main] -async fn main() -> anyhow::Result<()> { - // let endpoint = "http://localhost:14265"; - let endpoint = "https://api.alphanet.iotaledger.net"; - let faucet_manual = "https://faucet.alphanet.iotaledger.net"; - let network_name = NetworkName::try_from("alpha")?; - - // =========================================================================== - // Step 1: Create or load your wallet. - // =========================================================================== - - // let keypair = identity_core::crypto::KeyPair::new(identity_core::crypto::KeyType::Ed25519).unwrap(); - // println!("PrivateKey: {}", keypair.private().to_string()); - // let mnemonic = - // iota_client::crypto::keys::bip39::wordlist::encode(keypair.private().as_ref(),&bip39::wordlist::ENGLISH).unwrap(); - - // NOTE: this is just a randomly generated mnemonic, REMOVE THIS, never actually commit your seed or mnemonic. - let mnemonic = "veteran provide abstract express quick another fee dragon trend extend cotton tail dog truly angle napkin lunch dinosaur shrimp odor gain bag media mountain"; - println!("Mnemonic: {}", mnemonic); - let secret_manager = SecretManager::Mnemonic(MnemonicSecretManager::try_from_mnemonic(mnemonic)?); - - // Create a client instance. - let client = Client::builder() - .with_node(endpoint)? - .with_node_sync_disabled() - .finish()?; - - // Get the Bech32 human-readable part (HRP) of the protocol. - // E.g. "rms" for the Shimmer testnet. - let node_info = client.get_info().await?; - let network_hrp = node_info.node_info.protocol.bech32_hrp; - - let address = client.get_addresses(&secret_manager).with_range(0..1).get_raw().await?[0]; - let address_bech32 = address.to_bech32(network_hrp); - println!("Wallet address: {address_bech32}"); - - println!("INTERACTION REQUIRED: request faucet funds to the above wallet from {faucet_manual}"); - // let faucet_auto = format!("{endpoint}/api/plugins/faucet/v1/enqueue"); - // iota_client::request_funds_from_faucet(&faucet_auto, &address_bech32).await?; - // tokio::time::sleep(std::time::Duration::from_secs(15)).await; - - // =========================================================================== - // Step 2: Create and publish a DID Document in an Alias Output. - // =========================================================================== - - // Create an empty DID Document. - // - // All new Stardust DID Documents initially use a placeholder DID for the network, - // "did:stardust:rms:0x0000000000000000000000000000000000000000000000000000000000000000". - let document: StardustDocument = StardustDocument::new(&network_name); - - println!("DID Document {:#}", document); - - // Create a new Alias Output with the DID Document as state metadata. - let byte_cost_config: ByteCostConfig = client.get_byte_cost_config().await?; - let alias_output: Output = AliasOutputBuilder::new_with_minimum_storage_deposit(byte_cost_config, AliasId::null())? - .with_state_index(0) - .with_foundry_counter(0) - .with_state_metadata(document.pack()?) - .add_feature(Feature::Sender(SenderFeature::new(address))) - .add_feature(Feature::Metadata(MetadataFeature::new(vec![1, 2, 3])?)) - .add_immutable_feature(Feature::Issuer(IssuerFeature::new(address))) - .add_unlock_condition(UnlockCondition::StateControllerAddress( - StateControllerAddressUnlockCondition::new(address), - )) - .add_unlock_condition(UnlockCondition::GovernorAddress(GovernorAddressUnlockCondition::new( - address, - ))) - .finish_output()?; - println!("Deposit amount: {}", alias_output.amount()); - - // Publish to the Tangle ledger. - let block1 = client - .block() - .with_secret_manager(&secret_manager) - .with_outputs(vec![alias_output])? - .finish() - .await?; - println!( - "Transaction with new alias output sent: {endpoint}/api/v2/blocks/{}", - block1.id() - ); - let _ = client.retry_until_included(&block1.id(), None, None).await?; - - // Infer DID from the Alias Output block. - let did: StardustDID = StardustDID::from_block(&block1, &network_name)?; - println!("DID: {did}"); - - // =========================================================================== - // Step 3: Resolve a DID Document. - // =========================================================================== - // iota.rs indexer example: - // https://github.com/iotaledger/iota.rs/blob/f945ccf326829a418334942ae9cf53b8fab3dbe5/examples/indexer.rs - - // Extract Alias ID from the DID tag. - let alias_id: AliasId = AliasId::from_str(did.tag())?; - println!("Alias ID: {alias_id}"); - - // Query Indexer INX Plugin for the Output of the Alias ID. - let alias_output_id = client.alias_output_id(alias_id).await?; - println!("Output ID: {alias_output_id}"); - let response = client.get_output(&alias_output_id).await?; - let output = Output::try_from(&response.output)?; - println!("Output: {output:?}"); - - // The resolved DID Document replaces the placeholder DID with the correct one. - let resolved_document = StardustDocument::unpack_from_output(&did, &output)?; - println!("Resolved Document: {resolved_document:#}"); - - let alias_output = match output { - Output::Alias(output) => Ok(output), - _ => Err(anyhow::anyhow!("not an alias output")), - }?; - - // =========================================================================== - // Step 4: Publish an updated Alias Output. (optional) - // =========================================================================== - - // Add a new Ed25519 verification method to the DID Document for authentication. (optional) - let mut updated_document = resolved_document.clone(); - let keypair: KeyPair = KeyPair::new(KeyType::Ed25519)?; - let method = StardustVerificationMethod::new( - updated_document.id().clone(), - keypair.type_(), - keypair.public(), - "#key-0", - )?; - updated_document.insert_method(method, MethodScope::authentication())?; - - // Update the Alias Output to contain an explicit Alias ID, and the updated DID Document. - let byte_cost_config: ByteCostConfig = client.get_byte_cost_config().await?; - let updated_alias_output = AliasOutputBuilder::from(&alias_output) - // Set the deposit to the new minimum covering the increased size of the DID Document. - .with_minimum_storage_deposit(byte_cost_config) - // Set the explicit Alias ID. - .with_alias_id(alias_id) - // Set the updated DID Document. - .with_state_metadata(updated_document.pack()?) - // State controller updates must increment the state index. - .with_state_index(alias_output.state_index() + 1) - .finish_output()?; - - println!("Updated output: {updated_alias_output:?}"); - - let block2 = client - .block() - .with_secret_manager(&secret_manager) - // Omit inputs so it automatically selects a Basic Output to cover the increased amount. - // .with_input(alias_output_id.into())? - .with_outputs(vec![updated_alias_output])? - .finish() - .await?; - - println!( - "Transaction with updated Alias Output sent: {endpoint}/api/v2/blocks/{}", - block2.id() - ); - let _ = client.retry_until_included(&block2.id(), None, None).await?; - - // =========================================================================== - // Step 5: Destroy Alias Output. (optional) - // =========================================================================== - - // Query Indexer INX Plugin for the latest Output of the Alias ID. - let alias_output_id = client.alias_output_id(alias_id).await?; - let response = client.get_output(&alias_output_id).await?; - let alias_output = Output::try_from(&response.output)?; - - // Consume the Alias Output containing the DID Document, sending its tokens to a new Basic Output. - // WARNING: this destroys the DID Document and renders it permanently unrecoverable. - let basic_output = BasicOutputBuilder::new_with_amount(alias_output.amount())? - .add_unlock_condition(UnlockCondition::Address(AddressUnlockCondition::new(address))) - .finish_output()?; - let block3 = client - .block() - .with_secret_manager(&secret_manager) - .with_input(alias_output_id.into())? - .with_outputs(vec![basic_output])? - .finish() - .await?; - - println!( - "Transaction destroying Alias Output sent: {endpoint}/api/v2/blocks/{}", - block3.id() - ); - let _ = client.retry_until_included(&block3.id(), None, None).await?; - - // Consolidate amounts in separate Basic Outputs into a single one. - client.consolidate_funds(&secret_manager, 0, 0..1).await?; - - Ok(()) -} diff --git a/identity_stardust/examples/ex0_create_did.rs b/identity_stardust/examples/ex0_create_did.rs new file mode 100644 index 0000000000..2284e2c7f7 --- /dev/null +++ b/identity_stardust/examples/ex0_create_did.rs @@ -0,0 +1,125 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use identity_core::crypto::KeyPair; +use identity_core::crypto::KeyType; +use identity_did::verification::MethodScope; +use identity_stardust::NetworkName; +use identity_stardust::StardustClientExt; +use identity_stardust::StardustDocument; +use identity_stardust::StardustVerificationMethod; + +use iota_client::block::address::Address; +use iota_client::block::output::AliasOutput; +use iota_client::block::output::Output; +use iota_client::constants::SHIMMER_TESTNET_BECH32_HRP; +use iota_client::crypto::keys::bip39; +use iota_client::node_api::indexer::query_parameters::QueryParameter; +use iota_client::secret::mnemonic::MnemonicSecretManager; +use iota_client::secret::SecretManager; +use iota_client::Client; + +static ENDPOINT: &str = "https://api.testnet.shimmer.network/"; +static FAUCET_URL: &str = "https://faucet.testnet.shimmer.network/api/enqueue"; + +/// Demonstrate how to create a DID Document and publish it in a new Alias Output. +pub async fn run() -> anyhow::Result<(Client, Address, SecretManager, StardustDocument)> { + // Create a client and an address with funds from the testnet faucet. + let client: Client = Client::builder().with_primary_node(ENDPOINT, None)?.finish()?; + let (address, secret_manager): (Address, SecretManager) = get_address_with_funds(&client).await?; + + // Get the BECH32 HRP identifier of the network. + let network_name: NetworkName = client.network_name().await?; + + // Create a new document with a placeholder DID and add a verification method. + // The placeholder will be replaced during publication, since the DID is derived from the id of the output + // that creates the Alias Output. + let mut document: StardustDocument = StardustDocument::new(&network_name); + + // Create a new key pair that we'll use to create a verification method. + let keypair: KeyPair = KeyPair::new(KeyType::Ed25519)?; + + // Create a new verification method based on the previously created key pair. + let method: StardustVerificationMethod = + StardustVerificationMethod::new(document.id().clone(), keypair.type_(), keypair.public(), "#key-1")?; + + // Insert the method into the document. + document.insert_method(method, MethodScope::VerificationMethod)?; + + // Construct an Alias Output containing the DID document, with `address` set as both the state controller and + // governor. + let alias_output: AliasOutput = client.new_did_output(address, document, None).await?; + + // Publish the output and get the published document. + let document: StardustDocument = client.publish_did_output(&secret_manager, alias_output).await?; + + println!("Published DID document: {:#?}", document); + + Ok((client, address, secret_manager, document)) +} + +/// Creates a new address and SecretManager with funds from the testnet faucet. +async fn get_address_with_funds(client: &Client) -> anyhow::Result<(Address, SecretManager)> { + let keypair = identity_core::crypto::KeyPair::new(KeyType::Ed25519)?; + let mnemonic = + iota_client::crypto::keys::bip39::wordlist::encode(keypair.private().as_ref(), &bip39::wordlist::ENGLISH) + .map_err(|err| anyhow::anyhow!(format!("{err:?}")))?; + + let secret_manager = SecretManager::Mnemonic(MnemonicSecretManager::try_from_mnemonic(&mnemonic)?); + + let address = client.get_addresses(&secret_manager).with_range(0..1).get_raw().await?[0]; + + request_faucet_funds(client, address).await?; + + Ok((address, secret_manager)) +} + +/// Requests funds from the testnet faucet for the given `address`. +async fn request_faucet_funds(client: &Client, address: Address) -> anyhow::Result<()> { + let address_bech32 = address.to_bech32(SHIMMER_TESTNET_BECH32_HRP); + + iota_client::request_funds_from_faucet(FAUCET_URL, &address_bech32).await?; + + tokio::time::timeout(std::time::Duration::from_secs(30), async { + loop { + tokio::time::sleep(std::time::Duration::from_secs(5)).await; + + let balance = get_address_balance(client, &address_bech32).await?; + if balance > 0 { + break; + } + } + Ok::<(), anyhow::Error>(()) + }) + .await??; + + Ok(()) +} + +/// Returns the balance of the given bech32-encoded `address`. +async fn get_address_balance(client: &Client, address: &str) -> anyhow::Result { + let output_ids = client + .basic_output_ids(vec![ + QueryParameter::Address(address.to_owned()), + QueryParameter::HasExpirationCondition(false), + QueryParameter::HasTimelockCondition(false), + QueryParameter::HasStorageReturnCondition(false), + ]) + .await?; + + let outputs_responses = client.get_outputs(output_ids).await?; + + let mut total_amount = 0; + for output_response in outputs_responses { + let output = Output::try_from(&output_response.output)?; + total_amount += output.amount(); + } + + Ok(total_amount) +} + +#[allow(dead_code)] +#[tokio::main] +async fn main() -> anyhow::Result<()> { + run().await.map(|_| ()) +} diff --git a/identity_stardust/examples/ex1_update_did.rs b/identity_stardust/examples/ex1_update_did.rs new file mode 100644 index 0000000000..550f35dca1 --- /dev/null +++ b/identity_stardust/examples/ex1_update_did.rs @@ -0,0 +1,62 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use identity_core::convert::FromJson; +use identity_core::json; +use identity_did::did::DID; +use identity_did::service::Service; +use identity_did::verification::MethodRelationship; +use identity_stardust::StardustClientExt; +use identity_stardust::StardustDocument; +use identity_stardust::StardustService; +use iota_client::block::output::AliasOutput; +use iota_client::block::output::AliasOutputBuilder; +use iota_client::secret::SecretManager; +use iota_client::Client; + +mod ex0_create_did; + +/// Demonstrate how to modify a DID document in an existing Alias Output. +#[tokio::main] +async fn main() -> anyhow::Result<()> { + // Create a new DID in an Alias Output for us to modify. + let (client, _, secret_manager, document): (Client, _, SecretManager, StardustDocument) = + ex0_create_did::run().await?; + + // Resolve the latest state of the document. + // Technically this is equivalent to the document above. + let mut document: StardustDocument = client.resolve_did(document.id()).await?; + + // Attach a new method relationship to the existing method. + document.attach_method_relationship( + &document.id().to_url().join("#key-1")?, + MethodRelationship::Authentication, + )?; + + // Add a new Service. + let service: StardustService = Service::from_json_value(json!({ + "id": document.id().to_url().join("#linked-domain")?, + "type": "LinkedDomains", + "serviceEndpoint": "https://iota.org/" + }))?; + assert!(document.insert_service(service)); + + // Resolve the latest output and update it with the given document. + let alias_output: AliasOutput = client.update_did_output(document.clone()).await?; + + // Obtain the current byte costs and increase the required storage deposit + // since the amount of stored bytes increased. + let rent_structure = client.get_rent_structure().await?; + let alias_output = AliasOutputBuilder::from(&alias_output) + .with_minimum_storage_deposit(rent_structure) + .finish()?; + + // Publish the output. + let resolved_document: StardustDocument = client.publish_did_output(&secret_manager, alias_output).await?; + + assert_eq!(document, resolved_document); + + println!("Published updated DID Document: {:#?}", resolved_document); + + Ok(()) +} diff --git a/identity_stardust/examples/ex2_resolve_did.rs b/identity_stardust/examples/ex2_resolve_did.rs new file mode 100644 index 0000000000..07e88ecb7a --- /dev/null +++ b/identity_stardust/examples/ex2_resolve_did.rs @@ -0,0 +1,29 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use identity_stardust::StardustClientExt; +use identity_stardust::StardustDocument; +use iota_client::block::output::AliasOutput; +use iota_client::Client; + +mod ex0_create_did; + +/// Demonstrate how to resolve an existing DID in an Alias Output. +#[tokio::main] +async fn main() -> anyhow::Result<()> { + let (client, _, _, document): (Client, _, _, StardustDocument) = ex0_create_did::run().await?; + + // Obtains the Alias Output and extracts the contained document. + let resolved_doc: StardustDocument = client.resolve_did(document.id()).await?; + + assert_eq!(resolved_doc, document); + + println!("Resolved DID Document: {resolved_doc:#?}"); + + // We can also resolve the Alias Output directly. + let alias_output: AliasOutput = client.resolve_did_output(document.id()).await?; + + println!("The Alias Output holds {} tokens", alias_output.amount()); + + Ok(()) +} diff --git a/identity_stardust/examples/ex3_deactivate_did.rs b/identity_stardust/examples/ex3_deactivate_did.rs new file mode 100644 index 0000000000..6bc80196ba --- /dev/null +++ b/identity_stardust/examples/ex3_deactivate_did.rs @@ -0,0 +1,44 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use identity_stardust::StardustClientExt; +use identity_stardust::StardustDocument; +use iota_client::block::address::Address; +use iota_client::block::output::AliasOutput; +use iota_client::secret::SecretManager; +use iota_client::Client; + +mod ex0_create_did; + +/// Demonstrate how to deactivate a DID in an Alias Output. +#[tokio::main] +async fn main() -> anyhow::Result<()> { + // Create a new DID in an Alias Output for us to modify. + let (client, _, secret_manager, document): (Client, Address, SecretManager, StardustDocument) = + ex0_create_did::run().await?; + + // Deactivate the DID by publishing an empty document. + // This process can be reversed since the Alias Output is not destroyed. + // Deactivation can only be done by the state controller of the Alias Output. + client.deactivate_did_output(&secret_manager, document.id()).await?; + + // Wait for the node to index the new state. + tokio::time::sleep(std::time::Duration::from_secs(5)).await; + + // Attempting to resolve a deactivated DID results in an document + // where the metadata's `deactivated` field is `true`. + let deactivated_document: StardustDocument = client.resolve_did(document.id()).await?; + + assert!(matches!(deactivated_document.metadata.deactivated, Some(true))); + + // Re-activate the DID by publishing a valid document. + let alias_output: AliasOutput = client.update_did_output(document.clone()).await?; + client.publish_did_output(&secret_manager, alias_output).await?; + + // Resolve the republished document. + let resolved_document: StardustDocument = client.resolve_did(document.id()).await?; + + assert_eq!(document, resolved_document); + + Ok(()) +} diff --git a/identity_stardust/examples/ex4_delete_did.rs b/identity_stardust/examples/ex4_delete_did.rs new file mode 100644 index 0000000000..8a9dbb8195 --- /dev/null +++ b/identity_stardust/examples/ex4_delete_did.rs @@ -0,0 +1,39 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use identity_stardust::Error; +use identity_stardust::StardustClientExt; +use identity_stardust::StardustDocument; +use iota_client::block::address::Address; +use iota_client::secret::SecretManager; +use iota_client::Client; + +mod ex0_create_did; + +/// Demonstrate how to delete an existing DID in an Alias Output, reclaiming the stored deposit. +#[tokio::main] +async fn main() -> anyhow::Result<()> { + // Create a new DID in an Alias Output for us to modify. + let (client, address, secret_manager, document): (Client, Address, SecretManager, StardustDocument) = + ex0_create_did::run().await?; + + // Deletes the Alias Output and its contained DID Document, rendering the DID permanently destroyed. + // This operation is *not* reversible. + // Deletion can only be done by the governor of the Alias Output. + client + .delete_did_output(&secret_manager, address, document.id()) + .await?; + + // Wait for the node to index the new state. + tokio::time::sleep(std::time::Duration::from_secs(5)).await; + + // Attempting to resolve a deleted DID results in a `NotFound` error. + let error: Error = client.resolve_did(document.id()).await.unwrap_err(); + + assert!(matches!( + error, + identity_stardust::Error::DIDResolutionError(iota_client::Error::NotFound) + )); + + Ok(()) +} diff --git a/identity_stardust/src/client/client_ext.rs b/identity_stardust/src/client/client_ext.rs new file mode 100644 index 0000000000..27858dac74 --- /dev/null +++ b/identity_stardust/src/client/client_ext.rs @@ -0,0 +1,349 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::borrow::Cow; +use std::ops::Deref; + +use identity_did::did::DIDError; +use iota_client::api_types::responses::OutputResponse; +use iota_client::block::address::Address; +use iota_client::block::output::feature::SenderFeature; +use iota_client::block::output::unlock_condition::AddressUnlockCondition; +use iota_client::block::output::unlock_condition::GovernorAddressUnlockCondition; +use iota_client::block::output::unlock_condition::StateControllerAddressUnlockCondition; +use iota_client::block::output::AliasId; +use iota_client::block::output::AliasOutput; +use iota_client::block::output::AliasOutputBuilder; +use iota_client::block::output::BasicOutputBuilder; +use iota_client::block::output::Feature; +use iota_client::block::output::Output; +use iota_client::block::output::OutputId; +use iota_client::block::output::RentStructure; +use iota_client::block::output::UnlockCondition; +use iota_client::block::payload::transaction::TransactionEssence; +use iota_client::block::payload::Payload; +use iota_client::block::Block; +use iota_client::secret::SecretManager; +use iota_client::Client; + +use crate::error::Result; +use crate::Error; +use crate::NetworkName; +use crate::StardustDID; +use crate::StardustDocument; + +/// An extension trait for a [`Client`] that provides helper functions for publication +/// and resolution of DID documents in Alias Outputs. +/// +/// This trait is only meant to be used rather than implemented. +#[async_trait::async_trait] +pub trait StardustClientExt: Sync { + /// Returns a reference to a [`Client`]. + fn client(&self) -> &Client; + + /// Create a DID with a new Alias Output containing the given `document`. + /// + /// The `address` will be set as the state controller and governor unlock conditions. + /// The minimum required token deposit amount will be set according to the given + /// `rent_structure`, which will be fetched from the node if not provided. + /// The returned Alias Output can be further customized before publication, if desired. + /// + /// NOTE: this does *not* publish the Alias Output. See [`publish_did_output`](StardustClientExt::publish_did_output). + /// + /// # Errors + /// + /// - Returns an [`Error::DIDUpdateError`] when retrieving the `RentStructure` fails. + /// - Returns an [`Error::AliasOutputBuildError`] when building the Alias Output fails. + async fn new_did_output( + &self, + address: Address, + document: StardustDocument, + rent_structure: Option, + ) -> Result { + let rent_structure: RentStructure = if let Some(inner) = rent_structure { + inner + } else { + self + .client() + .get_rent_structure() + .await + .map_err(Error::DIDUpdateError)? + }; + + AliasOutputBuilder::new_with_minimum_storage_deposit(rent_structure, AliasId::null()) + .map_err(Error::AliasOutputBuildError)? + .with_state_index(0) + .with_foundry_counter(0) + .with_state_metadata(document.pack()?) + .add_feature(Feature::Sender(SenderFeature::new(address))) + .add_unlock_condition(UnlockCondition::StateControllerAddress( + StateControllerAddressUnlockCondition::new(address), + )) + .add_unlock_condition(UnlockCondition::GovernorAddress(GovernorAddressUnlockCondition::new( + address, + ))) + .finish() + .map_err(Error::AliasOutputBuildError) + } + + /// Returns the updated Alias Output for further customization and publication. The storage deposit + /// on the output is unchanged. If the size of the document increased, the amount must be increased manually. + /// + /// NOTE: this does *not* publish the updated Alias Output. See + /// [`publish_did_output`](StardustClientExt::publish_did_output). + /// + /// # Errors + /// + /// Returns `Err` when failing to resolve the DID contained in `document`. + async fn update_did_output(&self, document: StardustDocument) -> Result { + let (alias_id, _, alias_output) = resolve_alias_output(self.client(), document.id()).await?; + + let mut alias_output_builder: AliasOutputBuilder = AliasOutputBuilder::from(&alias_output) + .with_state_index(alias_output.state_index() + 1) + .with_state_metadata(document.pack()?); + + if alias_output.alias_id().is_null() { + alias_output_builder = alias_output_builder.with_alias_id(alias_id); + } + + alias_output_builder.finish().map_err(Error::AliasOutputBuildError) + } + + /// Resolves the Alias Output associated to the `did`, removes the DID document, + /// and publishes the output. This effectively deactivates the DID. + /// Deactivating does not destroy the output. Hence, a deactivated DID can be + /// re-activated by updating the contained document. + /// + /// The storage deposit on the output is left unchanged. + /// + /// # Errors + /// + /// Returns `Err` when failing to resolve the `did`. + async fn deactivate_did_output(&self, secret_manager: &SecretManager, did: &StardustDID) -> Result<()> { + let (alias_id, _, alias_output) = resolve_alias_output(self.client(), did).await?; + + let mut alias_output_builder: AliasOutputBuilder = AliasOutputBuilder::from(&alias_output) + .with_state_index(alias_output.state_index() + 1) + .with_state_metadata(Vec::new()); + + if alias_output.alias_id().is_null() { + alias_output_builder = alias_output_builder.with_alias_id(alias_id); + } + + let alias_output: AliasOutput = alias_output_builder.finish().map_err(Error::AliasOutputBuildError)?; + + let _ = publish_output(self.client(), secret_manager, alias_output).await?; + + Ok(()) + } + + /// Publish the given `alias_output` with the provided `secret_manager` + /// and returns the block they were published in. + /// + /// Needs to be called by the state controller of the Alias Output. + /// + /// This method modifies the on-ledger state. + async fn publish_did_output( + &self, + secret_manager: &SecretManager, + alias_output: AliasOutput, + ) -> Result { + let block: Block = publish_output(self.client(), secret_manager, alias_output).await?; + + Ok( + documents_from_block(self.client(), &block) + .await? + .into_iter() + .next() + .expect("there should be exactly one document"), + ) + } + + /// Consume the Alias Output containing the given `did`, sending its tokens to a new Basic Output + /// unlockable by `address`. + /// + /// Note that only the governor of an Alias Output is allowed to destroy it. + /// + /// # WARNING + /// + /// This destroys the DID Document and the Alias Output and renders the DID permanently unrecoverable. + async fn delete_did_output(&self, secret_manager: &SecretManager, address: Address, did: &StardustDID) -> Result<()> { + let client: &Client = self.client(); + + let (_, output_id, alias_output) = resolve_alias_output(client, did).await?; + + let basic_output = BasicOutputBuilder::new_with_amount(alias_output.amount()) + .map_err(Error::BasicOutputBuildError)? + .add_unlock_condition(UnlockCondition::Address(AddressUnlockCondition::new(address))) + .finish_output() + .map_err(Error::BasicOutputBuildError)?; + + client + .block() + .with_secret_manager(secret_manager) + .with_input(output_id.into()) + .map_err(Error::DIDUpdateError)? + .with_outputs(vec![basic_output]) + .map_err(Error::DIDUpdateError)? + .finish() + .await + .map_err(Error::DIDUpdateError)?; + + Ok(()) + } + + /// Resolve a [`StardustDID`] to a [`StardustDocument`]. + /// + /// # Errors + /// + /// - Returns a [`NetworkMismatch`](Error::NetworkMismatch) error if the DID's and the client's network do not match. + /// - Returns a [`NotFound`](iota_client::Error::NotFound) error if the associated Alias Output wasn't found. + async fn resolve_did(&self, did: &StardustDID) -> Result { + let network_hrp: String = get_network_hrp(self.client()).await?; + + if did.network_str() != network_hrp.as_str() { + return Err(Error::NetworkMismatch { + expected: did.network_str().to_owned(), + actual: network_hrp, + }); + } + + let (_, _, alias_output) = resolve_alias_output(self.client(), did).await?; + + if alias_output.state_metadata().is_empty() { + let mut empty_document: StardustDocument = StardustDocument::new_with_id(did.to_owned()); + empty_document.metadata.deactivated = Some(true); + + Ok(empty_document) + } else { + let document: &[u8] = alias_output.state_metadata(); + StardustDocument::unpack(did, document) + } + } + + /// Resolve a [`StardustDID`] to an [`AliasOutput`]. + /// + /// # Errors + /// + /// - Returns a [`NetworkMismatch`](Error::NetworkMismatch) error if the DID's and the client's network do not match. + /// - Returns a [`NotFound`](iota_client::Error::NotFound) error if the associated Alias Output wasn't found. + async fn resolve_did_output(&self, did: &StardustDID) -> Result { + let network_hrp: String = get_network_hrp(self.client()).await?; + + if did.network_str() != network_hrp.as_str() { + return Err(Error::NetworkMismatch { + expected: did.network_str().to_owned(), + actual: network_hrp, + }); + } + + resolve_alias_output(self.client(), did) + .await + .map(|(_, _, alias_output)| alias_output) + } + + /// Returns the network name of the connected node, which is the + /// BECH32 human-readable part (HRP) of the network. + /// + /// For the IOTA main network this is `iota` and for the Shimmer network it is `smr`. + async fn network_name(&self) -> Result { + get_network_hrp(self.client()).await.and_then(NetworkName::try_from) + } +} + +impl StardustClientExt for Client { + fn client(&self) -> &Client { + self + } +} + +impl StardustClientExt for &Client { + fn client(&self) -> &Client { + self + } +} + +/// Publishes an `alias_output`. +/// Returns the block that the output was included in. +async fn publish_output(client: &Client, secret_manager: &SecretManager, alias_output: AliasOutput) -> Result { + let block: Block = client + .block() + .with_secret_manager(secret_manager) + .with_outputs(vec![alias_output.into()]) + .map_err(Error::DIDUpdateError)? + .finish() + .await + .map_err(Error::DIDUpdateError)?; + + let _ = client + .retry_until_included(&block.id(), None, None) + .await + .map_err(Error::DIDUpdateError)?; + + Ok(block) +} + +/// Get the BECH32 HRP from the client's network. +async fn get_network_hrp(client: &Client) -> Result { + client + .get_network_info() + .await + .map_err(Error::DIDResolutionError)? + .bech32_hrp + .ok_or_else(|| Error::InvalidNetworkName("".to_owned())) +} + +/// Returns all DID documents of the Alias Outputs contained in the payload's transaction, if any. +async fn documents_from_block(client: &Client, block: &Block) -> Result> { + let network_hrp: String = get_network_hrp(client).await?; + let mut documents = Vec::new(); + + if let Some(Payload::Transaction(tx_payload)) = block.payload() { + let TransactionEssence::Regular(regular) = tx_payload.essence(); + + for (index, output) in regular.outputs().iter().enumerate() { + if let Output::Alias(alias_output) = output { + let alias_id = if alias_output.alias_id().is_null() { + AliasId::from( + OutputId::new( + tx_payload.id(), + index + .try_into() + .map_err(|_| Error::OutputIdConversionError(format!("the output index {index} must fit into a u16")))?, + ) + .map_err(|err| Error::OutputIdConversionError(err.to_string()))?, + ) + } else { + alias_output.alias_id().to_owned() + }; + + let did: StardustDID = StardustDID::new( + alias_id.deref(), + &NetworkName::try_from(Cow::from(network_hrp.clone()))?, + ); + documents.push(StardustDocument::unpack(&did, alias_output.state_metadata())?); + } + } + } + + Ok(documents) +} + +/// Resolve a did into an Alias Output and the associated identifiers. +async fn resolve_alias_output(client: &Client, did: &StardustDID) -> Result<(AliasId, OutputId, AliasOutput)> { + let tag_bytes: [u8; StardustDID::TAG_BYTES_LEN] = + prefix_hex::decode(did.tag()).map_err(|_| DIDError::InvalidMethodId)?; + let alias_id: AliasId = AliasId::new(tag_bytes); + let output_id: OutputId = client + .alias_output_id(alias_id) + .await + .map_err(Error::DIDResolutionError)?; + let output_response: OutputResponse = client.get_output(&output_id).await.map_err(Error::DIDResolutionError)?; + let output: Output = Output::try_from(&output_response.output).map_err(Error::OutputConversionError)?; + + if let Output::Alias(alias_output) = output { + Ok((alias_id, output_id, alias_output)) + } else { + Err(Error::NotAnAliasOutput(output_id)) + } +} diff --git a/identity_stardust/src/client/mod.rs b/identity_stardust/src/client/mod.rs new file mode 100644 index 0000000000..beb796c365 --- /dev/null +++ b/identity_stardust/src/client/mod.rs @@ -0,0 +1,6 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +pub use client_ext::StardustClientExt; + +mod client_ext; diff --git a/identity_stardust/src/did/stardust_did.rs b/identity_stardust/src/did/stardust_did.rs index 381e5942b0..b18429ead5 100644 --- a/identity_stardust/src/did/stardust_did.rs +++ b/identity_stardust/src/did/stardust_did.rs @@ -25,9 +25,6 @@ pub type Result = std::result::Result; /// See [`DIDUrl`]. pub type StardustDIDUrl = DIDUrl; -// The length of an Alias ID, which is a BLAKE2b-256 hash (32-bytes). -const TAG_BYTES_LEN: usize = 32; - /// A DID conforming to the IOTA UTXO DID method specification. /// /// This is a thin wrapper around the [`DID`][`CoreDID`] type from the @@ -48,6 +45,9 @@ impl StardustDID { /// The default Tangle network (`"main"`). pub const DEFAULT_NETWORK: &'static str = "main"; + // The length of an Alias ID, which is a BLAKE2b-256 hash (32-bytes). + pub(crate) const TAG_BYTES_LEN: usize = 32; + // =========================================================================== // Constructors // =========================================================================== @@ -170,7 +170,7 @@ impl StardustDID { let (_, tag) = Self::denormalized_components(did.method_id()); // Implicitly catches if there are too many segments (:) in the DID too. - prefix_hex::decode::<[u8; TAG_BYTES_LEN]>(tag) + prefix_hex::decode::<[u8; Self::TAG_BYTES_LEN]>(tag) .map_err(|_| DIDError::InvalidMethodId) .map(|_| ()) } @@ -219,59 +219,6 @@ impl StardustDID { } } -#[cfg(feature = "iota-client")] -mod stardust_did_iota_client { - use std::ops::Deref; - - use iota_client::block::output::AliasId; - use iota_client::block::output::Output; - use iota_client::block::output::OutputId; - use iota_client::block::payload::transaction::TransactionEssence; - use iota_client::block::payload::Payload; - use iota_client::block::Block; - - use super::*; - - impl StardustDID { - /// Extracts an Alias ID from a published block and returns a new DID from it. - /// - /// NOTE: if there are multiple Alias Outputs in the payload, this uses the first one. - // TODO: can hopefully remove from public API if the publishing logic is wrapped. - pub fn from_block(block: &Block, network: &NetworkName) -> crate::Result { - let payload: &Payload = block.payload().ok_or(DIDError::Other("block missing payload"))?; - // TODO: improve error handling messages. - let output_id: OutputId = get_alias_output_id_from_payload(payload)?; - let id: AliasId = AliasId::from(output_id); - Ok(StardustDID::new(id.deref(), network)) - } - } - - // helper function to get the output id for the first alias output - // TODO: improve error handling - fn get_alias_output_id_from_payload(payload: &Payload) -> crate::Result { - if let Payload::Transaction(tx_payload) = payload { - let TransactionEssence::Regular(regular) = tx_payload.essence(); - if let Some((index, _)) = regular - .outputs() - .iter() - .enumerate() - .find(|(_, item)| matches!(item, Output::Alias(_))) - { - Ok(OutputId::new( - tx_payload.id(), - index - .try_into() - .map_err(|_| DIDError::Other("output index exceeds u16"))?, - )?) - } else { - Err(DIDError::Other("no alias output in transaction essence"))? - } - } else { - Err(DIDError::Other("not a transaction payload"))? - } - } -} - impl DID for StardustDID { /// Returns the [`StardustDID`] scheme. See [`DID::SCHEME`]. fn scheme(&self) -> &'static str { @@ -305,8 +252,7 @@ impl DID for StardustDID { self.0.into_string() } - // TODO: Link [`StardustDIDUrl`] after `document` has been refactored to use the types in this module. - /// Creates a new DIDUrl by joining with a relative DID Url string. + /// Creates a new [`DIDUrl`](crate::StardustDIDUrl) by joining with a relative DID Url string. /// /// # Errors /// diff --git a/identity_stardust/src/document/stardust_document.rs b/identity_stardust/src/document/stardust_document.rs index abb5e7574e..4dd237a93a 100644 --- a/identity_stardust/src/document/stardust_document.rs +++ b/identity_stardust/src/document/stardust_document.rs @@ -269,7 +269,7 @@ impl StardustDocument { } // =========================================================================== - // Publishing + // Packing // =========================================================================== /// Serializes the document for inclusion in an Alias Output's state metadata @@ -293,31 +293,6 @@ impl StardustDocument { } } -#[cfg(feature = "iota-client")] -mod stardust_document_iota_client { - use iota_client::block::output::Output; - - use crate::Error; - - use super::*; - - impl StardustDocument { - /// Deserializes a JSON-encoded `StardustDocument` from an Alias Output block. - /// - /// NOTE: `did` is required since it is omitted from the serialized DID Document and - /// cannot be inferred from the [`Output`]. It also indicates the network, which is not - /// encoded in the `AliasId` alone. - // TODO: remove? Is `unpack` sufficient? - pub fn unpack_from_output(did: &StardustDID, output: &Output) -> Result { - let document: &[u8] = match output { - Output::Alias(alias_output) => alias_output.state_metadata(), - _ => return Err(Error::InvalidStateMetadata("not an alias output")), - }; - Self::unpack(did, document) - } - } -} - impl Document for StardustDocument { type D = StardustDID; type U = Object; diff --git a/identity_stardust/src/document/stardust_document_metadata.rs b/identity_stardust/src/document/stardust_document_metadata.rs index 6f014ac7c5..52eac6e6c1 100644 --- a/identity_stardust/src/document/stardust_document_metadata.rs +++ b/identity_stardust/src/document/stardust_document_metadata.rs @@ -19,6 +19,8 @@ pub struct StardustDocumentMetadata { pub created: Option, #[serde(skip_serializing_if = "Option::is_none")] pub updated: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub deactivated: Option, #[serde(flatten)] pub properties: Object, } @@ -31,6 +33,7 @@ impl StardustDocumentMetadata { Self { created: Some(now), updated: Some(now), + deactivated: None, properties: Object::default(), } } diff --git a/identity_stardust/src/error.rs b/identity_stardust/src/error.rs index d717dbeb03..fe4eb8d4ed 100644 --- a/identity_stardust/src/error.rs +++ b/identity_stardust/src/error.rs @@ -3,29 +3,41 @@ pub type Result = core::result::Result; -// TODO: replace all variants with specific errors? #[derive(Debug, thiserror::Error, strum::IntoStaticStr)] #[non_exhaustive] pub enum Error { + #[error("serialization error")] + SerializationError(#[source] identity_core::Error), #[error("{0}")] - CoreError(#[from] identity_core::Error), - #[error("{0}")] - CredError(#[from] identity_credential::Error), - #[error("{0}")] - InvalidDID(#[from] identity_did::did::DIDError), + DIDSyntaxError(#[from] identity_did::did::DIDError), #[error("{0}")] InvalidDoc(#[from] identity_did::Error), #[cfg(feature = "iota-client")] - #[error("{0}")] - ClientError(#[from] iota_client::error::Error), + #[error("DID update failed")] + DIDUpdateError(#[source] iota_client::error::Error), + #[cfg(feature = "iota-client")] + #[error("DID resolution failed")] + DIDResolutionError(#[source] iota_client::error::Error), #[cfg(feature = "iota-client")] #[error("{0}")] - BlockError(#[from] iota_client::block::Error), - - #[error("invalid network name")] - InvalidNetworkName, + BasicOutputBuildError(#[source] iota_client::block::Error), + #[error("\"{0}\" is not a valid network name")] + InvalidNetworkName(String), + #[error("unable to resolve a `{expected}` DID on network `{actual}`")] + NetworkMismatch { expected: String, actual: String }, #[error("invalid state metadata {0}")] InvalidStateMetadata(&'static str), #[error("credential revocation error")] RevocationError(#[source] identity_did::Error), + #[cfg(feature = "iota-client")] + #[error("alias output build error")] + AliasOutputBuildError(#[source] iota_client::block::Error), + #[cfg(feature = "iota-client")] + #[error("output with id `{0}` is not an alias output")] + NotAnAliasOutput(iota_client::block::output::OutputId), + #[cfg(feature = "iota-client")] + #[error("converting a DTO to an output failed")] + OutputConversionError(#[source] iota_client::block::DtoError), + #[error("conversion to an OutputId failed: {0}")] + OutputIdConversionError(String), } diff --git a/identity_stardust/src/lib.rs b/identity_stardust/src/lib.rs index eb97610549..7000c3a315 100644 --- a/identity_stardust/src/lib.rs +++ b/identity_stardust/src/lib.rs @@ -4,6 +4,8 @@ #![forbid(unsafe_code)] #![allow(clippy::upper_case_acronyms)] +#[cfg(feature = "iota-client")] +pub use client::*; pub use did::StardustDID; pub use did::StardustDIDUrl; pub use document::*; @@ -13,6 +15,8 @@ pub use state_metadata::*; pub use self::error::Error; pub use self::error::Result; +#[cfg(feature = "iota-client")] +mod client; mod did; mod document; mod error; diff --git a/identity_stardust/src/network/network_name.rs b/identity_stardust/src/network/network_name.rs index f360fe739a..1b084b21ab 100644 --- a/identity_stardust/src/network/network_name.rs +++ b/identity_stardust/src/network/network_name.rs @@ -41,7 +41,7 @@ impl NetworkName { && (name.len() <= Self::MAX_LENGTH) && name.chars().all(|ch| ch.is_ascii_lowercase() || ch.is_ascii_digit()) }) - .ok_or(Error::InvalidNetworkName) + .ok_or_else(|| Error::InvalidNetworkName(name.to_owned())) } } diff --git a/identity_stardust/src/state_metadata/document.rs b/identity_stardust/src/state_metadata/document.rs index b11292528a..484b93096c 100644 --- a/identity_stardust/src/state_metadata/document.rs +++ b/identity_stardust/src/state_metadata/document.rs @@ -55,10 +55,10 @@ impl StateMetadataDocument { } /// Pack a [`StateMetadataDocument`] into bytes, suitable for inclusion in - /// an alias output's state metadata, according to the given `encoding`. + /// an Alias Output's state metadata, according to the given `encoding`. pub fn pack(self, encoding: StateMetadataEncoding) -> Result> { let encoded_message_data: Vec = match encoding { - StateMetadataEncoding::Json => self.to_json_vec()?, + StateMetadataEncoding::Json => self.to_json_vec().map_err(Error::SerializationError)?, }; // Prepend flags. @@ -97,7 +97,7 @@ impl StateMetadataDocument { ))?; match encoding { - StateMetadataEncoding::Json => StateMetadataDocument::from_json_slice(inner).map_err(Into::into), + StateMetadataEncoding::Json => StateMetadataDocument::from_json_slice(inner).map_err(Error::SerializationError), } } } From 3cef08b8c5d9847aedf9a54fee0c547fb45ecbbe Mon Sep 17 00:00:00 2001 From: Henrique Nogara Date: Mon, 15 Aug 2022 10:00:32 -0300 Subject: [PATCH 35/89] Change `Storage` to handle `CoreDID` (#968) * Replace IotaDID for CoreDID in storage; Add WasmCoreDID for the bindings * Change doc comments * Clippy, fmt, and doc fixes * Improve doc comments; Add match for DidType * Fix clippy warnings * Fix broken links * Fix broken links * Derive Eq - clippy warning * Ignore clippy::derive_partial_eq_without_eq for derive_enum_tests; Update docs * Add Eq derive for stardust doc * Derive Eq for libjose structs * Add retry until included for stardust --- bindings/stronghold-nodejs/Cargo.lock | 125 +---- bindings/stronghold-nodejs/Cargo.toml | 1 + bindings/stronghold-nodejs/js/stronghold.ts | 73 +-- bindings/stronghold-nodejs/package-lock.json | 152 +++++- bindings/stronghold-nodejs/src/stronghold.rs | 43 +- .../stronghold-nodejs/src/types/derive.rs | 4 +- .../src/types/did_location_tuple.rs | 14 +- .../stronghold-nodejs/src/types/did_type.rs | 19 + bindings/stronghold-nodejs/src/types/mod.rs | 2 + bindings/wasm/docs/api-reference.md | 482 ++++++++++-------- bindings/wasm/src/account/storage/traits.rs | 110 ++-- bindings/wasm/src/account/types/did_type.rs | 28 + bindings/wasm/src/account/types/mod.rs | 2 + .../wasm/src/account/wasm_account/account.rs | 8 +- .../account/wasm_account/account_builder.rs | 4 +- .../src/credential/credential_validator.rs | 6 +- .../src/credential/presentation_validator.rs | 6 +- bindings/wasm/src/did/mod.rs | 8 +- bindings/wasm/src/did/wasm_core_did.rs | 48 ++ bindings/wasm/src/did/wasm_did_url.rs | 6 +- bindings/wasm/src/did/wasm_diff_message.rs | 8 +- bindings/wasm/src/did/wasm_document.rs | 8 +- .../src/did/{wasm_did.rs => wasm_iota_did.rs} | 46 +- .../wasm/src/did/wasm_verification_method.rs | 12 +- bindings/wasm/src/tangle/client.rs | 6 +- bindings/wasm/src/tangle/explorer.rs | 4 +- bindings/wasm/src/tangle/resolver.rs | 6 +- bindings/wasm/tests/wasm.rs | 15 +- identity_account/src/account/account.rs | 27 +- identity_account/src/tests/account.rs | 21 +- identity_account/src/tests/updates.rs | 36 +- identity_account/src/updates/update.rs | 19 +- identity_account_storage/src/crypto/remote.rs | 6 +- .../src/identity/chain_state.rs | 2 +- .../src/storage/memstore.rs | 44 +- .../src/storage/stronghold.rs | 56 +- .../src/storage/test_suite.rs | 81 +-- .../src/storage/traits.rs | 36 +- .../src/stronghold/client_path.rs | 7 + .../src/stronghold/tests.rs | 4 +- .../src/stronghold/wrapper.rs | 4 +- .../src/types/did_type.rs | 9 + identity_account_storage/src/types/mod.rs | 2 + identity_core/src/common/context.rs | 2 +- identity_core/src/common/one_or_many.rs | 6 +- identity_core/src/common/one_or_set.rs | 2 +- identity_core/src/crypto/proof/proof_value.rs | 12 +- .../src/credential/credential.rs | 2 +- .../src/credential/evidence.rs | 2 +- identity_credential/src/credential/issuer.rs | 4 +- identity_credential/src/credential/policy.rs | 2 +- identity_credential/src/credential/refresh.rs | 2 +- .../credential/revocation_bitmap_status.rs | 2 +- identity_credential/src/credential/schema.rs | 2 +- identity_credential/src/credential/status.rs | 2 +- identity_credential/src/credential/subject.rs | 2 +- .../src/presentation/presentation.rs | 2 +- identity_did/src/diff/method_data.rs | 2 +- identity_did/src/document/core_document.rs | 2 +- identity_did/src/service/service.rs | 2 +- identity_did/src/verification/method_data.rs | 2 +- identity_did/src/verification/method_ref.rs | 2 +- .../src/verification/verification_method.rs | 2 +- identity_diff/tests/derive_enum_test.rs | 1 + .../src/document/resolved_iota_document.rs | 2 +- .../src/tangle/message/message_ext.rs | 2 +- identity_iota_client/src/tangle/receipt.rs | 2 +- identity_iota_core/src/did/segments.rs | 2 +- .../src/document/iota_document.rs | 2 +- .../src/document/iota_document_metadata.rs | 2 +- .../examples/ex3_deactivate_did.rs | 3 - identity_stardust/examples/ex4_delete_did.rs | 3 - identity_stardust/src/client/client_ext.rs | 7 +- .../src/document/stardust_document.rs | 2 +- libjose/src/jwe/header.rs | 2 +- libjose/src/jwk/key.rs | 2 +- libjose/src/jwk/key_set.rs | 2 +- libjose/src/jwm/attributes.rs | 2 +- libjose/src/jws/decoder.rs | 2 +- libjose/src/jws/header.rs | 2 +- libjose/src/jwt/claims.rs | 2 +- libjose/src/jwt/header.rs | 2 +- 82 files changed, 1021 insertions(+), 679 deletions(-) create mode 100644 bindings/stronghold-nodejs/src/types/did_type.rs create mode 100644 bindings/wasm/src/account/types/did_type.rs create mode 100644 bindings/wasm/src/did/wasm_core_did.rs rename bindings/wasm/src/did/{wasm_did.rs => wasm_iota_did.rs} (80%) create mode 100644 identity_account_storage/src/types/did_type.rs diff --git a/bindings/stronghold-nodejs/Cargo.lock b/bindings/stronghold-nodejs/Cargo.lock index cfd7a20ea0..e1755bcd21 100644 --- a/bindings/stronghold-nodejs/Cargo.lock +++ b/bindings/stronghold-nodejs/Cargo.lock @@ -189,6 +189,15 @@ dependencies = [ "opaque-debug", ] +[[package]] +name = "blake2" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9cf849ee05b2ee5fba5e36f97ff8ec2533916700fc0758d40d92136a42f3388" +dependencies = [ + "digest 0.10.3", +] + [[package]] name = "block-buffer" version = "0.9.0" @@ -720,6 +729,7 @@ version = "0.6.0" dependencies = [ "identity_account_storage", "identity_core", + "identity_did", "identity_iota_core", "napi", "napi-build", @@ -738,10 +748,9 @@ dependencies = [ "identity_core", "identity_did", "identity_iota_core", - "iota-crypto 0.8.0", + "iota-crypto 0.12.1", "iota_stronghold", "once_cell", - "parking_lot", "rand", "seahash", "serde", @@ -756,7 +765,7 @@ name = "identity_core" version = "0.6.0" dependencies = [ "identity-diff", - "iota-crypto 0.8.0", + "iota-crypto 0.12.1", "js-sys", "multibase", "serde", @@ -789,7 +798,7 @@ dependencies = [ "bee-message", "identity_core", "identity_did", - "iota-crypto 0.8.0", + "iota-crypto 0.12.1", "lazy_static", "serde", "strum", @@ -825,17 +834,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee273ae67ff1bc7c59317c0ab280e0e76259e6bd83e140ac4c2ecebec994f740" dependencies = [ "aead", - "aes", - "aes-gcm", - "blake2", + "blake2 0.9.2", "chacha20poly1305", "curve25519-dalek", "digest 0.9.0", - "ed25519-zebra 2.2.0", "generic-array", "getrandom 0.2.6", "hmac 0.11.0", - "pbkdf2 0.8.0", "sha2 0.9.9", "x25519-dalek", ] @@ -847,7 +852,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c98a3248cde6b42cb479a52089fe4fc20d12de51b8edd923294cd5240ec89b0" dependencies = [ "bee-ternary", - "blake2", + "blake2 0.9.2", "digest 0.9.0", "ed25519-zebra 2.2.0", "lazy_static", @@ -862,6 +867,7 @@ dependencies = [ "aead", "aes", "aes-gcm", + "blake2 0.10.4", "chacha20poly1305", "curve25519-dalek", "digest 0.10.3", @@ -869,7 +875,7 @@ dependencies = [ "generic-array", "getrandom 0.2.6", "hmac 0.12.1", - "pbkdf2 0.11.0", + "pbkdf2", "serde", "sha2 0.10.2", "unicode-normalization", @@ -947,16 +953,6 @@ dependencies = [ "walkdir", ] -[[package]] -name = "lock_api" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" -dependencies = [ - "autocfg", - "scopeguard", -] - [[package]] name = "log" version = "0.4.17" @@ -1087,44 +1083,12 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-sys", -] - [[package]] name = "paste" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc" -[[package]] -name = "pbkdf2" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" -dependencies = [ - "crypto-mac 0.11.1", -] - [[package]] name = "pbkdf2" version = "0.11.0" @@ -1308,12 +1272,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - [[package]] name = "seahash" version = "4.1.0" @@ -1392,12 +1350,6 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" -[[package]] -name = "smallvec" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" - [[package]] name = "spin" version = "0.5.2" @@ -1759,49 +1711,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" -dependencies = [ - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" - -[[package]] -name = "windows_i686_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" - -[[package]] -name = "windows_i686_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" - [[package]] name = "x25519-dalek" version = "1.1.1" diff --git a/bindings/stronghold-nodejs/Cargo.toml b/bindings/stronghold-nodejs/Cargo.toml index 4cec0a71ab..f46374b402 100644 --- a/bindings/stronghold-nodejs/Cargo.toml +++ b/bindings/stronghold-nodejs/Cargo.toml @@ -9,6 +9,7 @@ crate-type = ["cdylib"] [dependencies] identity_account_storage = { version = "=0.6.0", path = "../../identity_account_storage", default-features = false, features = ["stronghold", "send-sync-storage", "encryption"] } identity_core = { version = "=0.6.0", path = "../../identity_core", default-features = false } +identity_did = { version = "=0.6.0", path = "../../identity_did", default-features = false } identity_iota_core = { version = "=0.6.0", path = "../../identity_iota_core", default-features = false } napi = { version = "2.4.3", default-features = false, features = ["napi4", "tokio_rt", "serde-json"] } napi-derive = { version = "2.4.1", default-features = false, features = ["compat-mode", "full"] } diff --git a/bindings/stronghold-nodejs/js/stronghold.ts b/bindings/stronghold-nodejs/js/stronghold.ts index 2a463d3dd5..a6ffc3af58 100644 --- a/bindings/stronghold-nodejs/js/stronghold.ts +++ b/bindings/stronghold-nodejs/js/stronghold.ts @@ -1,5 +1,5 @@ -import { NapiStronghold, NapiDid, NapiKeyLocation, NapiKeyType, NapiDidLocation, NapiEncryptedData, NapiEncryptionAlgorithm, NapiCekAlgorithm } from '../napi-dist/napi'; -import { DID, KeyLocation, Signature, ChainState, Storage, KeyType, Document, EncryptedData, EncryptionAlgorithm, CekAlgorithm } from "@iota/identity-wasm/node"; +import { NapiStronghold, NapiCoreDid, NapiDIDType, NapiKeyLocation, NapiKeyType, NapiDidLocation, NapiEncryptedData, NapiEncryptionAlgorithm, NapiCekAlgorithm } from '../napi-dist/napi'; +import { CoreDID, DIDType, KeyLocation, Signature, Storage, KeyType, EncryptedData, EncryptionAlgorithm, CekAlgorithm } from "@iota/identity-wasm/node"; export class Stronghold implements Storage { private napiStronghold: NapiStronghold; @@ -16,38 +16,47 @@ export class Stronghold implements Storage { return stronghold } - public async didCreate(network: string, fragment: string, private_key?: Uint8Array): Promise<[DID, KeyLocation]> { + public async didCreate(didType: DIDType, network: string, fragment: string, private_key?: Uint8Array): Promise<[CoreDID, KeyLocation]> { let optPrivateKey = undefined; if (private_key) { optPrivateKey = Array.from(private_key) } - const napiDidLocation: NapiDidLocation = await this.napiStronghold.didCreate(network, fragment, optPrivateKey); + let napiDIDType: NapiDIDType | undefined = undefined; + switch (didType) { + case DIDType.IotaDID: + napiDIDType = NapiDIDType.IotaDID; + break; + default: + throw new Error("unexpected did type"); + } + + const napiDidLocation: NapiDidLocation = await this.napiStronghold.didCreate(napiDIDType, network, fragment, optPrivateKey); - const did: DID = DID.fromJSON(napiDidLocation.did().toJSON()); + const did: CoreDID = CoreDID.fromJSON(napiDidLocation.did().toJSON()); const location: KeyLocation = KeyLocation.fromJSON(napiDidLocation.keyLocation().toJSON()); return [did, location]; } - public async didPurge(did: DID): Promise { - const napiDID: NapiDid = NapiDid.fromJSON(did.toJSON()); + public async didPurge(did: CoreDID): Promise { + const napiDID: NapiCoreDid = NapiCoreDid.fromJSON(did.toJSON()); return this.napiStronghold.didPurge(napiDID); } - public async didExists(did: DID): Promise { - const napiDID: NapiDid = NapiDid.fromJSON(did.toJSON()); + public async didExists(did: CoreDID): Promise { + const napiDID: NapiCoreDid = NapiCoreDid.fromJSON(did.toJSON()); return this.napiStronghold.didExists(napiDID); } - public async didList(): Promise> { - const napiDids: Array = await this.napiStronghold.didList(); - const dids: Array = napiDids.map((did) => DID.fromJSON(did.toJSON())) + public async didList(): Promise> { + const napiDids: Array = await this.napiStronghold.didList(); + const dids: Array = napiDids.map((did) => CoreDID.fromJSON(did.toJSON())) return dids; } - public async keyGenerate(did: DID, keyType: KeyType, fragment: string): Promise { - const napiDID = NapiDid.fromJSON(did.toJSON()); + public async keyGenerate(did: CoreDID, keyType: KeyType, fragment: string): Promise { + const napiDID = NapiCoreDid.fromJSON(did.toJSON()); let napiKeyType: NapiKeyType | undefined = undefined; switch (keyType) { @@ -65,48 +74,48 @@ export class Stronghold implements Storage { return KeyLocation.fromJSON(napiKeyLocation.toJSON()); } - public async keyInsert(did: DID, keyLocation: KeyLocation, privateKey: Uint8Array): Promise { - const napiDID: NapiDid = NapiDid.fromJSON(did.toJSON()); + public async keyInsert(did: CoreDID, keyLocation: KeyLocation, privateKey: Uint8Array): Promise { + const napiDID: NapiCoreDid = NapiCoreDid.fromJSON(did.toJSON()); const napiKeyLocation: NapiKeyLocation = NapiKeyLocation.fromJSON(keyLocation.toJSON()); await this.napiStronghold.keyInsert(napiDID, napiKeyLocation, Array.from(privateKey)); } - public async keyExists(did: DID, keyLocation: KeyLocation): Promise { - const napiDID: NapiDid = NapiDid.fromJSON(did.toJSON()); + public async keyExists(did: CoreDID, keyLocation: KeyLocation): Promise { + const napiDID: NapiCoreDid = NapiCoreDid.fromJSON(did.toJSON()); const napiKeyLocation: NapiKeyLocation = NapiKeyLocation.fromJSON(keyLocation.toJSON()); return this.napiStronghold.keyExists(napiDID, napiKeyLocation) } - public async keyPublic(did: DID, keyLocation: KeyLocation): Promise { - const napiDID = NapiDid.fromJSON(did.toJSON()); + public async keyPublic(did: CoreDID, keyLocation: KeyLocation): Promise { + const napiDID = NapiCoreDid.fromJSON(did.toJSON()); const napiKeyLocation = NapiKeyLocation.fromJSON(keyLocation.toJSON()); const publicKey = await this.napiStronghold.keyPublic(napiDID, napiKeyLocation); return Uint8Array.from(publicKey); } - public async keyDelete(did: DID, keyLocation: KeyLocation): Promise { - const napiDID: NapiDid = NapiDid.fromJSON(did.toJSON()); + public async keyDelete(did: CoreDID, keyLocation: KeyLocation): Promise { + const napiDID: NapiCoreDid = NapiCoreDid.fromJSON(did.toJSON()); const napiKeyLocation = NapiKeyLocation.fromJSON(keyLocation.toJSON()); return this.napiStronghold.keyDelete(napiDID, napiKeyLocation); } - public async keySign(did: DID, keyLocation: KeyLocation, data: Uint8Array): Promise { - const napiDID: NapiDid = NapiDid.fromJSON(did.toJSON()); + public async keySign(did: CoreDID, keyLocation: KeyLocation, data: Uint8Array): Promise { + const napiDID: NapiCoreDid = NapiCoreDid.fromJSON(did.toJSON()); const napiKeyLocation = NapiKeyLocation.fromJSON(keyLocation.toJSON()); const napiSignature = await this.napiStronghold.keySign(napiDID, napiKeyLocation, Array.from(data)); return Signature.fromJSON(napiSignature.toJSON()); } - public async dataEncrypt(did: DID, plaintext: Uint8Array, associatedData: Uint8Array, encryptionAlgorithm: EncryptionAlgorithm, cekAlgorithm: CekAlgorithm, publicKey: Uint8Array): Promise { - const napiDID: NapiDid = NapiDid.fromJSON(did.toJSON()); + public async dataEncrypt(did: CoreDID, plaintext: Uint8Array, associatedData: Uint8Array, encryptionAlgorithm: EncryptionAlgorithm, cekAlgorithm: CekAlgorithm, publicKey: Uint8Array): Promise { + const napiDID: NapiCoreDid = NapiCoreDid.fromJSON(did.toJSON()); const napiCekAlgorithm = NapiCekAlgorithm.fromJSON(cekAlgorithm.toJSON()); const napiEncryptionAlgorithm = NapiEncryptionAlgorithm.fromJSON(encryptionAlgorithm.toJSON()); const napiEncryptedData = await this.napiStronghold.dataEncrypt(napiDID, Array.from(plaintext), Array.from(associatedData), napiEncryptionAlgorithm, napiCekAlgorithm, Array.from(publicKey)); return EncryptedData.fromJSON(napiEncryptedData.toJSON()); } - public async dataDecrypt(did: DID, data: EncryptedData, encryptionAlgorithm: EncryptionAlgorithm, cekAlgorithm: CekAlgorithm, privateKey: KeyLocation): Promise { - const napiDID: NapiDid = NapiDid.fromJSON(did.toJSON()); + public async dataDecrypt(did: CoreDID, data: EncryptedData, encryptionAlgorithm: EncryptionAlgorithm, cekAlgorithm: CekAlgorithm, privateKey: KeyLocation): Promise { + const napiDID: NapiCoreDid = NapiCoreDid.fromJSON(did.toJSON()); const napiPrivateKeyLocation = NapiKeyLocation.fromJSON(privateKey.toJSON()); const napiCekAlgorithm = NapiCekAlgorithm.fromJSON(cekAlgorithm.toJSON()); const napiEncryptionAlgorithm = NapiEncryptionAlgorithm.fromJSON(encryptionAlgorithm.toJSON()); @@ -115,8 +124,8 @@ export class Stronghold implements Storage { return Uint8Array.from(decryptedData); } - public async blobGet(did: DID): Promise { - const napiDID: NapiDid = NapiDid.fromJSON(did.toJSON()); + public async blobGet(did: CoreDID): Promise { + const napiDID: NapiCoreDid = NapiCoreDid.fromJSON(did.toJSON()); const value: number[] | undefined = await this.napiStronghold.blobGet(napiDID); if (value) { @@ -126,8 +135,8 @@ export class Stronghold implements Storage { } } - public async blobSet(did: DID, value: Uint8Array): Promise { - const napiDID: NapiDid = NapiDid.fromJSON(did.toJSON()); + public async blobSet(did: CoreDID, value: Uint8Array): Promise { + const napiDID: NapiCoreDid = NapiCoreDid.fromJSON(did.toJSON()); return this.napiStronghold.blobSet(napiDID, Array.from(value)); } diff --git a/bindings/stronghold-nodejs/package-lock.json b/bindings/stronghold-nodejs/package-lock.json index eebe55543b..c00da9ab6a 100644 --- a/bindings/stronghold-nodejs/package-lock.json +++ b/bindings/stronghold-nodejs/package-lock.json @@ -8,6 +8,9 @@ "name": "@iota/identity-stronghold-nodejs", "version": "0.6.0", "license": "Apache-2.0", + "dependencies": { + "@iota/identity-stronghold-nodejs": "^0.6.0" + }, "devDependencies": { "@napi-rs/cli": "^2.4.2", "@rollup/plugin-commonjs": "^21.0.2", @@ -26,7 +29,7 @@ "node": ">= 16" }, "peerDependencies": { - "@iota/identity-wasm": "0.5.0" + "@iota/identity-wasm": "0.6.0" } }, "node_modules/@babel/code-frame": { @@ -166,10 +169,103 @@ "node": ">=12" } }, + "node_modules/@iota/identity-stronghold-nodejs": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@iota/identity-stronghold-nodejs/-/identity-stronghold-nodejs-0.6.0.tgz", + "integrity": "sha512-/lpGBn0EwepD4eeypCLuUnxdeSPj4u6GtN7huv83pDmNhzo0n8eKc7gacSezwZrrEntO6g2CX27vSMAHClMJNw==", + "engines": { + "node": ">= 16" + }, + "optionalDependencies": { + "@iota/identity-stronghold-nodejs-darwin-arm64": "0.6.0", + "@iota/identity-stronghold-nodejs-darwin-x64": "0.6.0", + "@iota/identity-stronghold-nodejs-linux-x64-gnu": "0.6.0", + "@iota/identity-stronghold-nodejs-linux-x64-musl": "0.6.0", + "@iota/identity-stronghold-nodejs-win32-x64-msvc": "0.6.0" + }, + "peerDependencies": { + "@iota/identity-wasm": "0.6.0" + } + }, + "node_modules/@iota/identity-stronghold-nodejs-darwin-arm64": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@iota/identity-stronghold-nodejs-darwin-arm64/-/identity-stronghold-nodejs-darwin-arm64-0.6.0.tgz", + "integrity": "sha512-8EMTimRnkRrAkcIHcTiICwsh/EthPW2IKUoZ2BSj8XxdNsPwgynjk5eBwsPKvjBivigpl7fCOe4ztgC8PmkN9Q==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 16" + } + }, + "node_modules/@iota/identity-stronghold-nodejs-darwin-x64": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@iota/identity-stronghold-nodejs-darwin-x64/-/identity-stronghold-nodejs-darwin-x64-0.6.0.tgz", + "integrity": "sha512-PXxRdBj7l9L3l6C/xNq2CGsMA8fI11ukZWYP5hhZ6rt0ZAwQqXOaIkjRT1IgboVuYgD0fRF9jdIqte/6phBDjA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 16" + } + }, + "node_modules/@iota/identity-stronghold-nodejs-linux-x64-gnu": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@iota/identity-stronghold-nodejs-linux-x64-gnu/-/identity-stronghold-nodejs-linux-x64-gnu-0.6.0.tgz", + "integrity": "sha512-eUgzADBcD9bhDn4dy3Tns4fFqZFc4AMXj7oVyCdL+Zv/8+BaciaVK6aDnZ7NW09ghcABMqlFY7OpV6fshQDBOQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 16" + } + }, + "node_modules/@iota/identity-stronghold-nodejs-linux-x64-musl": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@iota/identity-stronghold-nodejs-linux-x64-musl/-/identity-stronghold-nodejs-linux-x64-musl-0.6.0.tgz", + "integrity": "sha512-3wa0Pvj3mrqPdSb7C+FZSsudPSWmebnogZy+ehvBKMBJ3Kqu9UMuof0Xaj6Mc+Rky05wRniFhtd9FG2owYs9Eg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 16" + } + }, + "node_modules/@iota/identity-stronghold-nodejs-win32-x64-msvc": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@iota/identity-stronghold-nodejs-win32-x64-msvc/-/identity-stronghold-nodejs-win32-x64-msvc-0.6.0.tgz", + "integrity": "sha512-vuLEDNa/gafOlHp1Jng5k0l5A2WUfJrUDjRfTyexbx28JgiGYCNRmh0Yh0hp9yhaqOFCOd9ipXnTAmWV5zgBAg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 16" + } + }, "node_modules/@iota/identity-wasm": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@iota/identity-wasm/-/identity-wasm-0.5.0.tgz", - "integrity": "sha512-rEMWaK6ZivHdCx/6PXebCQhN2QrcH1sx/Oc+MzI2ZiDLP3HDkWJhopU/i52zvw67gBc0kPjXG7z1n2UNAseRiA==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@iota/identity-wasm/-/identity-wasm-0.6.0.tgz", + "integrity": "sha512-NRCJx8m1RmayBwz94Ais9YphCao6+5Q5CtCsOBpfMm1j+2Pgmeknq0uXG0CQSvG3lxtulmrr7qgYEPgf1j320g==", "peer": true, "dependencies": { "node-fetch": "^2.6.7" @@ -2938,10 +3034,52 @@ "@cspotcode/source-map-consumer": "0.8.0" } }, + "@iota/identity-stronghold-nodejs": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@iota/identity-stronghold-nodejs/-/identity-stronghold-nodejs-0.6.0.tgz", + "integrity": "sha512-/lpGBn0EwepD4eeypCLuUnxdeSPj4u6GtN7huv83pDmNhzo0n8eKc7gacSezwZrrEntO6g2CX27vSMAHClMJNw==", + "requires": { + "@iota/identity-stronghold-nodejs-darwin-arm64": "0.6.0", + "@iota/identity-stronghold-nodejs-darwin-x64": "0.6.0", + "@iota/identity-stronghold-nodejs-linux-x64-gnu": "0.6.0", + "@iota/identity-stronghold-nodejs-linux-x64-musl": "0.6.0", + "@iota/identity-stronghold-nodejs-win32-x64-msvc": "0.6.0" + } + }, + "@iota/identity-stronghold-nodejs-darwin-arm64": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@iota/identity-stronghold-nodejs-darwin-arm64/-/identity-stronghold-nodejs-darwin-arm64-0.6.0.tgz", + "integrity": "sha512-8EMTimRnkRrAkcIHcTiICwsh/EthPW2IKUoZ2BSj8XxdNsPwgynjk5eBwsPKvjBivigpl7fCOe4ztgC8PmkN9Q==", + "optional": true + }, + "@iota/identity-stronghold-nodejs-darwin-x64": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@iota/identity-stronghold-nodejs-darwin-x64/-/identity-stronghold-nodejs-darwin-x64-0.6.0.tgz", + "integrity": "sha512-PXxRdBj7l9L3l6C/xNq2CGsMA8fI11ukZWYP5hhZ6rt0ZAwQqXOaIkjRT1IgboVuYgD0fRF9jdIqte/6phBDjA==", + "optional": true + }, + "@iota/identity-stronghold-nodejs-linux-x64-gnu": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@iota/identity-stronghold-nodejs-linux-x64-gnu/-/identity-stronghold-nodejs-linux-x64-gnu-0.6.0.tgz", + "integrity": "sha512-eUgzADBcD9bhDn4dy3Tns4fFqZFc4AMXj7oVyCdL+Zv/8+BaciaVK6aDnZ7NW09ghcABMqlFY7OpV6fshQDBOQ==", + "optional": true + }, + "@iota/identity-stronghold-nodejs-linux-x64-musl": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@iota/identity-stronghold-nodejs-linux-x64-musl/-/identity-stronghold-nodejs-linux-x64-musl-0.6.0.tgz", + "integrity": "sha512-3wa0Pvj3mrqPdSb7C+FZSsudPSWmebnogZy+ehvBKMBJ3Kqu9UMuof0Xaj6Mc+Rky05wRniFhtd9FG2owYs9Eg==", + "optional": true + }, + "@iota/identity-stronghold-nodejs-win32-x64-msvc": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@iota/identity-stronghold-nodejs-win32-x64-msvc/-/identity-stronghold-nodejs-win32-x64-msvc-0.6.0.tgz", + "integrity": "sha512-vuLEDNa/gafOlHp1Jng5k0l5A2WUfJrUDjRfTyexbx28JgiGYCNRmh0Yh0hp9yhaqOFCOd9ipXnTAmWV5zgBAg==", + "optional": true + }, "@iota/identity-wasm": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@iota/identity-wasm/-/identity-wasm-0.5.0.tgz", - "integrity": "sha512-rEMWaK6ZivHdCx/6PXebCQhN2QrcH1sx/Oc+MzI2ZiDLP3HDkWJhopU/i52zvw67gBc0kPjXG7z1n2UNAseRiA==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@iota/identity-wasm/-/identity-wasm-0.6.0.tgz", + "integrity": "sha512-NRCJx8m1RmayBwz94Ais9YphCao6+5Q5CtCsOBpfMm1j+2Pgmeknq0uXG0CQSvG3lxtulmrr7qgYEPgf1j320g==", "peer": true, "requires": { "node-fetch": "^2.6.7" diff --git a/bindings/stronghold-nodejs/src/stronghold.rs b/bindings/stronghold-nodejs/src/stronghold.rs index 5a35dfcd5a..4b52446387 100644 --- a/bindings/stronghold-nodejs/src/stronghold.rs +++ b/bindings/stronghold-nodejs/src/stronghold.rs @@ -7,7 +7,7 @@ use identity_account_storage::types::EncryptedData; use identity_account_storage::types::KeyLocation; use identity_core::crypto::PrivateKey; use identity_core::crypto::PublicKey; -use identity_iota_core::did::IotaDID; +use identity_did::did::CoreDID; use identity_iota_core::tangle::NetworkName; use napi::bindgen_prelude::Error; use napi::Result; @@ -15,7 +15,8 @@ use napi_derive::napi; use crate::error::NapiResult; use crate::types::NapiCekAlgorithm; -use crate::types::NapiDid; +use crate::types::NapiCoreDid; +use crate::types::NapiDIDType; use crate::types::NapiDidLocation; use crate::types::NapiEncryptedData; use crate::types::NapiEncryptionAlgorithm; @@ -69,6 +70,7 @@ impl NapiStronghold { #[napi] pub async fn did_create( &self, + did_type: NapiDIDType, network: String, fragment: String, private_key: Option>, @@ -79,9 +81,9 @@ impl NapiStronghold { None => None, }; - let (did, location): (IotaDID, KeyLocation) = self + let (did, location): (CoreDID, KeyLocation) = self .0 - .did_create(network, fragment.as_ref(), private_key) + .did_create(did_type.into(), network, fragment.as_ref(), private_key) .await .napi_result()?; @@ -94,19 +96,19 @@ impl NapiStronghold { /// /// Returns `true` if the did and its associated data was removed, `false` if nothing was done. #[napi] - pub async fn did_purge(&self, did: &NapiDid) -> Result { + pub async fn did_purge(&self, did: &NapiCoreDid) -> Result { self.0.did_purge(&did.0).await.napi_result() } /// Returns `true` if `did` exists in the list of stored DIDs. #[napi] - pub async fn did_exists(&self, did: &NapiDid) -> Result { + pub async fn did_exists(&self, did: &NapiCoreDid) -> Result { self.0.did_exists(&did.0).await.napi_result() } /// Returns the list of stored DIDs. #[napi] - pub async fn did_list(&self) -> Result> { + pub async fn did_list(&self) -> Result> { Ok( self .0 @@ -114,7 +116,7 @@ impl NapiStronghold { .await .napi_result()? .into_iter() - .map(NapiDid) + .map(NapiCoreDid) .collect(), ) } @@ -122,7 +124,12 @@ impl NapiStronghold { /// Generates a new key for the given `did` with the given `key_type` and `fragment` identifier /// and returns the location of the newly generated key. #[napi] - pub async fn key_generate(&self, did: &NapiDid, key_type: NapiKeyType, fragment: String) -> Result { + pub async fn key_generate( + &self, + did: &NapiCoreDid, + key_type: NapiKeyType, + fragment: String, + ) -> Result { let location: KeyLocation = self .0 .key_generate(&did.0, key_type.into(), fragment.as_ref()) @@ -136,14 +143,14 @@ impl NapiStronghold { /// /// If a key at `location` exists, it is overwritten. #[napi] - pub async fn key_insert(&self, did: &NapiDid, location: &NapiKeyLocation, private_key: Vec) -> Result<()> { + pub async fn key_insert(&self, did: &NapiCoreDid, location: &NapiKeyLocation, private_key: Vec) -> Result<()> { let private_key: PrivateKey = private_key.try_into_bytes()?.into(); self.0.key_insert(&did.0, &location.0, private_key).await.napi_result() } /// Retrieves the public key from `location`. #[napi] - pub async fn key_public(&self, did: &NapiDid, location: &NapiKeyLocation) -> Result> { + pub async fn key_public(&self, did: &NapiCoreDid, location: &NapiKeyLocation) -> Result> { let public_key: PublicKey = self.0.key_public(&did.0, &location.0).await.napi_result()?; let public_key: Vec = public_key.as_ref().to_vec(); Ok(public_key.into_iter().map(u32::from).collect()) @@ -155,13 +162,13 @@ impl NapiStronghold { /// /// Returns `true` if it removed the key, `false` if nothing was done. #[napi] - pub async fn key_delete(&self, did: &NapiDid, location: &NapiKeyLocation) -> Result { + pub async fn key_delete(&self, did: &NapiCoreDid, location: &NapiKeyLocation) -> Result { self.0.key_delete(&did.0, &location.0).await.napi_result() } /// Signs `data` with the private key at the specified `location`. #[napi] - pub async fn key_sign(&self, did: &NapiDid, location: &NapiKeyLocation, data: Vec) -> Result { + pub async fn key_sign(&self, did: &NapiCoreDid, location: &NapiKeyLocation, data: Vec) -> Result { let data: Vec = data.try_into_bytes()?; self .0 @@ -173,7 +180,7 @@ impl NapiStronghold { /// Returns `true` if a key exists at the specified `location`. #[napi] - pub async fn key_exists(&self, did: &NapiDid, location: &NapiKeyLocation) -> Result { + pub async fn key_exists(&self, did: &NapiCoreDid, location: &NapiKeyLocation) -> Result { self.0.key_exists(&did.0, &location.0).await.napi_result() } @@ -183,7 +190,7 @@ impl NapiStronghold { #[napi] pub async fn data_encrypt( &self, - did: &NapiDid, + did: &NapiCoreDid, plaintext: Vec, associated_data: Vec, encryption_algorithm: &NapiEncryptionAlgorithm, @@ -214,7 +221,7 @@ impl NapiStronghold { #[napi] pub async fn data_decrypt( &self, - did: &NapiDid, + did: &NapiCoreDid, data: &NapiEncryptedData, encryption_algorithm: &NapiEncryptionAlgorithm, cek_algorithm: &NapiCekAlgorithm, @@ -236,7 +243,7 @@ impl NapiStronghold { /// Returns the blob stored by the identity specified by `did`. #[napi] - pub async fn blob_get(&self, did: &NapiDid) -> Result>> { + pub async fn blob_get(&self, did: &NapiCoreDid) -> Result>> { self .0 .blob_get(&did.0) @@ -247,7 +254,7 @@ impl NapiStronghold { /// Stores an arbitrary blob for the identity specified by `did`. #[napi] - pub async fn blob_set(&self, did: &NapiDid, blob: Vec) -> Result<()> { + pub async fn blob_set(&self, did: &NapiCoreDid, blob: Vec) -> Result<()> { let blob: Vec = blob.try_into_bytes()?; self.0.blob_set(&did.0, blob).await.napi_result() } diff --git a/bindings/stronghold-nodejs/src/types/derive.rs b/bindings/stronghold-nodejs/src/types/derive.rs index 4f95b9e934..a216aff066 100644 --- a/bindings/stronghold-nodejs/src/types/derive.rs +++ b/bindings/stronghold-nodejs/src/types/derive.rs @@ -7,7 +7,7 @@ use identity_account_storage::types::EncryptedData; use identity_account_storage::types::EncryptionAlgorithm; use identity_account_storage::types::KeyLocation; use identity_account_storage::types::Signature; -use identity_iota_core::did::IotaDID; +use identity_did::did::CoreDID; use identity_iota_core::document::IotaDocument; use napi::Result; use napi_derive::napi; @@ -46,7 +46,7 @@ derive_napi_class!(CekAlgorithm, NapiCekAlgorithm); derive_napi_class!(ChainState, NapiChainState); derive_napi_class!(EncryptionAlgorithm, NapiEncryptionAlgorithm); derive_napi_class!(EncryptedData, NapiEncryptedData); -derive_napi_class!(IotaDID, NapiDid); +derive_napi_class!(CoreDID, NapiCoreDid); derive_napi_class!(IotaDocument, NapiDocument); derive_napi_class!(KeyLocation, NapiKeyLocation); derive_napi_class!(Signature, NapiSignature); diff --git a/bindings/stronghold-nodejs/src/types/did_location_tuple.rs b/bindings/stronghold-nodejs/src/types/did_location_tuple.rs index a317521d4e..dd5a23e1d1 100644 --- a/bindings/stronghold-nodejs/src/types/did_location_tuple.rs +++ b/bindings/stronghold-nodejs/src/types/did_location_tuple.rs @@ -2,18 +2,18 @@ // SPDX-License-Identifier: Apache-2.0 use identity_account_storage::types::KeyLocation; -use identity_iota_core::did::IotaDID; +use identity_did::did::CoreDID; use napi::Result; use napi_derive::napi; use crate::error::NapiResult; -use crate::types::NapiDid; +use crate::types::NapiCoreDid; use crate::types::NapiKeyLocation; /// Workaround for lack of tuple support in Napi. #[derive(serde::Serialize, serde::Deserialize)] struct DIDLocation { - did: IotaDID, + did: CoreDID, location: KeyLocation, } @@ -23,8 +23,8 @@ pub struct NapiDidLocation(DIDLocation); #[napi] impl NapiDidLocation { #[napi] - pub fn did(&self) -> NapiDid { - NapiDid(self.0.did.clone()) + pub fn did(&self) -> NapiCoreDid { + NapiCoreDid(self.0.did.clone()) } #[napi(js_name = keyLocation)] @@ -45,8 +45,8 @@ impl NapiDidLocation { } } -impl From<(IotaDID, KeyLocation)> for NapiDidLocation { - fn from(tuple: (IotaDID, KeyLocation)) -> Self { +impl From<(CoreDID, KeyLocation)> for NapiDidLocation { + fn from(tuple: (CoreDID, KeyLocation)) -> Self { Self(DIDLocation { did: tuple.0, location: tuple.1, diff --git a/bindings/stronghold-nodejs/src/types/did_type.rs b/bindings/stronghold-nodejs/src/types/did_type.rs new file mode 100644 index 0000000000..fd736c49d3 --- /dev/null +++ b/bindings/stronghold-nodejs/src/types/did_type.rs @@ -0,0 +1,19 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use identity_account_storage::types::DIDType; +use napi::bindgen_prelude::ToNapiValue; +use napi_derive::napi; + +#[napi] +pub enum NapiDIDType { + IotaDID, +} + +impl From for DIDType { + fn from(other: NapiDIDType) -> Self { + match other { + NapiDIDType::IotaDID => DIDType::IotaDID, + } + } +} diff --git a/bindings/stronghold-nodejs/src/types/mod.rs b/bindings/stronghold-nodejs/src/types/mod.rs index c8b5f1a521..9486cb84c1 100644 --- a/bindings/stronghold-nodejs/src/types/mod.rs +++ b/bindings/stronghold-nodejs/src/types/mod.rs @@ -3,8 +3,10 @@ pub use self::derive::*; pub use self::did_location_tuple::NapiDidLocation; +pub use self::did_type::NapiDIDType; pub use self::key_type::*; mod derive; mod did_location_tuple; +mod did_type; mod key_type; diff --git a/bindings/wasm/docs/api-reference.md b/bindings/wasm/docs/api-reference.md index eb8926723e..80918f86d2 100644 --- a/bindings/wasm/docs/api-reference.md +++ b/bindings/wasm/docs/api-reference.md @@ -26,6 +26,9 @@ the configuration of previously built accounts.

Client
+
CoreDID
+

A Decentralized Identifier (DID).

+
Credential
CredentialValidationOptions
@@ -33,9 +36,6 @@ the configuration of previously built accounts.

CredentialValidator
-
DID
-

A DID conforming to the IOTA DID method specification.

-
DIDUrl

A DID URL conforming to the IOTA DID method specification.

@@ -67,6 +67,9 @@ the configuration of previously built accounts.

IntegrationChainHistory
+
IotaDID
+

A DID conforming to the IOTA DID method specification.

+
KeyLocation

The storage location of a verification method key.

A key is uniquely identified by the fragment and a hash of its public key. @@ -176,6 +179,9 @@ See IVerifierOptions.

StateMetadataEncoding
+
DIDType
+

Supported types representing a DID that can be generated by the storage interface.

+
StatusCheck

Controls validation behaviour when checking whether or not a credential has been revoked by its credentialStatus.

@@ -247,7 +253,7 @@ publishing to the Tangle. * [.createMethod(options)](#Account+createMethod) ⇒ Promise.<void> * [.attachMethodRelationships(options)](#Account+attachMethodRelationships) ⇒ Promise.<void> * [.detachMethodRelationships(options)](#Account+detachMethodRelationships) ⇒ Promise.<void> - * [.did()](#Account+did) ⇒ [DID](#DID) + * [.did()](#Account+did) ⇒ [IotaDID](#IotaDID) * [.autopublish()](#Account+autopublish) ⇒ boolean * [.autosave()](#Account+autosave) ⇒ [AutoSave](#AutoSave) * [.document()](#Account+document) ⇒ [Document](#Document) @@ -318,8 +324,8 @@ Detaches the given relationship from the given method, if the method exists. -### account.did() ⇒ [DID](#DID) -Returns the [DID](#DID) of the managed identity. +### account.did() ⇒ [IotaDID](#IotaDID) +Returns the [IotaDID](#IotaDID) of the managed identity. **Kind**: instance method of [Account](#Account) @@ -590,7 +596,7 @@ The identity must exist in the configured `Storage`. | Param | Type | | --- | --- | -| did | [DID](#DID) | +| did | [IotaDID](#IotaDID) | @@ -926,7 +932,7 @@ Fetch the DID document specified by the given `DID`. | Param | Type | | --- | --- | -| did | [DID](#DID) \| string | +| did | [IotaDID](#IotaDID) \| string | @@ -937,7 +943,7 @@ Returns the message history of the given DID. | Param | Type | | --- | --- | -| did | [DID](#DID) \| string | +| did | [IotaDID](#IotaDID) \| string | @@ -967,6 +973,66 @@ Creates a new `Client` with the given settings. | --- | --- | | config | IClientConfig | + + +## CoreDID +A Decentralized Identifier (DID). + +**Kind**: global class + +* [CoreDID](#CoreDID) + * _instance_ + * [.toString()](#CoreDID+toString) ⇒ string + * [.toJSON()](#CoreDID+toJSON) ⇒ any + * [.clone()](#CoreDID+clone) ⇒ [CoreDID](#CoreDID) + * _static_ + * [.parse(input)](#CoreDID.parse) ⇒ [CoreDID](#CoreDID) + * [.fromJSON(json)](#CoreDID.fromJSON) ⇒ [CoreDID](#CoreDID) + + + +### coreDID.toString() ⇒ string +Returns the `CoreDID` as a string. + +**Kind**: instance method of [CoreDID](#CoreDID) + + +### coreDID.toJSON() ⇒ any +Serializes this to a JSON object. + +**Kind**: instance method of [CoreDID](#CoreDID) + + +### coreDID.clone() ⇒ [CoreDID](#CoreDID) +Deep clones the object. + +**Kind**: instance method of [CoreDID](#CoreDID) + + +### CoreDID.parse(input) ⇒ [CoreDID](#CoreDID) +Parses a [`CoreDID`] from the given `input`. + +# Errors + +Returns `Err` if the input is not a valid [`CoreDID`]. + +**Kind**: static method of [CoreDID](#CoreDID) + +| Param | Type | +| --- | --- | +| input | string | + + + +### CoreDID.fromJSON(json) ⇒ [CoreDID](#CoreDID) +Deserializes an instance from a JSON object. + +**Kind**: static method of [CoreDID](#CoreDID) + +| Param | Type | +| --- | --- | +| json | any | + ## Credential @@ -1203,7 +1269,7 @@ Deserializes an instance from a JSON object. * [.verifySignature(credential, trusted_issuers, options)](#CredentialValidator.verifySignature) * [.check_subject_holder_relationship(credential, holder_url, relationship)](#CredentialValidator.check_subject_holder_relationship) * [.checkStatus(credential, trustedIssuers, statusCheck)](#CredentialValidator.checkStatus) - * [.extractIssuer(credential)](#CredentialValidator.extractIssuer) ⇒ [DID](#DID) + * [.extractIssuer(credential)](#CredentialValidator.extractIssuer) ⇒ [IotaDID](#IotaDID) @@ -1331,7 +1397,7 @@ Only supports `BitmapRevocation2022`. -### CredentialValidator.extractIssuer(credential) ⇒ [DID](#DID) +### CredentialValidator.extractIssuer(credential) ⇒ [IotaDID](#IotaDID) Utility for extracting the issuer field of a `Credential` as a DID. ### Errors @@ -1344,179 +1410,6 @@ Fails if the issuer field is not a valid DID. | --- | --- | | credential | [Credential](#Credential) | - - -## DID -A DID conforming to the IOTA DID method specification. - -**Kind**: global class - -* [DID](#DID) - * [new DID(public_key, network)](#new_DID_new) - * _instance_ - * [.network()](#DID+network) ⇒ [Network](#Network) - * [.networkStr()](#DID+networkStr) ⇒ string - * [.tag()](#DID+tag) ⇒ string - * [.scheme()](#DID+scheme) ⇒ string - * [.authority()](#DID+authority) ⇒ string - * [.method()](#DID+method) ⇒ string - * [.methodId()](#DID+methodId) ⇒ string - * [.join(segment)](#DID+join) ⇒ [DIDUrl](#DIDUrl) - * [.toUrl()](#DID+toUrl) ⇒ [DIDUrl](#DIDUrl) - * [.intoUrl()](#DID+intoUrl) ⇒ [DIDUrl](#DIDUrl) - * [.toString()](#DID+toString) ⇒ string - * [.toJSON()](#DID+toJSON) ⇒ any - * [.clone()](#DID+clone) ⇒ [DID](#DID) - * _static_ - * [.METHOD](#DID.METHOD) ⇒ string - * [.DEFAULT_NETWORK](#DID.DEFAULT_NETWORK) ⇒ string - * [.parse(input)](#DID.parse) ⇒ [DID](#DID) - * [.fromJSON(json)](#DID.fromJSON) ⇒ [DID](#DID) - - - -### new DID(public_key, network) -Creates a new `DID` from a public key. - - -| Param | Type | -| --- | --- | -| public_key | Uint8Array | -| network | string \| undefined | - - - -### did.network() ⇒ [Network](#Network) -Returns the Tangle network of the `DID`. - -**Kind**: instance method of [DID](#DID) - - -### did.networkStr() ⇒ string -Returns the Tangle network name of the `DID`. - -**Kind**: instance method of [DID](#DID) - - -### did.tag() ⇒ string -Returns a copy of the unique tag of the `DID`. - -**Kind**: instance method of [DID](#DID) - - -### did.scheme() ⇒ string -Returns the `DID` scheme. - -E.g. -- `"did:example:12345678" -> "did"` -- `"did:iota:main:12345678" -> "did"` - -**Kind**: instance method of [DID](#DID) - - -### did.authority() ⇒ string -Returns the `DID` authority: the method name and method-id. - -E.g. -- `"did:example:12345678" -> "example:12345678"` -- `"did:iota:main:12345678" -> "iota:main:12345678"` - -**Kind**: instance method of [DID](#DID) - - -### did.method() ⇒ string -Returns the `DID` method name. - -E.g. -- `"did:example:12345678" -> "example"` -- `"did:iota:main:12345678" -> "iota"` - -**Kind**: instance method of [DID](#DID) - - -### did.methodId() ⇒ string -Returns the `DID` method-specific ID. - -E.g. -- `"did:example:12345678" -> "12345678"` -- `"did:iota:main:12345678" -> "main:12345678"` - -**Kind**: instance method of [DID](#DID) - - -### did.join(segment) ⇒ [DIDUrl](#DIDUrl) -Construct a new `DIDUrl` by joining with a relative DID Url string. - -**Kind**: instance method of [DID](#DID) - -| Param | Type | -| --- | --- | -| segment | string | - - - -### did.toUrl() ⇒ [DIDUrl](#DIDUrl) -Clones the `DID` into a `DIDUrl`. - -**Kind**: instance method of [DID](#DID) - - -### did.intoUrl() ⇒ [DIDUrl](#DIDUrl) -Converts the `DID` into a `DIDUrl`, consuming it. - -**Kind**: instance method of [DID](#DID) - - -### did.toString() ⇒ string -Returns the `DID` as a string. - -**Kind**: instance method of [DID](#DID) - - -### did.toJSON() ⇒ any -Serializes this to a JSON object. - -**Kind**: instance method of [DID](#DID) - - -### did.clone() ⇒ [DID](#DID) -Deep clones the object. - -**Kind**: instance method of [DID](#DID) - - -### DID.METHOD ⇒ string -The IOTA DID method name (`"iota"`). - -**Kind**: static property of [DID](#DID) - - -### DID.DEFAULT\_NETWORK ⇒ string -The default Tangle network (`"main"`). - -**Kind**: static property of [DID](#DID) - - -### DID.parse(input) ⇒ [DID](#DID) -Parses a `DID` from the input string. - -**Kind**: static method of [DID](#DID) - -| Param | Type | -| --- | --- | -| input | string | - - - -### DID.fromJSON(json) ⇒ [DID](#DID) -Deserializes an instance from a JSON object. - -**Kind**: static method of [DID](#DID) - -| Param | Type | -| --- | --- | -| json | any | - ## DIDUrl @@ -1526,7 +1419,7 @@ A DID URL conforming to the IOTA DID method specification. * [DIDUrl](#DIDUrl) * _instance_ - * [.did()](#DIDUrl+did) ⇒ [DID](#DID) + * [.did()](#DIDUrl+did) ⇒ [IotaDID](#IotaDID) * [.urlStr()](#DIDUrl+urlStr) ⇒ string * [.fragment()](#DIDUrl+fragment) ⇒ string \| undefined * [.setFragment(value)](#DIDUrl+setFragment) @@ -1544,7 +1437,7 @@ A DID URL conforming to the IOTA DID method specification. -### didUrl.did() ⇒ [DID](#DID) +### didUrl.did() ⇒ [IotaDID](#IotaDID) Return a copy of the `DID` section of the `DIDUrl`. **Kind**: instance method of [DIDUrl](#DIDUrl) @@ -1723,8 +1616,8 @@ Defines the difference between two DID `Document`s' JSON representations. * ~~[DiffMessage](#DiffMessage)~~ * _instance_ - * ~~[.id()](#DiffMessage+id) ⇒ [DID](#DID)~~ - * ~~[.did()](#DiffMessage+did) ⇒ [DID](#DID)~~ + * ~~[.id()](#DiffMessage+id) ⇒ [IotaDID](#IotaDID)~~ + * ~~[.did()](#DiffMessage+did) ⇒ [IotaDID](#IotaDID)~~ * ~~[.diff()](#DiffMessage+diff) ⇒ string~~ * ~~[.messageId()](#DiffMessage+messageId) ⇒ string~~ * ~~[.setMessageId(message_id)](#DiffMessage+setMessageId)~~ @@ -1739,7 +1632,7 @@ Defines the difference between two DID `Document`s' JSON representations. -### ~~diffMessage.id() ⇒ [DID](#DID)~~ +### ~~diffMessage.id() ⇒ [IotaDID](#IotaDID)~~ ***Deprecated*** Returns the DID of the associated DID Document. @@ -1749,7 +1642,7 @@ NOTE: clones the data. **Kind**: instance method of [DiffMessage](#DiffMessage) -### ~~diffMessage.did() ⇒ [DID](#DID)~~ +### ~~diffMessage.did() ⇒ [IotaDID](#IotaDID)~~ ***Deprecated*** Returns a copy of the DID of the associated DID Document. @@ -1858,9 +1751,9 @@ Deserializes an instance from a JSON object. * [Document](#Document) * [new Document(keypair, network, fragment)](#new_Document_new) * _instance_ - * [.id()](#Document+id) ⇒ [DID](#DID) + * [.id()](#Document+id) ⇒ [IotaDID](#IotaDID) * [.setController(controllers)](#Document+setController) - * [.controller()](#Document+controller) ⇒ [Array.<DID>](#DID) + * [.controller()](#Document+controller) ⇒ Array.<DID> * [.setAlsoKnownAs(urls)](#Document+setAlsoKnownAs) * [.alsoKnownAs()](#Document+alsoKnownAs) ⇒ Array.<string> * [.setPropertyUnchecked(key, value)](#Document+setPropertyUnchecked) @@ -1936,7 +1829,7 @@ Arguments: -### document.id() ⇒ [DID](#DID) +### document.id() ⇒ [IotaDID](#IotaDID) Returns a copy of the DID Document `id`. **Kind**: instance method of [Document](#Document) @@ -1952,11 +1845,11 @@ Use `null` to remove all controllers. | Param | Type | | --- | --- | -| controllers | [DID](#DID) \| [Array.<DID>](#DID) \| null | +| controllers | DID \| Array.<DID> \| null | -### document.controller() ⇒ [Array.<DID>](#DID) +### document.controller() ⇒ Array.<DID> Returns a copy of the list of document controllers. **Kind**: instance method of [Document](#Document) @@ -2934,7 +2827,7 @@ E.g. https://explorer.iota.org/mainnet/identity-resolver/{did} | Param | Type | | --- | --- | -| did | [DID](#DID) \| string | +| did | [IotaDID](#IotaDID) \| string | @@ -3029,6 +2922,179 @@ Deserializes from a JSON object. | --- | --- | | json | any | + + +## IotaDID +A DID conforming to the IOTA DID method specification. + +**Kind**: global class + +* [IotaDID](#IotaDID) + * [new IotaDID(public_key, network)](#new_IotaDID_new) + * _instance_ + * [.network()](#IotaDID+network) ⇒ [Network](#Network) + * [.networkStr()](#IotaDID+networkStr) ⇒ string + * [.tag()](#IotaDID+tag) ⇒ string + * [.scheme()](#IotaDID+scheme) ⇒ string + * [.authority()](#IotaDID+authority) ⇒ string + * [.method()](#IotaDID+method) ⇒ string + * [.methodId()](#IotaDID+methodId) ⇒ string + * [.join(segment)](#IotaDID+join) ⇒ [DIDUrl](#DIDUrl) + * [.toUrl()](#IotaDID+toUrl) ⇒ [DIDUrl](#DIDUrl) + * [.intoUrl()](#IotaDID+intoUrl) ⇒ [DIDUrl](#DIDUrl) + * [.toString()](#IotaDID+toString) ⇒ string + * [.toJSON()](#IotaDID+toJSON) ⇒ any + * [.clone()](#IotaDID+clone) ⇒ [IotaDID](#IotaDID) + * _static_ + * [.METHOD](#IotaDID.METHOD) ⇒ string + * [.DEFAULT_NETWORK](#IotaDID.DEFAULT_NETWORK) ⇒ string + * [.parse(input)](#IotaDID.parse) ⇒ [IotaDID](#IotaDID) + * [.fromJSON(json)](#IotaDID.fromJSON) ⇒ [IotaDID](#IotaDID) + + + +### new IotaDID(public_key, network) +Creates a new `DID` from a public key. + + +| Param | Type | +| --- | --- | +| public_key | Uint8Array | +| network | string \| undefined | + + + +### iotaDID.network() ⇒ [Network](#Network) +Returns the Tangle network of the `IotaDID`. + +**Kind**: instance method of [IotaDID](#IotaDID) + + +### iotaDID.networkStr() ⇒ string +Returns the Tangle network name of the `IotaDID`. + +**Kind**: instance method of [IotaDID](#IotaDID) + + +### iotaDID.tag() ⇒ string +Returns a copy of the unique tag of the `IotaDID`. + +**Kind**: instance method of [IotaDID](#IotaDID) + + +### iotaDID.scheme() ⇒ string +Returns the `DID` scheme. + +E.g. +- `"did:example:12345678" -> "did"` +- `"did:iota:main:12345678" -> "did"` + +**Kind**: instance method of [IotaDID](#IotaDID) + + +### iotaDID.authority() ⇒ string +Returns the `DID` authority: the method name and method-id. + +E.g. +- `"did:example:12345678" -> "example:12345678"` +- `"did:iota:main:12345678" -> "iota:main:12345678"` + +**Kind**: instance method of [IotaDID](#IotaDID) + + +### iotaDID.method() ⇒ string +Returns the `DID` method name. + +E.g. +- `"did:example:12345678" -> "example"` +- `"did:iota:main:12345678" -> "iota"` + +**Kind**: instance method of [IotaDID](#IotaDID) + + +### iotaDID.methodId() ⇒ string +Returns the `DID` method-specific ID. + +E.g. +- `"did:example:12345678" -> "12345678"` +- `"did:iota:main:12345678" -> "main:12345678"` + +**Kind**: instance method of [IotaDID](#IotaDID) + + +### iotaDID.join(segment) ⇒ [DIDUrl](#DIDUrl) +Construct a new `DIDUrl` by joining with a relative DID Url string. + +**Kind**: instance method of [IotaDID](#IotaDID) + +| Param | Type | +| --- | --- | +| segment | string | + + + +### iotaDID.toUrl() ⇒ [DIDUrl](#DIDUrl) +Clones the `IotaDID` into a `DIDUrl`. + +**Kind**: instance method of [IotaDID](#IotaDID) + + +### iotaDID.intoUrl() ⇒ [DIDUrl](#DIDUrl) +Converts the `IotaDID` into a `DIDUrl`, consuming it. + +**Kind**: instance method of [IotaDID](#IotaDID) + + +### iotaDID.toString() ⇒ string +Returns the `IotaDID` as a string. + +**Kind**: instance method of [IotaDID](#IotaDID) + + +### iotaDID.toJSON() ⇒ any +Serializes this to a JSON object. + +**Kind**: instance method of [IotaDID](#IotaDID) + + +### iotaDID.clone() ⇒ [IotaDID](#IotaDID) +Deep clones the object. + +**Kind**: instance method of [IotaDID](#IotaDID) + + +### IotaDID.METHOD ⇒ string +The IOTA DID method name (`"iota"`). + +**Kind**: static property of [IotaDID](#IotaDID) + + +### IotaDID.DEFAULT\_NETWORK ⇒ string +The default Tangle network (`"main"`). + +**Kind**: static property of [IotaDID](#IotaDID) + + +### IotaDID.parse(input) ⇒ [IotaDID](#IotaDID) +Parses a `IotaDID` from the input string. + +**Kind**: static method of [IotaDID](#IotaDID) + +| Param | Type | +| --- | --- | +| input | string | + + + +### IotaDID.fromJSON(json) ⇒ [IotaDID](#IotaDID) +Deserializes an instance from a JSON object. + +**Kind**: static method of [IotaDID](#IotaDID) + +| Param | Type | +| --- | --- | +| json | any | + ## KeyLocation @@ -3779,7 +3845,7 @@ Deserializes an instance from a JSON object. * [.validate(presentation, holder, issuers, options, fail_fast)](#PresentationValidator.validate) * [.verifyPresentationSignature(presentation, holder, options)](#PresentationValidator.verifyPresentationSignature) * [.checkStructure(presentation)](#PresentationValidator.checkStructure) - * [.extractHolder(presentation)](#PresentationValidator.extractHolder) ⇒ [DID](#DID) + * [.extractHolder(presentation)](#PresentationValidator.extractHolder) ⇒ [IotaDID](#IotaDID) @@ -3854,7 +3920,7 @@ Validates the semantic structure of the `Presentation`. -### PresentationValidator.extractHolder(presentation) ⇒ [DID](#DID) +### PresentationValidator.extractHolder(presentation) ⇒ [IotaDID](#IotaDID) Utility for extracting the holder field of a `Presentation` as a DID. ### Errors @@ -4306,7 +4372,7 @@ Fetches the `Document` of the given `DID`. | Param | Type | | --- | --- | -| did | [DID](#DID) \| string | +| did | [IotaDID](#IotaDID) \| string | @@ -4317,7 +4383,7 @@ Fetches the `DocumentHistory` of the given `DID`. | Param | Type | | --- | --- | -| did | [DID](#DID) \| string | +| did | [IotaDID](#IotaDID) \| string | @@ -5864,7 +5930,7 @@ Deserializes an instance from a JSON object. * [new VerificationMethod(did, keyType, publicKey, fragment)](#new_VerificationMethod_new) * _instance_ * [.id()](#VerificationMethod+id) ⇒ [DIDUrl](#DIDUrl) - * [.controller()](#VerificationMethod+controller) ⇒ [DID](#DID) + * [.controller()](#VerificationMethod+controller) ⇒ [IotaDID](#IotaDID) * [.setController(did)](#VerificationMethod+setController) * [.type()](#VerificationMethod+type) ⇒ [MethodType](#MethodType) * [.data()](#VerificationMethod+data) ⇒ [MethodData](#MethodData) @@ -5881,7 +5947,7 @@ Creates a new `VerificationMethod` from the given `did` and public key. | Param | Type | | --- | --- | -| did | [DID](#DID) | +| did | [IotaDID](#IotaDID) | | keyType | number | | publicKey | Uint8Array | | fragment | string | @@ -5894,20 +5960,20 @@ Returns a copy of the `id` `DIDUrl` of the `VerificationMethod`. **Kind**: instance method of [VerificationMethod](#VerificationMethod) -### verificationMethod.controller() ⇒ [DID](#DID) +### verificationMethod.controller() ⇒ [IotaDID](#IotaDID) Returns a copy of the `controller` `DID` of the `VerificationMethod`. **Kind**: instance method of [VerificationMethod](#VerificationMethod) ### verificationMethod.setController(did) -Sets the `controller` `DID` of the `VerificationMethod`. +Sets the `controller` `DID` of the `VerificationMethod` object. **Kind**: instance method of [VerificationMethod](#VerificationMethod) | Param | Type | | --- | --- | -| did | [DID](#DID) | +| did | [IotaDID](#IotaDID) | @@ -6074,6 +6140,12 @@ This is possible because Ed25519 is birationally equivalent to Curve25519 used b ## StateMetadataEncoding +**Kind**: global variable + + +## DIDType +Supported types representing a DID that can be generated by the storage interface. + **Kind**: global variable diff --git a/bindings/wasm/src/account/storage/traits.rs b/bindings/wasm/src/account/storage/traits.rs index c9f067d753..f63fe1f75e 100644 --- a/bindings/wasm/src/account/storage/traits.rs +++ b/bindings/wasm/src/account/storage/traits.rs @@ -5,6 +5,7 @@ use core::fmt::Debug; use core::fmt::Formatter; use identity_iota::account_storage::CekAlgorithm; +use identity_iota::account_storage::DIDType; use identity_iota::account_storage::EncryptedData; use identity_iota::account_storage::EncryptionAlgorithm; use identity_iota::account_storage::Error as AccountStorageError; @@ -14,7 +15,7 @@ use identity_iota::account_storage::Signature; use identity_iota::account_storage::Storage; use identity_iota::crypto::PrivateKey; use identity_iota::crypto::PublicKey; -use identity_iota::iota_core::IotaDID; +use identity_iota::did::CoreDID; use identity_iota::iota_core::NetworkName; use identity_iota::prelude::KeyType; use js_sys::Array; @@ -25,13 +26,14 @@ use wasm_bindgen::JsCast; use wasm_bindgen_futures::JsFuture; use crate::account::types::WasmCekAlgorithm; +use crate::account::types::WasmDIDType; use crate::account::types::WasmEncryptedData; use crate::account::types::WasmEncryptionAlgorithm; use crate::account::types::WasmKeyLocation; use crate::common::PromiseBool; use crate::common::PromiseVoid; use crate::crypto::WasmKeyType; -use crate::did::WasmDID; +use crate::did::WasmCoreDID; use crate::error::JsValueResult; #[wasm_bindgen] @@ -42,9 +44,9 @@ extern "C" { pub type PromiseSignature; #[wasm_bindgen(typescript_type = "Promise")] pub type PromiseKeyLocation; - #[wasm_bindgen(typescript_type = "Promise>")] + #[wasm_bindgen(typescript_type = "Promise>")] pub type PromiseArrayDID; - #[wasm_bindgen(typescript_type = "Promise<[DID, KeyLocation]>")] + #[wasm_bindgen(typescript_type = "Promise<[CoreDID, KeyLocation]>")] pub type PromiseDIDKeyLocation; #[wasm_bindgen(typescript_type = "Promise")] pub type PromiseEncryptedData; @@ -62,34 +64,45 @@ extern "C" { #[wasm_bindgen(method, js_name = didCreate)] pub fn did_create( this: &WasmStorage, + did_type: WasmDIDType, network: &str, fragment: &str, private_key: Option>, ) -> PromiseDIDKeyLocation; #[wasm_bindgen(method, js_name = didPurge)] - pub fn did_purge(this: &WasmStorage, did: WasmDID) -> PromiseVoid; + pub fn did_purge(this: &WasmStorage, did: WasmCoreDID) -> PromiseVoid; #[wasm_bindgen(method, js_name = didExists)] - pub fn did_exists(this: &WasmStorage, did: WasmDID) -> PromiseBool; + pub fn did_exists(this: &WasmStorage, did: WasmCoreDID) -> PromiseBool; #[wasm_bindgen(method, js_name = didList)] pub fn did_list(this: &WasmStorage) -> PromiseArrayDID; #[wasm_bindgen(method, js_name = keyGenerate)] - pub fn key_generate(this: &WasmStorage, did: WasmDID, key_type: WasmKeyType, fragment: String) -> PromiseKeyLocation; + pub fn key_generate( + this: &WasmStorage, + did: WasmCoreDID, + key_type: WasmKeyType, + fragment: String, + ) -> PromiseKeyLocation; #[wasm_bindgen(method, js_name = keyInsert)] - pub fn key_insert(this: &WasmStorage, did: WasmDID, location: WasmKeyLocation, private_key: Vec) -> PromiseVoid; + pub fn key_insert( + this: &WasmStorage, + did: WasmCoreDID, + location: WasmKeyLocation, + private_key: Vec, + ) -> PromiseVoid; #[wasm_bindgen(method, js_name = keyPublic)] - pub fn key_public(this: &WasmStorage, did: WasmDID, location: WasmKeyLocation) -> PromisePublicKey; + pub fn key_public(this: &WasmStorage, did: WasmCoreDID, location: WasmKeyLocation) -> PromisePublicKey; #[wasm_bindgen(method, js_name = keyDelete)] - pub fn key_delete(this: &WasmStorage, did: WasmDID, location: WasmKeyLocation) -> PromiseVoid; + pub fn key_delete(this: &WasmStorage, did: WasmCoreDID, location: WasmKeyLocation) -> PromiseVoid; #[wasm_bindgen(method, js_name = keySign)] - pub fn key_sign(this: &WasmStorage, did: WasmDID, location: WasmKeyLocation, data: Vec) -> PromiseSignature; + pub fn key_sign(this: &WasmStorage, did: WasmCoreDID, location: WasmKeyLocation, data: Vec) -> PromiseSignature; #[wasm_bindgen(method, js_name = keyExists)] - pub fn key_exists(this: &WasmStorage, did: WasmDID, location: WasmKeyLocation) -> PromiseBool; + pub fn key_exists(this: &WasmStorage, did: WasmCoreDID, location: WasmKeyLocation) -> PromiseBool; #[wasm_bindgen(method, js_name = dataEncrypt)] pub fn data_encrypt( this: &WasmStorage, - did: WasmDID, + did: WasmCoreDID, plaintext: Vec, associated_data: Vec, encryption_algorithm: WasmEncryptionAlgorithm, @@ -99,16 +112,16 @@ extern "C" { #[wasm_bindgen(method, js_name = dataDecrypt)] pub fn data_decrypt( this: &WasmStorage, - did: WasmDID, + did: WasmCoreDID, data: WasmEncryptedData, encryption_algorithm: WasmEncryptionAlgorithm, cek_algorithm: WasmCekAlgorithm, private_key: WasmKeyLocation, ) -> Uint8Array; #[wasm_bindgen(method, js_name = blobGet)] - pub fn blob_get(this: &WasmStorage, did: WasmDID) -> PromiseOptionBytes; + pub fn blob_get(this: &WasmStorage, did: WasmCoreDID) -> PromiseOptionBytes; #[wasm_bindgen(method, js_name = blobSet)] - pub fn blob_set(this: &WasmStorage, did: WasmDID, blob: Vec) -> PromiseVoid; + pub fn blob_set(this: &WasmStorage, did: WasmCoreDID, blob: Vec) -> PromiseVoid; #[wasm_bindgen(method, js_name = flushChanges)] pub fn flush_changes(this: &WasmStorage) -> PromiseVoid; } @@ -123,22 +136,23 @@ impl Debug for WasmStorage { impl Storage for WasmStorage { async fn did_create( &self, + did_type: DIDType, network: NetworkName, fragment: &str, private_key: Option, - ) -> AccountStorageResult<(IotaDID, KeyLocation)> { + ) -> AccountStorageResult<(CoreDID, KeyLocation)> { let private_key: Option> = private_key.map(|key| { let key_bytes: Vec = key.as_ref().to_vec(); core::mem::drop(key); key_bytes }); - let promise: Promise = Promise::resolve(&self.did_create(network.as_ref(), fragment, private_key)); + let promise: Promise = Promise::resolve(&self.did_create(did_type.into(), network.as_ref(), fragment, private_key)); let result: JsValueResult = JsFuture::from(promise).await.into(); let did_location_tuple: js_sys::Array = js_sys::Array::from(&result.to_account_error()?); let mut did_location_tuple: js_sys::ArrayIter = did_location_tuple.iter(); - let did: IotaDID = did_location_tuple + let did: CoreDID = did_location_tuple .next() .ok_or_else(|| AccountStorageError::JsError("expected a tuple of size 2".to_owned()))? .into_serde() @@ -153,19 +167,19 @@ impl Storage for WasmStorage { Ok((did, location)) } - async fn did_purge(&self, did: &IotaDID) -> AccountStorageResult { + async fn did_purge(&self, did: &CoreDID) -> AccountStorageResult { let promise: Promise = Promise::resolve(&self.did_purge(did.clone().into())); let result: JsValueResult = JsFuture::from(promise).await.into(); result.into() } - async fn did_exists(&self, did: &IotaDID) -> AccountStorageResult { + async fn did_exists(&self, did: &CoreDID) -> AccountStorageResult { let promise: Promise = Promise::resolve(&self.did_exists(did.clone().into())); let result: JsValueResult = JsFuture::from(promise).await.into(); result.into() } - async fn did_list(&self) -> AccountStorageResult> { + async fn did_list(&self) -> AccountStorageResult> { let promise: Promise = Promise::resolve(&self.did_list()); let result: JsValueResult = JsFuture::from(promise).await.into(); let js_value: JsValue = result.to_account_error()?; @@ -175,7 +189,7 @@ impl Storage for WasmStorage { .map_err(|err| AccountStorageError::SerializationError(err.to_string())) } - async fn key_generate(&self, did: &IotaDID, key_type: KeyType, fragment: &str) -> AccountStorageResult { + async fn key_generate(&self, did: &CoreDID, key_type: KeyType, fragment: &str) -> AccountStorageResult { let promise: Promise = Promise::resolve(&self.key_generate(did.clone().into(), key_type.into(), fragment.to_owned())); let result: JsValueResult = JsFuture::from(promise).await.into(); @@ -189,7 +203,7 @@ impl Storage for WasmStorage { async fn key_insert( &self, - did: &IotaDID, + did: &CoreDID, location: &KeyLocation, private_key: PrivateKey, ) -> AccountStorageResult<()> { @@ -202,20 +216,20 @@ impl Storage for WasmStorage { result.into() } - async fn key_public(&self, did: &IotaDID, location: &KeyLocation) -> AccountStorageResult { + async fn key_public(&self, did: &CoreDID, location: &KeyLocation) -> AccountStorageResult { let promise: Promise = Promise::resolve(&self.key_public(did.clone().into(), location.clone().into())); let result: JsValueResult = JsFuture::from(promise).await.into(); let public_key: Vec = result.to_account_error().map(uint8array_to_bytes)??; Ok(public_key.into()) } - async fn key_delete(&self, did: &IotaDID, location: &KeyLocation) -> AccountStorageResult { + async fn key_delete(&self, did: &CoreDID, location: &KeyLocation) -> AccountStorageResult { let promise: Promise = Promise::resolve(&self.key_delete(did.clone().into(), location.clone().into())); let result: JsValueResult = JsFuture::from(promise).await.into(); result.into() } - async fn key_sign(&self, did: &IotaDID, location: &KeyLocation, data: Vec) -> AccountStorageResult { + async fn key_sign(&self, did: &CoreDID, location: &KeyLocation, data: Vec) -> AccountStorageResult { let promise: Promise = Promise::resolve(&self.key_sign(did.clone().into(), location.clone().into(), data)); let result: JsValueResult = JsFuture::from(promise).await.into(); let js_value: JsValue = result.to_account_error()?; @@ -225,7 +239,7 @@ impl Storage for WasmStorage { Ok(signature) } - async fn key_exists(&self, did: &IotaDID, location: &KeyLocation) -> AccountStorageResult { + async fn key_exists(&self, did: &CoreDID, location: &KeyLocation) -> AccountStorageResult { let promise: Promise = Promise::resolve(&self.key_exists(did.clone().into(), location.clone().into())); let result: JsValueResult = JsFuture::from(promise).await.into(); result.into() @@ -233,7 +247,7 @@ impl Storage for WasmStorage { async fn data_encrypt( &self, - did: &IotaDID, + did: &CoreDID, plaintext: Vec, associated_data: Vec, encryption_algorithm: &EncryptionAlgorithm, @@ -258,7 +272,7 @@ impl Storage for WasmStorage { async fn data_decrypt( &self, - did: &IotaDID, + did: &CoreDID, data: EncryptedData, encryption_algorithm: &EncryptionAlgorithm, cek_algorithm: &CekAlgorithm, @@ -276,7 +290,7 @@ impl Storage for WasmStorage { Ok(data) } - async fn blob_get(&self, did: &IotaDID) -> AccountStorageResult>> { + async fn blob_get(&self, did: &CoreDID) -> AccountStorageResult>> { let promise: Promise = Promise::resolve(&self.blob_get(did.clone().into())); let result: JsValueResult = JsFuture::from(promise).await.into(); let js_value: JsValue = result.to_account_error()?; @@ -287,7 +301,7 @@ impl Storage for WasmStorage { Ok(Some(value)) } - async fn blob_set(&self, did: &IotaDID, blob: Vec) -> AccountStorageResult<()> { + async fn blob_set(&self, did: &CoreDID, blob: Vec) -> AccountStorageResult<()> { let promise: Promise = Promise::resolve(&self.blob_set(did.clone().into(), blob)); let result: JsValueResult = JsFuture::from(promise).await.into(); result.into() @@ -326,70 +340,70 @@ Other operations on the list are `did_exists` and `did_list`. See the `MemStore` example for a test implementation. */ interface Storage { - /** Creates a new identity for the given `network`. + /** Creates a new identity of the type declared in `did_type` for the given `network`. - Uses the given Ed25519 `private_key` or generates a new key if it's `None`. - Returns an error if the DID already exists. - Adds the newly created DID to a list which can be accessed via `did_list`. - Returns the generated DID and the location at which the key was stored. */ - didCreate: (network: string, fragment: string, privateKey?: Uint8Array) => Promise<[DID, KeyLocation]>; + Returns the generated DID represented as a [`CoreDID`] and the location at which the key was stored. */ + didCreate: (didType: DIDType, network: string, fragment: string, privateKey?: Uint8Array) => Promise<[CoreDID, KeyLocation]>; /** Removes the keys and any other state for the given `did`. This operation is idempotent: it does not fail if the given `did` does not (or no longer) exist. Returns `true` if the did and its associated data was removed, `false` if nothing was done. */ - didPurge: (did: DID) => Promise; + didPurge: (did: CoreDID) => Promise; /** Returns `true` if `did` exists in the list of stored DIDs. */ - didExists: (did: DID) => Promise; + didExists: (did: CoreDID) => Promise; /** Returns the list of stored DIDs. */ - didList: () => Promise>; + didList: () => Promise>; /** Generates a new key for the given `did` with the given `key_type` and `fragment` identifier and returns the location of the newly generated key. */ - keyGenerate: (did: DID, keyType: KeyType, fragment: string) => Promise; + keyGenerate: (did: CoreDID, keyType: KeyType, fragment: string) => Promise; /** Inserts a private key at the specified `location`. If a key at `location` exists, it is overwritten. */ - keyInsert: (did: DID, keyLocation: KeyLocation, privateKey: Uint8Array) => Promise; + keyInsert: (did: CoreDID, keyLocation: KeyLocation, privateKey: Uint8Array) => Promise; /** Retrieves the public key from `location`. */ - keyPublic: (did: DID, keyLocation: KeyLocation) => Promise; + keyPublic: (did: CoreDID, keyLocation: KeyLocation) => Promise; /** Deletes the key at `location`. This operation is idempotent: it does not fail if the key does not exist. Returns `true` if it removed the key, `false` if nothing was done. */ - keyDelete: (did: DID, keyLocation: KeyLocation) => Promise; + keyDelete: (did: CoreDID, keyLocation: KeyLocation) => Promise; /** Signs `data` with the private key at the specified `location`. */ - keySign: (did: DID, keyLocation: KeyLocation, data: Uint8Array) => Promise; + keySign: (did: CoreDID, keyLocation: KeyLocation, data: Uint8Array) => Promise; /** Returns `true` if a key exists at the specified `location`. */ - keyExists: (did: DID, keyLocation: KeyLocation) => Promise; + keyExists: (did: CoreDID, keyLocation: KeyLocation) => Promise; /** Encrypts the given `plaintext` with the specified `encryptionAlgorithm` and `cekAlgorithm`. * * Returns an `EncryptedData` instance. */ - dataEncrypt: (did: DID, plaintext: Uint8Array, associatedData: Uint8Array, encryptionAlgorithm: EncryptionAlgorithm, cekAlgorithm: CekAlgorithm, publicKey: Uint8Array) => Promise; + dataEncrypt: (did: CoreDID, plaintext: Uint8Array, associatedData: Uint8Array, encryptionAlgorithm: EncryptionAlgorithm, cekAlgorithm: CekAlgorithm, publicKey: Uint8Array) => Promise; /** Decrypts the given `data` with the specified `encryptionAlgorithm` and `cekAlgorithm`. * * Returns the decrypted text. */ - dataDecrypt: (did: DID, data: EncryptedData, encryptionAlgorithm: EncryptionAlgorithm, cekAlgorithm: CekAlgorithm, privateKey: KeyLocation) => Promise; + dataDecrypt: (did: CoreDID, data: EncryptedData, encryptionAlgorithm: EncryptionAlgorithm, cekAlgorithm: CekAlgorithm, privateKey: KeyLocation) => Promise; /** Returns the blob stored by the identity specified by `did`. */ - blobGet: (did: DID) => Promise; + blobGet: (did: CoreDID) => Promise; /** Stores an arbitrary blob for the identity specified by `did`. */ - blobSet: (did: DID, blob: Uint8Array) => Promise; + blobSet: (did: CoreDID, blob: Uint8Array) => Promise; /** Persists any unsaved changes. */ flushChanges: () => Promise; diff --git a/bindings/wasm/src/account/types/did_type.rs b/bindings/wasm/src/account/types/did_type.rs new file mode 100644 index 0000000000..9fff9df331 --- /dev/null +++ b/bindings/wasm/src/account/types/did_type.rs @@ -0,0 +1,28 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use identity_iota::account_storage::DIDType; +use wasm_bindgen::prelude::*; + +/// Supported types representing a DID that can be generated by the storage interface. +#[wasm_bindgen(js_name = DIDType)] +#[derive(Clone, Copy, Debug, Deserialize, Serialize)] +pub enum WasmDIDType { + IotaDID, +} + +impl From for DIDType { + fn from(other: WasmDIDType) -> Self { + match other { + WasmDIDType::IotaDID => DIDType::IotaDID, + } + } +} + +impl From for WasmDIDType { + fn from(other: DIDType) -> Self { + match other { + DIDType::IotaDID => WasmDIDType::IotaDID, + } + } +} diff --git a/bindings/wasm/src/account/types/mod.rs b/bindings/wasm/src/account/types/mod.rs index e7ca9a285a..a43c7c50b9 100644 --- a/bindings/wasm/src/account/types/mod.rs +++ b/bindings/wasm/src/account/types/mod.rs @@ -5,6 +5,7 @@ pub use agreement_info::WasmAgreementInfo; pub use auto_save::OptionAutoSave; pub use auto_save::WasmAutoSave; pub use cek_algorithm::WasmCekAlgorithm; +pub use did_type::WasmDIDType; pub use encrypted_data::WasmEncryptedData; pub use encryption_algorithm::WasmEncryptionAlgorithm; pub use identity_setup::WasmIdentitySetup; @@ -15,6 +16,7 @@ pub use signature::WasmSignature; mod agreement_info; mod auto_save; mod cek_algorithm; +mod did_type; mod encrypted_data; mod encryption_algorithm; mod identity_setup; diff --git a/bindings/wasm/src/account/wasm_account/account.rs b/bindings/wasm/src/account/wasm_account/account.rs index 0d9582ce34..20a65fb4bc 100644 --- a/bindings/wasm/src/account/wasm_account/account.rs +++ b/bindings/wasm/src/account/wasm_account/account.rs @@ -36,8 +36,8 @@ use crate::credential::WasmCredential; use crate::credential::WasmPresentation; use crate::crypto::WasmProofOptions; use crate::did::PromiseResolvedDocument; -use crate::did::WasmDID; use crate::did::WasmDocument; +use crate::did::WasmIotaDID; use crate::did::WasmResolvedDocument; use crate::error::Result; use crate::error::WasmResult; @@ -53,10 +53,10 @@ pub struct WasmAccount(pub(crate) Rc>); #[wasm_bindgen(js_class = Account)] impl WasmAccount { - /// Returns the {@link DID} of the managed identity. + /// Returns the {@link IotaDID} of the managed identity. #[wasm_bindgen(js_name = did)] - pub fn did(&self) -> WasmDID { - WasmDID::from(self.0.borrow().did().clone()) + pub fn did(&self) -> WasmIotaDID { + WasmIotaDID::from(self.0.borrow().did().clone()) } /// Returns whether auto-publish is enabled. diff --git a/bindings/wasm/src/account/wasm_account/account_builder.rs b/bindings/wasm/src/account/wasm_account/account_builder.rs index 3132593985..97c6bf0c7f 100644 --- a/bindings/wasm/src/account/wasm_account/account_builder.rs +++ b/bindings/wasm/src/account/wasm_account/account_builder.rs @@ -19,7 +19,7 @@ use crate::account::types::OptionAutoSave; use crate::account::types::WasmIdentitySetup; use crate::account::wasm_account::PromiseAccount; use crate::account::wasm_account::WasmAccount; -use crate::did::WasmDID; +use crate::did::WasmIotaDID; use crate::error::Result; use crate::error::WasmResult; use crate::tangle::IClientConfig; @@ -69,7 +69,7 @@ impl WasmAccountBuilder { /// Loads an existing identity with the specified `did` using the current builder configuration. /// The identity must exist in the configured `Storage`. #[wasm_bindgen(js_name = loadIdentity)] - pub fn load_identity(&mut self, did: &WasmDID) -> Result { + pub fn load_identity(&mut self, did: &WasmIotaDID) -> Result { let builder: Rc> = self.0.clone(); let did: IotaDID = did.0.clone(); let promise: Promise = future_to_promise(async move { diff --git a/bindings/wasm/src/credential/credential_validator.rs b/bindings/wasm/src/credential/credential_validator.rs index 2995e6539a..d9da532841 100644 --- a/bindings/wasm/src/credential/credential_validator.rs +++ b/bindings/wasm/src/credential/credential_validator.rs @@ -15,7 +15,7 @@ use crate::credential::validation_options::WasmFailFast; use crate::credential::validation_options::WasmStatusCheck; use crate::did::ArrayDocumentOrResolvedDocument; use crate::did::DocumentOrResolvedDocument; -use crate::did::WasmDID; +use crate::did::WasmIotaDID; use crate::did::WasmVerifierOptions; use crate::error::Result; use crate::error::WasmResult; @@ -139,8 +139,8 @@ impl WasmCredentialValidator { /// /// Fails if the issuer field is not a valid DID. #[wasm_bindgen(js_name = extractIssuer)] - pub fn extract_issuer(credential: &WasmCredential) -> Result { + pub fn extract_issuer(credential: &WasmCredential) -> Result { let did: IotaDID = CredentialValidator::extract_issuer(&credential.0).wasm_result()?; - Ok(WasmDID::from(did)) + Ok(WasmIotaDID::from(did)) } } diff --git a/bindings/wasm/src/credential/presentation_validator.rs b/bindings/wasm/src/credential/presentation_validator.rs index df4cf58678..946cf96335 100644 --- a/bindings/wasm/src/credential/presentation_validator.rs +++ b/bindings/wasm/src/credential/presentation_validator.rs @@ -11,7 +11,7 @@ use crate::credential::WasmPresentation; use crate::credential::WasmPresentationValidationOptions; use crate::did::ArrayDocumentOrResolvedDocument; use crate::did::DocumentOrResolvedDocument; -use crate::did::WasmDID; +use crate::did::WasmIotaDID; use crate::did::WasmVerifierOptions; use crate::error::Result; use crate::error::WasmResult; @@ -90,8 +90,8 @@ impl WasmPresentationValidator { /// /// Fails if the holder field is missing or not a valid DID. #[wasm_bindgen(js_name = extractHolder)] - pub fn extract_holder(presentation: &WasmPresentation) -> Result { + pub fn extract_holder(presentation: &WasmPresentation) -> Result { let did: IotaDID = PresentationValidator::extract_holder(&presentation.0).wasm_result()?; - Ok(WasmDID::from(did)) + Ok(WasmIotaDID::from(did)) } } diff --git a/bindings/wasm/src/did/mod.rs b/bindings/wasm/src/did/mod.rs index 5d52195ef8..27eafdde20 100644 --- a/bindings/wasm/src/did/mod.rs +++ b/bindings/wasm/src/did/mod.rs @@ -1,12 +1,13 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -pub use self::wasm_did::UWasmDID; -pub use self::wasm_did::WasmDID; +pub use self::wasm_core_did::WasmCoreDID; pub use self::wasm_did_url::WasmDIDUrl; pub use self::wasm_diff_message::WasmDiffMessage; pub use self::wasm_document::WasmDocument; pub use self::wasm_document_metadata::WasmDocumentMetadata; +pub use self::wasm_iota_did::UWasmIotaDID; +pub use self::wasm_iota_did::WasmIotaDID; pub use self::wasm_method_data::WasmMethodData; pub use self::wasm_method_relationship::WasmMethodRelationship; pub use self::wasm_method_scope::OptionMethodScope; @@ -25,11 +26,12 @@ pub use self::wasm_service::WasmService; pub use self::wasm_verification_method::WasmVerificationMethod; pub use self::wasm_verifier_options::WasmVerifierOptions; -mod wasm_did; +mod wasm_core_did; mod wasm_did_url; mod wasm_diff_message; mod wasm_document; mod wasm_document_metadata; +mod wasm_iota_did; mod wasm_method_data; mod wasm_method_relationship; mod wasm_method_scope; diff --git a/bindings/wasm/src/did/wasm_core_did.rs b/bindings/wasm/src/did/wasm_core_did.rs new file mode 100644 index 0000000000..f05069b428 --- /dev/null +++ b/bindings/wasm/src/did/wasm_core_did.rs @@ -0,0 +1,48 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use identity_iota::did::CoreDID; +use wasm_bindgen::prelude::*; + +use crate::did::WasmIotaDID; +use crate::error::Result; +use crate::error::WasmResult; + +/// A Decentralized Identifier (DID). +#[wasm_bindgen(js_name = CoreDID, inspectable)] +pub struct WasmCoreDID(pub(crate) CoreDID); + +#[wasm_bindgen(js_class = CoreDID)] +impl WasmCoreDID { + /// Parses a [`CoreDID`] from the given `input`. + /// + /// # Errors + /// + /// Returns `Err` if the input is not a valid [`CoreDID`]. + pub fn parse(input: &str) -> Result { + CoreDID::parse(input).wasm_result().map(Self) + } + + /// Returns the `CoreDID` as a string. + #[allow(clippy::inherent_to_string)] + #[wasm_bindgen(js_name = toString)] + pub fn to_string(&self) -> String { + self.0.to_string() + } +} + +impl_wasm_json!(WasmCoreDID, CoreDID); +impl_wasm_clone!(WasmCoreDID, CoreDID); + +impl From for WasmCoreDID { + fn from(did: CoreDID) -> Self { + WasmCoreDID(did) + } +} + +impl From for WasmCoreDID { + fn from(wasm_did: WasmIotaDID) -> Self { + let core_did: CoreDID = wasm_did.0.into(); + core_did.into() + } +} diff --git a/bindings/wasm/src/did/wasm_did_url.rs b/bindings/wasm/src/did/wasm_did_url.rs index 8ae1af32ea..f9a03a7daf 100644 --- a/bindings/wasm/src/did/wasm_did_url.rs +++ b/bindings/wasm/src/did/wasm_did_url.rs @@ -4,7 +4,7 @@ use identity_iota::iota_core::IotaDIDUrl; use wasm_bindgen::prelude::*; -use crate::did::WasmDID; +use crate::did::WasmIotaDID; use crate::error::Result; use crate::error::WasmResult; @@ -24,8 +24,8 @@ impl WasmDIDUrl { /// Return a copy of the `DID` section of the `DIDUrl`. #[wasm_bindgen] - pub fn did(&self) -> WasmDID { - WasmDID::from(self.0.did().clone()) + pub fn did(&self) -> WasmIotaDID { + WasmIotaDID::from(self.0.did().clone()) } /// Return a copy of the relative DID Url as a string, including only the path, query, and fragment. diff --git a/bindings/wasm/src/did/wasm_diff_message.rs b/bindings/wasm/src/did/wasm_diff_message.rs index e10fde8383..033922d4a5 100644 --- a/bindings/wasm/src/did/wasm_diff_message.rs +++ b/bindings/wasm/src/did/wasm_diff_message.rs @@ -9,8 +9,8 @@ use identity_iota::iota_core::MessageId; use wasm_bindgen::prelude::*; use crate::crypto::WasmProof; -use crate::did::WasmDID; use crate::did::WasmDocument; +use crate::did::WasmIotaDID; use crate::error::Result; use crate::error::WasmResult; @@ -28,15 +28,15 @@ impl WasmDiffMessage { /// /// @deprecated since 0.5.0, diff chain features are slated for removal. #[wasm_bindgen] - pub fn id(&self) -> WasmDID { - WasmDID::from(self.0.id().clone()) + pub fn id(&self) -> WasmIotaDID { + WasmIotaDID::from(self.0.id().clone()) } /// Returns a copy of the DID of the associated DID Document. /// /// @deprecated since 0.5.0, diff chain features are slated for removal. #[wasm_bindgen] - pub fn did(&self) -> WasmDID { + pub fn did(&self) -> WasmIotaDID { self.id() } diff --git a/bindings/wasm/src/did/wasm_document.rs b/bindings/wasm/src/did/wasm_document.rs index 4cf66955bd..cd1b8ecce6 100644 --- a/bindings/wasm/src/did/wasm_document.rs +++ b/bindings/wasm/src/did/wasm_document.rs @@ -35,10 +35,10 @@ use crate::crypto::WasmKeyPair; use crate::crypto::WasmProof; use crate::crypto::WasmProofOptions; use crate::did::RefMethodScope; -use crate::did::WasmDID; use crate::did::WasmDIDUrl; use crate::did::WasmDiffMessage; use crate::did::WasmDocumentMetadata; +use crate::did::WasmIotaDID; use crate::did::WasmMethodRelationship; use crate::did::WasmMethodScope; use crate::did::WasmMethodType; @@ -101,8 +101,8 @@ impl WasmDocument { /// Returns a copy of the DID Document `id`. #[wasm_bindgen] - pub fn id(&self) -> WasmDID { - WasmDID(self.0.id().clone()) + pub fn id(&self) -> WasmIotaDID { + WasmIotaDID(self.0.id().clone()) } /// Sets the controllers of the DID Document. @@ -132,7 +132,7 @@ impl WasmDocument { Some(controllers) => controllers .iter() .cloned() - .map(WasmDID::from) + .map(WasmIotaDID::from) .map(JsValue::from) .collect::() .unchecked_into::(), diff --git a/bindings/wasm/src/did/wasm_did.rs b/bindings/wasm/src/did/wasm_iota_did.rs similarity index 80% rename from bindings/wasm/src/did/wasm_did.rs rename to bindings/wasm/src/did/wasm_iota_did.rs index 4c8c15c283..15a08986c4 100644 --- a/bindings/wasm/src/did/wasm_did.rs +++ b/bindings/wasm/src/did/wasm_iota_did.rs @@ -11,13 +11,11 @@ use crate::error::WasmResult; use crate::tangle::WasmNetwork; /// A DID conforming to the IOTA DID method specification. -/// -/// @typicalname did -#[wasm_bindgen(js_name = DID, inspectable)] -pub struct WasmDID(pub(crate) IotaDID); +#[wasm_bindgen(js_name = IotaDID, inspectable)] +pub struct WasmIotaDID(pub(crate) IotaDID); -#[wasm_bindgen(js_class = DID)] -impl WasmDID { +#[wasm_bindgen(js_class = IotaDID)] +impl WasmIotaDID { /// The IOTA DID method name (`"iota"`). #[wasm_bindgen(getter = METHOD)] pub fn static_method() -> String { @@ -36,12 +34,12 @@ impl WasmDID { /// Creates a new `DID` from a public key. #[wasm_bindgen(constructor)] - pub fn new(public_key: &[u8], network: Option) -> Result { + pub fn new(public_key: &[u8], network: Option) -> Result { Self::from_public_key(public_key, network) } - /// Creates a new `DID` from an arbitrary public key. - fn from_public_key(public_key: &[u8], network: Option) -> Result { + /// Creates a new `IotaDID` from an arbitrary public key. + fn from_public_key(public_key: &[u8], network: Option) -> Result { let did = if let Some(network) = network { IotaDID::new_with_network(public_key, network) } else { @@ -50,9 +48,9 @@ impl WasmDID { did.wasm_result().map(Self) } - /// Parses a `DID` from the input string. + /// Parses a `IotaDID` from the input string. #[wasm_bindgen] - pub fn parse(input: &str) -> Result { + pub fn parse(input: &str) -> Result { IotaDID::parse(input).wasm_result().map(Self) } @@ -60,19 +58,19 @@ impl WasmDID { // Properties // =========================================================================== - /// Returns the Tangle network of the `DID`. + /// Returns the Tangle network of the `IotaDID`. #[wasm_bindgen] pub fn network(&self) -> Result { self.0.network().map(Into::into).wasm_result() } - /// Returns the Tangle network name of the `DID`. + /// Returns the Tangle network name of the `IotaDID`. #[wasm_bindgen(js_name = networkStr)] pub fn network_str(&self) -> String { self.0.network_str().to_owned() } - /// Returns a copy of the unique tag of the `DID`. + /// Returns a copy of the unique tag of the `IotaDID`. #[wasm_bindgen] pub fn tag(&self) -> String { self.0.tag().to_owned() @@ -128,19 +126,19 @@ impl WasmDID { self.0.clone().join(segment).wasm_result().map(WasmDIDUrl) } - /// Clones the `DID` into a `DIDUrl`. + /// Clones the `IotaDID` into a `DIDUrl`. #[wasm_bindgen(js_name = toUrl)] pub fn to_url(&self) -> WasmDIDUrl { WasmDIDUrl::from(self.0.to_url()) } - /// Converts the `DID` into a `DIDUrl`, consuming it. + /// Converts the `IotaDID` into a `DIDUrl`, consuming it. #[wasm_bindgen(js_name = intoUrl)] pub fn into_url(self) -> WasmDIDUrl { WasmDIDUrl::from(self.0.into_url()) } - /// Returns the `DID` as a string. + /// Returns the `IotaDID` as a string. #[allow(clippy::inherent_to_string)] #[wasm_bindgen(js_name = toString)] pub fn to_string(&self) -> String { @@ -148,10 +146,10 @@ impl WasmDID { } } -impl_wasm_json!(WasmDID, DID); -impl_wasm_clone!(WasmDID, DID); +impl_wasm_json!(WasmIotaDID, IotaDID); +impl_wasm_clone!(WasmIotaDID, IotaDID); -impl From for WasmDID { +impl From for WasmIotaDID { fn from(did: IotaDID) -> Self { Self(did) } @@ -160,14 +158,14 @@ impl From for WasmDID { /// Duck-typed union to pass either a string or WasmDID as a parameter. #[wasm_bindgen] extern "C" { - #[wasm_bindgen(typescript_type = "DID | string")] - pub type UWasmDID; + #[wasm_bindgen(typescript_type = "IotaDID | string")] + pub type UWasmIotaDID; } -impl TryFrom for IotaDID { +impl TryFrom for IotaDID { type Error = JsValue; - fn try_from(did: UWasmDID) -> std::result::Result { + fn try_from(did: UWasmIotaDID) -> std::result::Result { // Parse rather than going through serde directly to return proper error types. let json: String = did.into_serde().wasm_result()?; IotaDID::parse(&json).wasm_result() diff --git a/bindings/wasm/src/did/wasm_verification_method.rs b/bindings/wasm/src/did/wasm_verification_method.rs index b6351a51ec..30b94a276e 100644 --- a/bindings/wasm/src/did/wasm_verification_method.rs +++ b/bindings/wasm/src/did/wasm_verification_method.rs @@ -6,8 +6,8 @@ use identity_iota::iota_core::IotaVerificationMethod; use wasm_bindgen::prelude::*; use crate::crypto::WasmKeyType; -use crate::did::WasmDID; use crate::did::WasmDIDUrl; +use crate::did::WasmIotaDID; use crate::did::WasmMethodData; use crate::did::WasmMethodType; use crate::error::Result; @@ -22,7 +22,7 @@ impl WasmVerificationMethod { #[allow(non_snake_case)] #[wasm_bindgen(constructor)] pub fn new( - did: &WasmDID, + did: &WasmIotaDID, keyType: WasmKeyType, publicKey: Vec, fragment: String, @@ -41,13 +41,13 @@ impl WasmVerificationMethod { /// Returns a copy of the `controller` `DID` of the `VerificationMethod`. #[wasm_bindgen] - pub fn controller(&self) -> WasmDID { - WasmDID::from(self.0.controller().clone()) + pub fn controller(&self) -> WasmIotaDID { + WasmIotaDID::from(self.0.controller().clone()) } - /// Sets the `controller` `DID` of the `VerificationMethod`. + /// Sets the `controller` `DID` of the `VerificationMethod` object. #[wasm_bindgen(js_name = setController)] - pub fn set_controller(&mut self, did: &WasmDID) { + pub fn set_controller(&mut self, did: &WasmIotaDID) { *self.0.controller_mut() = did.0.clone(); } diff --git a/bindings/wasm/src/tangle/client.rs b/bindings/wasm/src/tangle/client.rs index 0560a40b4e..1dac0d17ca 100644 --- a/bindings/wasm/src/tangle/client.rs +++ b/bindings/wasm/src/tangle/client.rs @@ -24,7 +24,7 @@ use crate::chain::PromiseDocumentHistory; use crate::chain::WasmDocumentHistory; use crate::common::PromiseBool; use crate::did::PromiseResolvedDocument; -use crate::did::UWasmDID; +use crate::did::UWasmIotaDID; use crate::did::WasmDiffMessage; use crate::did::WasmDocument; use crate::did::WasmResolvedDocument; @@ -175,7 +175,7 @@ impl WasmClient { /// Fetch the DID document specified by the given `DID`. #[wasm_bindgen] - pub fn resolve(&self, did: UWasmDID) -> Result { + pub fn resolve(&self, did: UWasmIotaDID) -> Result { let did: IotaDID = IotaDID::try_from(did)?; let client: Rc = self.client.clone(); @@ -194,7 +194,7 @@ impl WasmClient { /// Returns the message history of the given DID. #[wasm_bindgen(js_name = resolveHistory)] - pub fn resolve_history(&self, did: UWasmDID) -> Result { + pub fn resolve_history(&self, did: UWasmIotaDID) -> Result { let did: IotaDID = IotaDID::try_from(did)?; let client: Rc = self.client.clone(); diff --git a/bindings/wasm/src/tangle/explorer.rs b/bindings/wasm/src/tangle/explorer.rs index d075e83bea..3885372a7e 100644 --- a/bindings/wasm/src/tangle/explorer.rs +++ b/bindings/wasm/src/tangle/explorer.rs @@ -8,7 +8,7 @@ use identity_iota::iota_core::IotaDID; use identity_iota::iota_core::MessageId; use wasm_bindgen::prelude::*; -use crate::did::UWasmDID; +use crate::did::UWasmIotaDID; use crate::error::Result; use crate::error::WasmResult; @@ -53,7 +53,7 @@ impl WasmExplorerUrl { /// /// E.g. https://explorer.iota.org/mainnet/identity-resolver/{did} #[wasm_bindgen(js_name = resolverUrl)] - pub fn resolver_url(&self, did: UWasmDID) -> Result { + pub fn resolver_url(&self, did: UWasmIotaDID) -> Result { let did: IotaDID = IotaDID::try_from(did)?; self.0.resolver_url(&did).map(|url| url.to_string()).wasm_result() } diff --git a/bindings/wasm/src/tangle/resolver.rs b/bindings/wasm/src/tangle/resolver.rs index dab1f124ab..76a4844fb6 100644 --- a/bindings/wasm/src/tangle/resolver.rs +++ b/bindings/wasm/src/tangle/resolver.rs @@ -35,7 +35,7 @@ use crate::did::ArrayDocumentOrResolvedDocument; use crate::did::DocumentOrResolvedDocument; use crate::did::PromiseArrayResolvedDocument; use crate::did::PromiseResolvedDocument; -use crate::did::UWasmDID; +use crate::did::UWasmIotaDID; use crate::did::WasmResolvedDocument; use crate::error::Result; use crate::error::WasmResult; @@ -79,7 +79,7 @@ impl WasmResolver { /// Fetches the `Document` of the given `DID`. #[wasm_bindgen] - pub fn resolve(&self, did: UWasmDID) -> Result { + pub fn resolve(&self, did: UWasmIotaDID) -> Result { let did: IotaDID = IotaDID::try_from(did)?; let resolver: Rc>> = Rc::clone(&self.0); @@ -98,7 +98,7 @@ impl WasmResolver { /// Fetches the `DocumentHistory` of the given `DID`. #[wasm_bindgen(js_name = resolveHistory)] - pub fn resolve_history(&self, did: UWasmDID) -> Result { + pub fn resolve_history(&self, did: UWasmIotaDID) -> Result { let did: IotaDID = IotaDID::try_from(did)?; let resolver: Rc>> = Rc::clone(&self.0); diff --git a/bindings/wasm/tests/wasm.rs b/bindings/wasm/tests/wasm.rs index e5cc782f86..de0679c0c2 100644 --- a/bindings/wasm/tests/wasm.rs +++ b/bindings/wasm/tests/wasm.rs @@ -13,9 +13,9 @@ use wasm_bindgen_test::*; use identity_wasm::common::WasmTimestamp; use identity_wasm::crypto::WasmKeyPair; use identity_wasm::crypto::WasmKeyType; -use identity_wasm::did::WasmDID; use identity_wasm::did::WasmDIDUrl; use identity_wasm::did::WasmDocument; +use identity_wasm::did::WasmIotaDID; use identity_wasm::did::WasmMethodScope; use identity_wasm::did::WasmVerificationMethod; use identity_wasm::error::WasmError; @@ -49,14 +49,15 @@ fn test_js_error_from_wasm_error() { #[wasm_bindgen_test] fn test_did() { let key = WasmKeyPair::new(WasmKeyType::Ed25519).unwrap(); - let did = WasmDID::new(&key.public(), None).unwrap(); + let did = WasmIotaDID::new(&key.public(), None).unwrap(); assert_eq!(did.network_str(), "main"); - let parsed = WasmDID::parse(&did.to_string()).unwrap(); + let parsed = WasmIotaDID::parse(&did.to_string()).unwrap(); + assert_eq!(did.to_string(), parsed.to_string()); - let base58 = WasmDID::new(&key.public(), Some("dev".to_owned())).unwrap(); + let base58 = WasmIotaDID::new(&key.public(), Some("dev".to_owned())).unwrap(); assert_eq!(base58.tag(), did.tag()); assert_eq!(base58.network_str(), "dev"); @@ -66,7 +67,7 @@ fn test_did() { fn test_did_methods() { let tag = "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV"; let did_str = format!("did:iota:dev:{tag}"); - let did = WasmDID::parse(&did_str).unwrap(); + let did = WasmIotaDID::parse(&did_str).unwrap(); assert_eq!(did.to_string(), did_str); assert_eq!(did.tag(), tag); @@ -81,7 +82,7 @@ fn test_did_methods() { fn test_did_url() { // Base DID Url let key = WasmKeyPair::new(WasmKeyType::Ed25519).unwrap(); - let did = WasmDID::new(&key.public(), None).unwrap(); + let did = WasmIotaDID::new(&key.public(), None).unwrap(); let did_url = did.to_url(); assert_eq!(did.to_string(), did_url.to_string()); @@ -286,7 +287,7 @@ fn test_did_serde() { // Check WasmDID deserialization. { - let wasm_did: WasmDID = WasmDID::from(expected.clone()); + let wasm_did: WasmIotaDID = WasmIotaDID::from(expected.clone()); let de: IotaDID = wasm_did.to_json().unwrap().into_serde().unwrap(); assert_eq!(de, expected); } diff --git a/identity_account/src/account/account.rs b/identity_account/src/account/account.rs index 03f5964df6..787bf3a5f8 100644 --- a/identity_account/src/account/account.rs +++ b/identity_account/src/account/account.rs @@ -129,7 +129,11 @@ where } // Ensure the identity exists in storage - let identity_state_bytes: Vec = setup.storage.blob_get(&did).await?.ok_or(Error::IdentityNotFound)?; + let identity_state_bytes: Vec = setup + .storage + .blob_get(did.as_ref()) + .await? + .ok_or(Error::IdentityNotFound)?; let identity_state: IdentityState = IdentityState::from_json_slice(&identity_state_bytes)?; let chain_state: ChainState = identity_state .chain_state()? @@ -240,7 +244,7 @@ where /// Note: This will remove all associated document updates and key material - recovery is NOT POSSIBLE! pub async fn delete_identity(self) -> Result<()> { // Remove all associated keys and events - self.storage().did_purge(self.did()).await?; + self.storage().did_purge(self.did().as_ref()).await?; // Write the changes to disk self.save(false).await?; @@ -324,7 +328,7 @@ where let identity_state_bytes: Vec = self .storage() .deref() - .blob_get(self.did()) + .blob_get(self.did().as_ref()) .await? .ok_or(Error::IdentityNotFound)?; let identity_state: IdentityState = IdentityState::from_json_slice(&identity_state_bytes)?; @@ -421,7 +425,10 @@ where async fn store_state(&self) -> Result<()> { let identity_state: IdentityState = IdentityState::new(Some(&self.document), Some(&self.chain_state))?; - self.storage.blob_set(self.did(), identity_state.to_json_vec()?).await?; + self + .storage + .blob_set(self.did().as_ref(), identity_state.to_json_vec()?) + .await?; self.save(false).await?; @@ -546,7 +553,7 @@ where let location: KeyLocation = KeyLocation::from_verification_method(method)?; // Create a private key suitable for identity_core::crypto - let private: RemoteKey<'_> = RemoteKey::new(did, &location, self.storage().deref()); + let private: RemoteKey<'_> = RemoteKey::new(did.as_ref(), &location, self.storage().deref()); let method_url: IotaDIDUrl = method.id().to_owned(); @@ -637,7 +644,7 @@ mod account_encryption { self .storage() .data_encrypt( - self.did(), + self.did().as_ref(), plaintext.to_vec(), associated_data.to_vec(), encryption_algorithm, @@ -666,7 +673,13 @@ mod account_encryption { let private_key: KeyLocation = KeyLocation::from_verification_method(method)?; self .storage() - .data_decrypt(self.did(), data, encryption_algorithm, cek_algorithm, &private_key) + .data_decrypt( + self.did().as_ref(), + data, + encryption_algorithm, + cek_algorithm, + &private_key, + ) .await .map_err(Into::into) } diff --git a/identity_account/src/tests/account.rs b/identity_account/src/tests/account.rs index 3300f17ab6..1caab4ddc3 100644 --- a/identity_account/src/tests/account.rs +++ b/identity_account/src/tests/account.rs @@ -12,6 +12,7 @@ use identity_account_storage::storage::Stronghold; use identity_core::common::Timestamp; use identity_core::common::Url; use identity_core::crypto::ProofOptions; +use identity_did::did::CoreDID; use identity_did::utils::Queryable; use identity_did::verification::MethodScope; use identity_iota_client::chain::DocumentChain; @@ -538,7 +539,7 @@ async fn test_account_sync_diff_msg_update() { .await .unwrap(); client - .publish_diff(&*account.chain_state().last_integration_message_id(), &diff_msg) + .publish_diff(account.chain_state().last_integration_message_id(), &diff_msg) .await .unwrap(); let chain: DocumentChain = client.read_document_chain(account.did()).await.unwrap(); @@ -625,27 +626,27 @@ async fn test_storage_index() { .await .unwrap(); - let index: Vec = account1.storage().did_list().await.unwrap(); + let index: Vec = account1.storage().did_list().await.unwrap(); assert_eq!(index.len(), 1); - assert!(index.contains(account1.did())); + assert!(index.contains(account1.did().as_ref())); let account2: Account = Account::create_identity(setup, IdentitySetup::default()).await.unwrap(); - let index: Vec = account2.storage().did_list().await.unwrap(); + let index: Vec = account2.storage().did_list().await.unwrap(); assert_eq!(index.len(), 2); - assert!(index.contains(account1.did())); - assert!(index.contains(account2.did())); + assert!(index.contains(account1.did().as_ref())); + assert!(index.contains(account2.did().as_ref())); - assert!(account2.storage().did_exists(account1.did()).await.unwrap()); - assert!(account2.storage().did_exists(account2.did()).await.unwrap()); + assert!(account2.storage().did_exists(account1.did().as_ref()).await.unwrap()); + assert!(account2.storage().did_exists(account2.did().as_ref()).await.unwrap()); let account1_did: IotaDID = account1.did().to_owned(); account1.delete_identity().await.unwrap(); - assert!(!account2.storage().did_exists(&account1_did).await.unwrap()); - assert!(account2.storage().did_exists(account2.did()).await.unwrap()); + assert!(!account2.storage().did_exists(account1_did.as_ref()).await.unwrap()); + assert!(account2.storage().did_exists(account2.did().as_ref()).await.unwrap()); assert_eq!(account2.storage().did_list().await.unwrap().len(), 1); } } diff --git a/identity_account/src/tests/updates.rs b/identity_account/src/tests/updates.rs index 0bf57b726d..846d150080 100644 --- a/identity_account/src/tests/updates.rs +++ b/identity_account/src/tests/updates.rs @@ -70,7 +70,11 @@ async fn test_create_identity() -> Result<()> { ); // Ensure the key exists in storage. - assert!(account.storage().key_exists(account.did(), &location).await.unwrap()); + assert!(account + .storage() + .key_exists(account.did().as_ref(), &location) + .await + .unwrap()); // Ensure the state was written to storage. assert!(account.load_document().await.is_ok()); @@ -80,7 +84,7 @@ async fn test_create_identity() -> Result<()> { assert!(document.metadata.updated.unwrap() > Timestamp::from_unix(Timestamp::now_utc().to_unix() - 15).unwrap()); // Ensure the DID was added to the index. - assert!(account.storage().did_exists(account.did()).await.unwrap()); + assert!(account.storage().did_exists(account.did().as_ref()).await.unwrap()); } Ok(()) @@ -133,7 +137,7 @@ async fn test_create_identity_already_exists() -> Result<()> { .await .unwrap(); - let initial_state: Vec = account_setup.storage.blob_get(account.did()).await?.unwrap(); + let initial_state: Vec = account_setup.storage.blob_get(account.did().as_ref()).await?.unwrap(); let initial_state: IdentityState = IdentityState::from_json_slice(&initial_state).unwrap(); let output = Account::create_identity(account_setup.clone(), identity_create).await; @@ -144,7 +148,7 @@ async fn test_create_identity_already_exists() -> Result<()> { )); // Ensure nothing was overwritten in storage - let account_state: Vec = account_setup.storage.blob_get(account.did()).await?.unwrap(); + let account_state: Vec = account_setup.storage.blob_get(account.did().as_ref()).await?.unwrap(); let account_state: IdentityState = IdentityState::from_json_slice(&account_state).unwrap(); assert_eq!(initial_state.document()?, account_state.document()?); } @@ -211,7 +215,11 @@ async fn test_create_method_content_generate() -> Result<()> { ); // Ensure the key exists in storage. - assert!(account.storage().key_exists(account.did(), &location).await.unwrap()); + assert!(account + .storage() + .key_exists(account.did().as_ref(), &location) + .await + .unwrap()); // Ensure `created` wasn't updated. assert_eq!(initial_document.metadata.created, document.metadata.created); @@ -249,7 +257,11 @@ async fn test_create_method_content_public() -> Result<()> { // Ensure no key exists in storage. let location: KeyLocation = KeyLocation::from_verification_method(method).unwrap(); - assert!(!account.storage().key_exists(account.did(), &location).await.unwrap()); + assert!(!account + .storage() + .key_exists(account.did().as_ref(), &location) + .await + .unwrap()); } Ok(()) } @@ -361,7 +373,11 @@ async fn test_create_method_from_private_key() { let location: KeyLocation = KeyLocation::from_verification_method(method).unwrap(); - let public_key = account.storage().key_public(account.did(), &location).await.unwrap(); + let public_key = account + .storage() + .key_public(account.did().as_ref(), &location) + .await + .unwrap(); assert_eq!(public_key.as_ref(), keypair.public().as_ref()); } @@ -588,7 +604,11 @@ async fn test_delete_method() -> Result<()> { assert_eq!(document.core_document().methods().count(), 1); // Ensure the key still exists in storage. - assert!(account.storage().key_exists(account.did(), &location).await.unwrap()); + assert!(account + .storage() + .key_exists(account.did().as_ref(), &location) + .await + .unwrap()); // Ensure `created` wasn't updated. assert_eq!(initial_document.metadata.created, document.metadata.created); diff --git a/identity_account/src/updates/update.rs b/identity_account/src/updates/update.rs index 0cb2a3bdc1..766922d84b 100644 --- a/identity_account/src/updates/update.rs +++ b/identity_account/src/updates/update.rs @@ -1,6 +1,8 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use identity_account_storage::types::DIDType; +use identity_did::did::CoreDID; use log::debug; use log::trace; @@ -50,12 +52,14 @@ pub(crate) async fn create_identity( .map_err(|err| UpdateError::InvalidMethodContent(err.to_string()))?; }; - let (did, location) = store.did_create(network.clone(), fragment, setup.private_key).await?; + let (did, location) = store + .did_create(DIDType::IotaDID, network.clone(), fragment, setup.private_key) + .await?; let public_key: PublicKey = store.key_public(&did, &location).await?; let method: IotaVerificationMethod = - IotaVerificationMethod::new(did.clone(), KeyType::Ed25519, &public_key, fragment)?; + IotaVerificationMethod::new(did.clone().try_into()?, KeyType::Ed25519, &public_key, fragment)?; let document = IotaDocument::from_verification_method(method)?; @@ -119,16 +123,15 @@ impl Update { // Generate or extract the private key and/or retrieve the public key. let key_type: KeyType = content.key_type(); - let public: PublicKey = match content { MethodContent::GenerateEd25519 | MethodContent::GenerateX25519 => { - let location: KeyLocation = storage.key_generate(did, key_type, fragment.name()).await?; - storage.key_public(did, &location).await? + let location: KeyLocation = storage.key_generate(did.as_ref(), key_type, fragment.name()).await?; + storage.key_public(did.as_ref(), &location).await? } MethodContent::PrivateEd25519(private_key) | MethodContent::PrivateX25519(private_key) => { let location: KeyLocation = - insert_method_secret(storage, did, key_type, fragment.name(), private_key).await?; - storage.key_public(did, &location).await? + insert_method_secret(storage, did.as_ref(), key_type, fragment.name(), private_key).await?; + storage.key_public(did.as_ref(), &location).await? } MethodContent::PublicEd25519(public_key) => public_key, MethodContent::PublicX25519(public_key) => public_key, @@ -248,7 +251,7 @@ impl Update { async fn insert_method_secret( store: &dyn Storage, - did: &IotaDID, + did: &CoreDID, key_type: KeyType, fragment: &str, private_key: PrivateKey, diff --git a/identity_account_storage/src/crypto/remote.rs b/identity_account_storage/src/crypto/remote.rs index 1a57bb100b..24896706b0 100644 --- a/identity_account_storage/src/crypto/remote.rs +++ b/identity_account_storage/src/crypto/remote.rs @@ -3,7 +3,7 @@ use core::marker::PhantomData; -use identity_iota_core::did::IotaDID; +use identity_did::did::CoreDID; use serde::Serialize; use identity_core::convert::ToJson; @@ -61,14 +61,14 @@ impl RemoteEd25519 { /// A reference to a storage instance and identity key location. #[derive(Debug)] pub struct RemoteKey<'a> { - did: &'a IotaDID, + did: &'a CoreDID, location: &'a KeyLocation, store: &'a dyn Storage, } impl<'a> RemoteKey<'a> { /// Creates a new `RemoteKey` instance. - pub fn new(did: &'a IotaDID, location: &'a KeyLocation, store: &'a dyn Storage) -> Self { + pub fn new(did: &'a CoreDID, location: &'a KeyLocation, store: &'a dyn Storage) -> Self { Self { did, location, store } } } diff --git a/identity_account_storage/src/identity/chain_state.rs b/identity_account_storage/src/identity/chain_state.rs index 8f6437b040..8bc4673495 100644 --- a/identity_account_storage/src/identity/chain_state.rs +++ b/identity_account_storage/src/identity/chain_state.rs @@ -7,7 +7,7 @@ use serde::Deserialize; use serde::Serialize; /// Holds the last published message ids of the integration and diff chains. -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct ChainState { #[serde(default = "MessageId::null", skip_serializing_if = "MessageId::is_null")] last_integration_message_id: MessageId, diff --git a/identity_account_storage/src/storage/memstore.rs b/identity_account_storage/src/storage/memstore.rs index 23b19f0b5d..be710e57b2 100644 --- a/identity_account_storage/src/storage/memstore.rs +++ b/identity_account_storage/src/storage/memstore.rs @@ -20,6 +20,7 @@ use identity_core::crypto::PublicKey; use identity_core::crypto::Sign; #[cfg(feature = "encryption")] use identity_core::crypto::X25519; +use identity_did::did::CoreDID; use identity_iota_core::did::IotaDID; use identity_iota_core::tangle::NetworkName; use std::sync::RwLockReadGuard; @@ -31,6 +32,7 @@ use crate::error::Result; use crate::storage::Storage; #[cfg(feature = "encryption")] use crate::types::CekAlgorithm; +use crate::types::DIDType; #[cfg(feature = "encryption")] use crate::types::EncryptedData; #[cfg(feature = "encryption")] @@ -40,7 +42,7 @@ use crate::types::Signature; use crate::utils::Shared; // The map from DIDs to vaults. -type Vaults = HashMap; +type Vaults = HashMap; // The map from key locations to key pairs, that lives within a DID partition. type MemVault = HashMap; @@ -48,7 +50,7 @@ type MemVault = HashMap; pub struct MemStore { // Controls whether to print the storages content when debugging. expand: bool, - blobs: Shared>>, + blobs: Shared>>, vaults: Shared, } @@ -79,10 +81,11 @@ impl MemStore { impl Storage for MemStore { async fn did_create( &self, + did_type: DIDType, network: NetworkName, fragment: &str, private_key: Option, - ) -> Result<(IotaDID, KeyLocation)> { + ) -> Result<(CoreDID, KeyLocation)> { // Extract a `KeyPair` from the passed private key or generate a new one. // For `did_create` we can assume the `KeyType` to be `Ed25519` because // that is the only currently available signature type. @@ -96,8 +99,13 @@ impl Storage for MemStore { let location: KeyLocation = KeyLocation::new(KeyType::Ed25519, fragment.to_owned(), keypair.public().as_ref()); // Next we use the public key to derive the initial DID. - let did: IotaDID = IotaDID::new_with_network(keypair.public().as_ref(), network) - .map_err(|err| crate::Error::DIDCreationError(err.to_string()))?; + let did: CoreDID = { + match did_type { + DIDType::IotaDID => IotaDID::new_with_network(keypair.public().as_ref(), network) + .map_err(|err| crate::Error::DIDCreationError(err.to_string()))? + .into(), + } + }; // Obtain exclusive access to the vaults. let mut vaults: RwLockWriteGuard<'_, _> = self.vaults.write()?; @@ -118,7 +126,7 @@ impl Storage for MemStore { Ok((did, location)) } - async fn did_purge(&self, did: &IotaDID) -> Result { + async fn did_purge(&self, did: &CoreDID) -> Result { // This method is supposed to be idempotent, // so we only need to do work if the DID still exists. // The return value signals whether the DID was actually removed during this operation. @@ -130,17 +138,17 @@ impl Storage for MemStore { } } - async fn did_exists(&self, did: &IotaDID) -> Result { + async fn did_exists(&self, did: &CoreDID) -> Result { // Note that any failure to get access to the storage and do the actual existence check // should result in an error rather than returning `false`. Ok(self.vaults.read()?.contains_key(did)) } - async fn did_list(&self) -> Result> { + async fn did_list(&self) -> Result> { Ok(self.vaults.read()?.keys().cloned().collect()) } - async fn key_generate(&self, did: &IotaDID, key_type: KeyType, fragment: &str) -> Result { + async fn key_generate(&self, did: &CoreDID, key_type: KeyType, fragment: &str) -> Result { // Obtain exclusive access to the vaults. let mut vaults: RwLockWriteGuard<'_, _> = self.vaults.write()?; // Get or insert the MemVault. @@ -158,7 +166,7 @@ impl Storage for MemStore { Ok(location) } - async fn key_insert(&self, did: &IotaDID, location: &KeyLocation, mut private_key: PrivateKey) -> Result<()> { + async fn key_insert(&self, did: &CoreDID, location: &KeyLocation, mut private_key: PrivateKey) -> Result<()> { // Obtain exclusive access to the vaults. let mut vaults: RwLockWriteGuard<'_, _> = self.vaults.write()?; // Get or insert the MemVault. @@ -188,7 +196,7 @@ impl Storage for MemStore { } } - async fn key_exists(&self, did: &IotaDID, location: &KeyLocation) -> Result { + async fn key_exists(&self, did: &CoreDID, location: &KeyLocation) -> Result { // Obtain read access to the vaults. let vaults: RwLockReadGuard<'_, _> = self.vaults.read()?; @@ -200,7 +208,7 @@ impl Storage for MemStore { Ok(false) } - async fn key_public(&self, did: &IotaDID, location: &KeyLocation) -> Result { + async fn key_public(&self, did: &CoreDID, location: &KeyLocation) -> Result { // Obtain read access to the vaults. let vaults: RwLockReadGuard<'_, _> = self.vaults.read()?; // Lookup the vault for the given DID. @@ -212,7 +220,7 @@ impl Storage for MemStore { Ok(keypair.public().clone()) } - async fn key_delete(&self, did: &IotaDID, location: &KeyLocation) -> Result { + async fn key_delete(&self, did: &CoreDID, location: &KeyLocation) -> Result { // Obtain read access to the vaults. let mut vaults: RwLockWriteGuard<'_, _> = self.vaults.write()?; // Lookup the vault for the given DID. @@ -223,7 +231,7 @@ impl Storage for MemStore { Ok(vault.remove(location).is_some()) } - async fn key_sign(&self, did: &IotaDID, location: &KeyLocation, data: Vec) -> Result { + async fn key_sign(&self, did: &CoreDID, location: &KeyLocation, data: Vec) -> Result { // Obtain read access to the vaults. let vaults: RwLockReadGuard<'_, _> = self.vaults.read()?; // Lookup the vault for the given DID. @@ -251,7 +259,7 @@ impl Storage for MemStore { #[cfg(feature = "encryption")] async fn data_encrypt( &self, - _did: &IotaDID, + _did: &CoreDID, plaintext: Vec, associated_data: Vec, encryption_algorithm: &EncryptionAlgorithm, @@ -312,7 +320,7 @@ impl Storage for MemStore { #[cfg(feature = "encryption")] async fn data_decrypt( &self, - did: &IotaDID, + did: &CoreDID, data: EncryptedData, encryption_algorithm: &EncryptionAlgorithm, cek_algorithm: &CekAlgorithm, @@ -370,14 +378,14 @@ impl Storage for MemStore { } } - async fn blob_set(&self, did: &IotaDID, value: Vec) -> Result<()> { + async fn blob_set(&self, did: &CoreDID, value: Vec) -> Result<()> { // Set the arbitrary value for the given DID. self.blobs.write()?.insert(did.clone(), value); Ok(()) } - async fn blob_get(&self, did: &IotaDID) -> Result>> { + async fn blob_get(&self, did: &CoreDID) -> Result>> { // Lookup the value stored of the given DID. self.blobs.read().map(|data| data.get(did).cloned()) } diff --git a/identity_account_storage/src/storage/stronghold.rs b/identity_account_storage/src/storage/stronghold.rs index 25fac69da2..a0e8102e58 100644 --- a/identity_account_storage/src/storage/stronghold.rs +++ b/identity_account_storage/src/storage/stronghold.rs @@ -13,6 +13,7 @@ use identity_core::crypto::KeyType; use identity_core::crypto::PrivateKey; use identity_core::crypto::PublicKey; use identity_core::crypto::X25519; +use identity_did::did::CoreDID; use identity_iota_core::did::IotaDID; use identity_iota_core::tangle::NetworkName; use iota_stronghold::procedures; @@ -40,6 +41,7 @@ use crate::stronghold::StrongholdError; use crate::stronghold::VaultOperation; use crate::types::AgreementInfo; use crate::types::CekAlgorithm; +use crate::types::DIDType; use crate::types::EncryptedData; use crate::types::EncryptionAlgorithm; use crate::types::KeyLocation; @@ -59,10 +61,11 @@ static VAULT_PATH: &[u8; 6] = b"$vault"; impl Storage for Stronghold { async fn did_create( &self, + did_type: DIDType, network: NetworkName, fragment: &str, private_key: Option, - ) -> Result<(IotaDID, KeyLocation)> { + ) -> Result<(CoreDID, KeyLocation)> { // ============================= // KEY GENERATION/INSERTION // ============================= @@ -81,8 +84,13 @@ impl Storage for Stronghold { let public_key: PublicKey = retrieve_public_key(&tmp_client, &tmp_location)?; - let did: IotaDID = IotaDID::new_with_network(public_key.as_ref(), network) - .map_err(|err| crate::Error::DIDCreationError(err.to_string()))?; + let did: CoreDID = { + match did_type { + DIDType::IotaDID => IotaDID::new_with_network(public_key.as_ref(), network) + .map_err(|err| crate::Error::DIDCreationError(err.to_string()))? + .into(), + } + }; // ============================= // ADD DID TO INDEX @@ -94,7 +102,7 @@ impl Storage for Stronghold { let index_client: Client = self.client(&index_client_path)?; let index_store: Store = index_client.store(); - let mut index: BTreeSet = get_index(&index_store)?; + let mut index: BTreeSet = get_index(&index_store)?; if index.contains(&did) { return Err(crate::Error::IdentityAlreadyExists); @@ -136,14 +144,14 @@ impl Storage for Stronghold { Ok((did, location)) } - async fn did_purge(&self, did: &IotaDID) -> Result { + async fn did_purge(&self, did: &CoreDID) -> Result { let index_lock: RwLockReadGuard<'_, _> = self.index_lock.read().await; let index_client_path: ClientPath = ClientPath::from(INDEX_CLIENT_PATH); let index_client: Client = self.client(&index_client_path)?; let index_store: Store = index_client.store(); - let mut index: BTreeSet = get_index(&index_store)?; + let mut index: BTreeSet = get_index(&index_store)?; // Remove index entry if present. if !index.remove(did) { @@ -171,13 +179,13 @@ impl Storage for Stronghold { Ok(true) } - async fn did_exists(&self, did: &IotaDID) -> Result { + async fn did_exists(&self, did: &CoreDID) -> Result { let index_lock: RwLockReadGuard<'_, _> = self.index_lock.read().await; let client: Client = self.client(&ClientPath::from(INDEX_CLIENT_PATH))?; let store: Store = client.store(); - let dids: BTreeSet = get_index(&store)?; + let dids: BTreeSet = get_index(&store)?; let has_did: bool = dids.contains(did); @@ -187,13 +195,13 @@ impl Storage for Stronghold { Ok(has_did) } - async fn did_list(&self) -> Result> { + async fn did_list(&self) -> Result> { let index_lock: RwLockReadGuard<'_, _> = self.index_lock.read().await; let client: Client = self.client(&ClientPath::from(INDEX_CLIENT_PATH))?; let store: Store = client.store(); - let dids: BTreeSet = get_index(&store)?; + let dids: BTreeSet = get_index(&store)?; // Explicitly drop the lock so it's not considered unused. std::mem::drop(index_lock); @@ -201,7 +209,7 @@ impl Storage for Stronghold { Ok(dids.into_iter().collect()) } - async fn key_generate(&self, did: &IotaDID, key_type: KeyType, fragment: &str) -> Result { + async fn key_generate(&self, did: &CoreDID, key_type: KeyType, fragment: &str) -> Result { self.mutate_client(did, |client| { let tmp_location: KeyLocation = random_location(key_type); @@ -220,16 +228,16 @@ impl Storage for Stronghold { }) } - async fn key_insert(&self, did: &IotaDID, location: &KeyLocation, private_key: PrivateKey) -> Result<()> { + async fn key_insert(&self, did: &CoreDID, location: &KeyLocation, private_key: PrivateKey) -> Result<()> { self.mutate_client(did, |client| insert_private_key(&client, private_key, location)) } - async fn key_public(&self, did: &IotaDID, location: &KeyLocation) -> Result { + async fn key_public(&self, did: &CoreDID, location: &KeyLocation) -> Result { let client: Client = self.client(&ClientPath::from(did))?; retrieve_public_key(&client, location) } - async fn key_delete(&self, did: &IotaDID, location: &KeyLocation) -> Result { + async fn key_delete(&self, did: &CoreDID, location: &KeyLocation) -> Result { self.mutate_client(did, |client| { // Technically there is a race condition here between existence check and removal. // However, the RevokeData procedure does not return an error if the record doesn't exist, so it's fine. @@ -255,7 +263,7 @@ impl Storage for Stronghold { }) } - async fn key_sign(&self, did: &IotaDID, location: &KeyLocation, data: Vec) -> Result { + async fn key_sign(&self, did: &CoreDID, location: &KeyLocation, data: Vec) -> Result { let client: Client = self.client(&ClientPath::from(did))?; match location.key_type { @@ -264,7 +272,7 @@ impl Storage for Stronghold { } } - async fn key_exists(&self, did: &IotaDID, location: &KeyLocation) -> Result { + async fn key_exists(&self, did: &CoreDID, location: &KeyLocation) -> Result { let client: Client = self.client(&ClientPath::from(did))?; client @@ -276,7 +284,7 @@ impl Storage for Stronghold { #[cfg(feature = "encryption")] async fn data_encrypt( &self, - did: &IotaDID, + did: &CoreDID, plaintext: Vec, associated_data: Vec, encryption_algorithm: &EncryptionAlgorithm, @@ -330,7 +338,7 @@ impl Storage for Stronghold { #[cfg(feature = "encryption")] async fn data_decrypt( &self, - did: &IotaDID, + did: &CoreDID, data: EncryptedData, encryption_algorithm: &EncryptionAlgorithm, cek_algorithm: &CekAlgorithm, @@ -374,7 +382,7 @@ impl Storage for Stronghold { } } - async fn blob_set(&self, did: &IotaDID, blob: Vec) -> Result<()> { + async fn blob_set(&self, did: &CoreDID, blob: Vec) -> Result<()> { self.mutate_client(did, |client| { let store: Store = client.store(); @@ -385,7 +393,7 @@ impl Storage for Stronghold { }) } - async fn blob_get(&self, did: &IotaDID) -> Result>> { + async fn blob_get(&self, did: &CoreDID) -> Result>> { let client: Client = self.client(&ClientPath::from(did))?; let store: Store = client.store(); let data: Option> = store @@ -667,20 +675,20 @@ fn move_key(client: &Client, source: &KeyLocation, target: &KeyLocation) -> Resu Ok(()) } -fn get_index(store: &Store) -> Result> { +fn get_index(store: &Store) -> Result> { let data: Option> = store .get(INDEX_STORE_KEY.as_bytes()) .map_err(|err| StrongholdError::Store(StoreOperation::Get, err))?; - let index: BTreeSet = match data { - Some(index_vec) => BTreeSet::::from_json_slice(&index_vec)?, + let index: BTreeSet = match data { + Some(index_vec) => BTreeSet::::from_json_slice(&index_vec)?, None => BTreeSet::new(), }; Ok(index) } -fn set_index(store: &Store, index: BTreeSet) -> Result<()> { +fn set_index(store: &Store, index: BTreeSet) -> Result<()> { let index_vec: Vec = index.to_json_vec()?; store diff --git a/identity_account_storage/src/storage/test_suite.rs b/identity_account_storage/src/storage/test_suite.rs index 9809459800..b4987ddc42 100644 --- a/identity_account_storage/src/storage/test_suite.rs +++ b/identity_account_storage/src/storage/test_suite.rs @@ -3,6 +3,7 @@ use anyhow::Context; use function_name::named; +use identity_did::did::CoreDID; use rand::distributions::DistString; use rand::rngs::OsRng; @@ -22,6 +23,7 @@ use identity_iota_core::tangle::NetworkName; use crate::identity::ChainState; use crate::types::AgreementInfo; use crate::types::CekAlgorithm; +use crate::types::DIDType; use crate::types::EncryptedData; use crate::types::EncryptionAlgorithm; use crate::types::KeyLocation; @@ -66,12 +68,19 @@ impl StorageTestSuite { let keypair: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); let network: NetworkName = Network::Mainnet.name(); - let expected_did: IotaDID = IotaDID::new_with_network(keypair.public().as_ref(), network.clone()).unwrap(); + let expected_did: CoreDID = IotaDID::new_with_network(keypair.public().as_ref(), network.clone()) + .unwrap() + .into(); let expected_location: KeyLocation = KeyLocation::new(KeyType::Ed25519, fragment.clone(), keypair.public().as_ref()); - let (did, location): (IotaDID, KeyLocation) = storage - .did_create(network.clone(), &fragment, Some(keypair.private().to_owned())) + let (did, location): (CoreDID, KeyLocation) = storage + .did_create( + DIDType::IotaDID, + network.clone(), + &fragment, + Some(keypair.private().to_owned()), + ) .await .context("did_create returned an error")?; @@ -95,7 +104,7 @@ impl StorageTestSuite { ensure!(exists, "expected key at location `{location}` to exist"); let result: Result<_, crate::Error> = storage - .did_create(network, &fragment, Some(keypair.private().to_owned())) + .did_create(DIDType::IotaDID, network, &fragment, Some(keypair.private().to_owned())) .await; ensure!( @@ -122,11 +131,11 @@ impl StorageTestSuite { pub async fn did_create_generate_key_test(storage: impl Storage) -> anyhow::Result<()> { let fragment: String = random_string(); let network: NetworkName = Network::Devnet.name(); - let (did, location): (IotaDID, KeyLocation) = storage - .did_create(network.clone(), &fragment, None) + let (core_did, location): (CoreDID, KeyLocation) = storage + .did_create(DIDType::IotaDID, network.clone(), &fragment, None) .await .context("did_create returned an error")?; - + let did: IotaDID = IotaDID::try_from(core_did.clone()).unwrap(); ensure_eq!( did.network_str(), network.as_ref(), @@ -135,14 +144,14 @@ impl StorageTestSuite { ); let exists: bool = storage - .key_exists(&did, &location) + .key_exists(&core_did, &location) .await .context("key_exists returned an error")?; ensure!(exists, "expected key at location `{location}` to exist"); let public_key: PublicKey = storage - .key_public(&did, &location) + .key_public(&core_did, &location) .await .context("key_public returned an error")?; @@ -162,8 +171,8 @@ impl StorageTestSuite { let fragment: String = random_string(); let network: NetworkName = Network::Mainnet.name(); - let (did, _): (IotaDID, _) = storage - .did_create(network.clone(), &fragment, None) + let (did, _): (CoreDID, _) = storage + .did_create(DIDType::IotaDID, network.clone(), &fragment, None) .await .context("did_create returned an error")?; @@ -204,8 +213,8 @@ impl StorageTestSuite { let fragment: String = random_string(); let network: NetworkName = Network::Mainnet.name(); - let (did, _): (IotaDID, _) = storage - .did_create(network.clone(), &fragment, None) + let (did, _): (CoreDID, _) = storage + .did_create(DIDType::IotaDID, network.clone(), &fragment, None) .await .context("did_create returned an error")?; @@ -252,7 +261,7 @@ impl StorageTestSuite { let fragment: String = random_string(); let network: NetworkName = Network::Mainnet.name(); - let list: Vec = storage.did_list().await.context("did_list returned an error")?; + let list: Vec = storage.did_list().await.context("did_list returned an error")?; ensure!( list.is_empty(), @@ -261,8 +270,8 @@ impl StorageTestSuite { ); for i in 0..NUM_IDENTITIES { - let (did, _): (IotaDID, _) = storage - .did_create(network.clone(), &fragment, None) + let (did, _): (CoreDID, _) = storage + .did_create(DIDType::IotaDID, network.clone(), &fragment, None) .await .context("did_create returned an error")?; @@ -287,8 +296,8 @@ impl StorageTestSuite { let fragment: String = random_string(); let network: NetworkName = Network::Mainnet.name(); - let (did, _): (IotaDID, _) = storage - .did_create(network.clone(), &fragment, None) + let (did, _): (CoreDID, _) = storage + .did_create(DIDType::IotaDID, network.clone(), &fragment, None) .await .context("did_create returned an error")?; @@ -354,8 +363,13 @@ impl StorageTestSuite { let fragment: String = random_string(); let network: NetworkName = Network::Mainnet.name(); - let (did, location): (IotaDID, KeyLocation) = storage - .did_create(network.clone(), &fragment, Some(PrivateKey::from(PRIVATE_KEY.to_vec()))) + let (did, location): (CoreDID, KeyLocation) = storage + .did_create( + DIDType::IotaDID, + network.clone(), + &fragment, + Some(PrivateKey::from(PRIVATE_KEY.to_vec())), + ) .await .context("did_create returned an error")?; @@ -379,8 +393,8 @@ impl StorageTestSuite { let fragment: String = random_string(); let network: NetworkName = Network::Mainnet.name(); - let (did, location): (IotaDID, KeyLocation) = storage - .did_create(network.clone(), &fragment, None) + let (did, location): (CoreDID, KeyLocation) = storage + .did_create(DIDType::IotaDID, network.clone(), &fragment, None) .await .context("did_create returned an error")?; @@ -393,8 +407,13 @@ impl StorageTestSuite { .await .context("key_public returned an error")?; - let method: IotaVerificationMethod = - IotaVerificationMethod::new(did.clone(), KeyType::Ed25519, &public_key, &fragment).unwrap(); + let method: IotaVerificationMethod = IotaVerificationMethod::new( + did.clone().try_into().unwrap(), + KeyType::Ed25519, + &public_key, + &fragment, + ) + .unwrap(); let expected_document: IotaDocument = IotaDocument::from_verification_method(method).unwrap(); storage @@ -431,8 +450,8 @@ impl StorageTestSuite { let fragment: String = random_string(); let network: NetworkName = Network::Mainnet.name(); - let (did, location): (IotaDID, KeyLocation) = storage - .did_create(network.clone(), &fragment, None) + let (did, location): (CoreDID, KeyLocation) = storage + .did_create(DIDType::IotaDID, network.clone(), &fragment, None) .await .context("did_create returned an error")?; @@ -470,7 +489,7 @@ impl StorageTestSuite { "expected key at location `{location}` to no longer exist after purge" ); - let list: Vec = storage.did_list().await.context("did_list returned an error")?; + let list: Vec = storage.did_list().await.context("did_list returned an error")?; ensure!( list.is_empty(), @@ -491,13 +510,13 @@ impl StorageTestSuite { let network: NetworkName = Network::Mainnet.name(); // Both Alice (Sender) and Bob (Receiver) must have a DID. - let (alice_did, _): (IotaDID, KeyLocation) = alice_storage - .did_create(network.clone(), &random_string(), None) + let (alice_did, _): (CoreDID, KeyLocation) = alice_storage + .did_create(DIDType::IotaDID, network.clone(), &random_string(), None) .await .context("did_create returned an error")?; - let (bob_did, _): (IotaDID, KeyLocation) = bob_storage - .did_create(network.clone(), &random_string(), None) + let (bob_did, _): (CoreDID, KeyLocation) = bob_storage + .did_create(DIDType::IotaDID, network.clone(), &random_string(), None) .await .context("did_create returned an error")?; diff --git a/identity_account_storage/src/storage/traits.rs b/identity_account_storage/src/storage/traits.rs index 99c004e617..dba5a12c96 100644 --- a/identity_account_storage/src/storage/traits.rs +++ b/identity_account_storage/src/storage/traits.rs @@ -8,12 +8,13 @@ use async_trait::async_trait; use identity_core::crypto::KeyType; use identity_core::crypto::PrivateKey; use identity_core::crypto::PublicKey; -use identity_iota_core::did::IotaDID; +use identity_did::did::CoreDID; use identity_iota_core::tangle::NetworkName; use crate::error::Result; #[cfg(feature = "encryption")] use crate::types::CekAlgorithm; +use crate::types::DIDType; #[cfg(feature = "encryption")] use crate::types::EncryptedData; #[cfg(feature = "encryption")] @@ -70,57 +71,58 @@ mod storage_sub_trait { #[cfg_attr(not(feature = "send-sync-storage"), async_trait(?Send))] #[cfg_attr(feature = "send-sync-storage", async_trait)] pub trait Storage: storage_sub_trait::StorageSendSyncMaybe + Debug { - /// Creates a new identity for the given `network`. + /// Creates a new identity of the type declared in `did_type` for the given `network`. /// /// - Uses the given Ed25519 `private_key` or generates a new key if it's `None`. /// - Returns an error if the DID already exists. /// - Adds the newly created DID to a list which can be accessed via [`Storage::did_list`]. /// - /// Returns the generated DID and the location at which the key was stored. + /// Returns the generated DID represented as a [`CoreDID`] and the location at which the key was stored. async fn did_create( &self, + did_type: DIDType, network: NetworkName, fragment: &str, private_key: Option, - ) -> Result<(IotaDID, KeyLocation)>; + ) -> Result<(CoreDID, KeyLocation)>; /// Removes the keys and any other state for the given `did`. /// /// This operation is idempotent: it does not fail if the given `did` does not (or no longer) exist. /// /// Returns `true` if the did and its associated data was removed, `false` if nothing was done. - async fn did_purge(&self, did: &IotaDID) -> Result; + async fn did_purge(&self, did: &CoreDID) -> Result; /// Returns `true` if `did` exists in the list of stored DIDs. - async fn did_exists(&self, did: &IotaDID) -> Result; + async fn did_exists(&self, did: &CoreDID) -> Result; /// Returns the list of stored DIDs. - async fn did_list(&self) -> Result>; + async fn did_list(&self) -> Result>; /// Generates a new key for the given `did` with the given `key_type` and `fragment` identifier /// and returns the location of the newly generated key. - async fn key_generate(&self, did: &IotaDID, key_type: KeyType, fragment: &str) -> Result; + async fn key_generate(&self, did: &CoreDID, key_type: KeyType, fragment: &str) -> Result; /// Inserts a private key at the specified `location`. /// /// If a key at `location` exists, it is overwritten. - async fn key_insert(&self, did: &IotaDID, location: &KeyLocation, private_key: PrivateKey) -> Result<()>; + async fn key_insert(&self, did: &CoreDID, location: &KeyLocation, private_key: PrivateKey) -> Result<()>; /// Retrieves the public key from `location`. - async fn key_public(&self, did: &IotaDID, location: &KeyLocation) -> Result; + async fn key_public(&self, did: &CoreDID, location: &KeyLocation) -> Result; /// Deletes the key at `location`. /// /// This operation is idempotent: it does not fail if the key does not exist. /// /// Returns `true` if it removed the key, `false` if nothing was done. - async fn key_delete(&self, did: &IotaDID, location: &KeyLocation) -> Result; + async fn key_delete(&self, did: &CoreDID, location: &KeyLocation) -> Result; /// Signs `data` with the private key at the specified `location`. - async fn key_sign(&self, did: &IotaDID, location: &KeyLocation, data: Vec) -> Result; + async fn key_sign(&self, did: &CoreDID, location: &KeyLocation, data: Vec) -> Result; /// Returns `true` if a key exists at the specified `location`. - async fn key_exists(&self, did: &IotaDID, location: &KeyLocation) -> Result; + async fn key_exists(&self, did: &CoreDID, location: &KeyLocation) -> Result; /// Encrypts the given `plaintext` with the specified `encryption_algorithm` and `cek_algorithm`. /// @@ -128,7 +130,7 @@ pub trait Storage: storage_sub_trait::StorageSendSyncMaybe + Debug { #[cfg(feature = "encryption")] async fn data_encrypt( &self, - did: &IotaDID, + did: &CoreDID, plaintext: Vec, associated_data: Vec, encryption_algorithm: &EncryptionAlgorithm, @@ -142,7 +144,7 @@ pub trait Storage: storage_sub_trait::StorageSendSyncMaybe + Debug { #[cfg(feature = "encryption")] async fn data_decrypt( &self, - did: &IotaDID, + did: &CoreDID, data: EncryptedData, encryption_algorithm: &EncryptionAlgorithm, cek_algorithm: &CekAlgorithm, @@ -150,10 +152,10 @@ pub trait Storage: storage_sub_trait::StorageSendSyncMaybe + Debug { ) -> Result>; /// Stores an arbitrary blob for the identity specified by `did`. - async fn blob_set(&self, did: &IotaDID, blob: Vec) -> Result<()>; + async fn blob_set(&self, did: &CoreDID, blob: Vec) -> Result<()>; /// Returns the blob stored by the identity specified by `did`. - async fn blob_get(&self, did: &IotaDID) -> Result>>; + async fn blob_get(&self, did: &CoreDID) -> Result>>; /// Persists any unsaved changes. async fn flush_changes(&self) -> Result<()>; diff --git a/identity_account_storage/src/stronghold/client_path.rs b/identity_account_storage/src/stronghold/client_path.rs index 5dad310b59..3270b441a9 100644 --- a/identity_account_storage/src/stronghold/client_path.rs +++ b/identity_account_storage/src/stronghold/client_path.rs @@ -1,6 +1,7 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use identity_did::did::CoreDID; use identity_iota_core::did::IotaDID; /// A helper type to ensure a consistently generated client path, for DIDs and strings @@ -14,6 +15,12 @@ impl AsRef<[u8]> for ClientPath { } } +impl From<&CoreDID> for ClientPath { + fn from(did: &CoreDID) -> Self { + Self(did.to_string()) + } +} + impl From<&IotaDID> for ClientPath { fn from(did: &IotaDID) -> Self { Self(did.to_string()) diff --git a/identity_account_storage/src/stronghold/tests.rs b/identity_account_storage/src/stronghold/tests.rs index e4b47acdb5..cd2337e17e 100644 --- a/identity_account_storage/src/stronghold/tests.rs +++ b/identity_account_storage/src/stronghold/tests.rs @@ -49,7 +49,7 @@ async fn test_mutate_client_persists_client_into_snapshot() { let keypair: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); stronghold - .mutate_client(&did, |client| { + .mutate_client(did.as_ref(), |client| { let vault: ClientVault = client.vault(b"vault"); vault @@ -83,7 +83,7 @@ async fn test_incorrect_password_returns_error() { let location: &KeyLocation = &random_key_location(); stronghold - .mutate_client(&did, |client| { + .mutate_client(did.as_ref(), |client| { client .execute_procedure(GenerateKey { ty: procedures::KeyType::Ed25519, diff --git a/identity_account_storage/src/stronghold/wrapper.rs b/identity_account_storage/src/stronghold/wrapper.rs index 5e9858cd4a..54954de01e 100644 --- a/identity_account_storage/src/stronghold/wrapper.rs +++ b/identity_account_storage/src/stronghold/wrapper.rs @@ -3,7 +3,7 @@ use std::path::Path; -use identity_iota_core::did::IotaDID; +use identity_did::did::CoreDID; use iota_stronghold::Client; use iota_stronghold::ClientError; use iota_stronghold::KeyProvider; @@ -103,7 +103,7 @@ impl Stronghold { /// Load the client for the given `did` and apply function `f` to it. /// The (potentially) modified client is then written to the stronghold's snapshot state. - pub(crate) fn mutate_client(&self, did: &IotaDID, f: FUN) -> Result + pub(crate) fn mutate_client(&self, did: &CoreDID, f: FUN) -> Result where FUN: FnOnce(Client) -> Result, { diff --git a/identity_account_storage/src/types/did_type.rs b/identity_account_storage/src/types/did_type.rs new file mode 100644 index 0000000000..6d1eafebbe --- /dev/null +++ b/identity_account_storage/src/types/did_type.rs @@ -0,0 +1,9 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +/// Supported types representing a DID that can be generated by the [`Storage`](crate::storage::Storage) interface. +#[derive(Clone, Copy, Debug)] +pub enum DIDType { + /// Corresponds to [`IotaDID`](identity_iota_core::did::IotaDID). + IotaDID, +} diff --git a/identity_account_storage/src/types/mod.rs b/identity_account_storage/src/types/mod.rs index 0374a04ffc..f2b3204116 100644 --- a/identity_account_storage/src/types/mod.rs +++ b/identity_account_storage/src/types/mod.rs @@ -1,11 +1,13 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +mod did_type; #[cfg(feature = "encryption")] mod encryption; mod key_location; mod signature; +pub use self::did_type::*; #[cfg(feature = "encryption")] pub use self::encryption::*; pub use self::key_location::*; diff --git a/identity_core/src/common/context.rs b/identity_core/src/common/context.rs index 3c294d065d..96c415c9e2 100644 --- a/identity_core/src/common/context.rs +++ b/identity_core/src/common/context.rs @@ -15,7 +15,7 @@ use crate::common::Url; /// A reference to a JSON-LD context /// /// [More Info](https://www.w3.org/TR/vc-data-model/#contexts) -#[derive(Clone, PartialEq, Deserialize, Serialize)] +#[derive(Clone, PartialEq, Eq, Deserialize, Serialize)] #[serde(untagged)] pub enum Context { /// A JSON-LD context expressed as a Url. diff --git a/identity_core/src/common/one_or_many.rs b/identity_core/src/common/one_or_many.rs index e16ca88476..a70c4f075d 100644 --- a/identity_core/src/common/one_or_many.rs +++ b/identity_core/src/common/one_or_many.rs @@ -93,7 +93,7 @@ impl OneOrMany { /// Returns a reference to the contents as a slice. pub fn as_slice(&self) -> &[T] { - &*self + &**self } /// Consumes the [`OneOrMany`] and returns the contents as a [`Vec`]. @@ -123,14 +123,14 @@ impl Deref for OneOrMany { fn deref(&self) -> &Self::Target { match self { Self::One(inner) => from_ref(inner), - Self::Many(inner) => &*inner, + Self::Many(inner) => &**inner, } } } impl AsRef<[T]> for OneOrMany { fn as_ref(&self) -> &[T] { - &*self + &**self } } diff --git a/identity_core/src/common/one_or_set.rs b/identity_core/src/common/one_or_set.rs index c4ee406c75..af3b012b1c 100644 --- a/identity_core/src/common/one_or_set.rs +++ b/identity_core/src/common/one_or_set.rs @@ -186,7 +186,7 @@ where /// Returns a reference to the contents as a slice. pub fn as_slice(&self) -> &[T] { - &*self + &**self } /// Consumes the [`OneOrSet`] and returns the contents as a [`Vec`]. diff --git a/identity_core/src/crypto/proof/proof_value.rs b/identity_core/src/crypto/proof/proof_value.rs index 0567711602..ce329ca424 100644 --- a/identity_core/src/crypto/proof/proof_value.rs +++ b/identity_core/src/crypto/proof/proof_value.rs @@ -51,9 +51,9 @@ impl ProofValue { pub fn as_str(&self) -> &str { match self { Self::None => "", - Self::Jws(inner) => &*inner, - Self::Proof(inner) => &*inner, - Self::Signature(inner) => &*inner, + Self::Jws(inner) => &**inner, + Self::Proof(inner) => &**inner, + Self::Signature(inner) => &**inner, } } @@ -70,7 +70,7 @@ impl ProofValue { /// Returns the `Jws` type proof data as a string slice. pub fn as_jws(&self) -> Option<&str> { match self { - Self::Jws(inner) => Some(&*inner), + Self::Jws(inner) => Some(&**inner), _ => None, } } @@ -78,7 +78,7 @@ impl ProofValue { /// Returns the `Proof` type proof data as a string slice. pub fn as_proof(&self) -> Option<&str> { match self { - Self::Proof(inner) => Some(&*inner), + Self::Proof(inner) => Some(&**inner), _ => None, } } @@ -86,7 +86,7 @@ impl ProofValue { /// Returns the `Signature` type proof data as a string slice. pub fn as_signature(&self) -> Option<&str> { match self { - Self::Signature(inner) => Some(&*inner), + Self::Signature(inner) => Some(&**inner), _ => None, } } diff --git a/identity_credential/src/credential/credential.rs b/identity_credential/src/credential/credential.rs index b4e0564b57..82e4f99120 100644 --- a/identity_credential/src/credential/credential.rs +++ b/identity_credential/src/credential/credential.rs @@ -35,7 +35,7 @@ lazy_static! { } /// Represents a set of claims describing an entity. -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct Credential { /// The JSON-LD context(s) applicable to the `Credential`. #[serde(rename = "@context")] diff --git a/identity_credential/src/credential/evidence.rs b/identity_credential/src/credential/evidence.rs index 226a24bd18..fd851fdf72 100644 --- a/identity_credential/src/credential/evidence.rs +++ b/identity_credential/src/credential/evidence.rs @@ -7,7 +7,7 @@ use identity_core::common::OneOrMany; /// Information used to increase confidence in the claims of a `Credential` /// /// [More Info](https://www.w3.org/TR/vc-data-model/#evidence) -#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)] pub struct Evidence { /// A Url that allows retrieval of information about the evidence. #[serde(skip_serializing_if = "Option::is_none")] diff --git a/identity_credential/src/credential/issuer.rs b/identity_credential/src/credential/issuer.rs index c3171543be..fac2b30933 100644 --- a/identity_credential/src/credential/issuer.rs +++ b/identity_credential/src/credential/issuer.rs @@ -7,7 +7,7 @@ use identity_core::common::Url; /// A [`Credential`][crate::credential::Credential] issuer in object form. /// /// [More Info](https://www.w3.org/TR/vc-data-model/#issuer) -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct IssuerData { /// A Url identifying the credential issuer. pub id: Url, @@ -19,7 +19,7 @@ pub struct IssuerData { /// An identifier representing the issuer of a [`Credential`][crate::credential::Credential]. /// /// [More Info](https://www.w3.org/TR/vc-data-model/#issuer) -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] #[serde(untagged)] pub enum Issuer { /// A credential issuer expressed as a Url. diff --git a/identity_credential/src/credential/policy.rs b/identity_credential/src/credential/policy.rs index d19e12620f..75a9889276 100644 --- a/identity_credential/src/credential/policy.rs +++ b/identity_credential/src/credential/policy.rs @@ -9,7 +9,7 @@ use identity_core::common::Url; /// a [`Credential`][crate::credential::Credential] or [`Presentation`][crate::presentation::Presentation]. /// /// [More Info](https://www.w3.org/TR/vc-data-model/#terms-of-use) -#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)] pub struct Policy { /// The instance id of the credential terms-of-use. #[serde(skip_serializing_if = "Option::is_none")] diff --git a/identity_credential/src/credential/refresh.rs b/identity_credential/src/credential/refresh.rs index 1a1bd58744..0d8534cb6e 100644 --- a/identity_credential/src/credential/refresh.rs +++ b/identity_credential/src/credential/refresh.rs @@ -8,7 +8,7 @@ use identity_core::common::Url; /// Information used to refresh or assert the status of a [`Credential`][crate::credential::Credential]. /// /// [More Info](https://www.w3.org/TR/vc-data-model/#refreshing) -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct RefreshService { /// The Url of the credential refresh service. pub id: Url, diff --git a/identity_credential/src/credential/revocation_bitmap_status.rs b/identity_credential/src/credential/revocation_bitmap_status.rs index e26f0f59a1..b1cb029843 100644 --- a/identity_credential/src/credential/revocation_bitmap_status.rs +++ b/identity_credential/src/credential/revocation_bitmap_status.rs @@ -15,7 +15,7 @@ use crate::error::Result; /// Information used to determine the current status of a [`Credential`][crate::credential::Credential] /// using the `RevocationBitmap2022` specification. -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct RevocationBitmapStatus(Status); impl RevocationBitmapStatus { diff --git a/identity_credential/src/credential/schema.rs b/identity_credential/src/credential/schema.rs index cbdd60f85c..af3f2196b0 100644 --- a/identity_credential/src/credential/schema.rs +++ b/identity_credential/src/credential/schema.rs @@ -8,7 +8,7 @@ use identity_core::common::Url; /// Information used to validate the structure of a [`Credential`][crate::credential::Credential]. /// /// [More Info](https://www.w3.org/TR/vc-data-model/#data-schemas) -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct Schema { /// A Url identifying the credential schema file. pub id: Url, diff --git a/identity_credential/src/credential/status.rs b/identity_credential/src/credential/status.rs index 9f04160868..7daa6c9175 100644 --- a/identity_credential/src/credential/status.rs +++ b/identity_credential/src/credential/status.rs @@ -7,7 +7,7 @@ use identity_core::common::Url; /// Information used to determine the current status of a [`Credential`][crate::credential::Credential]. /// /// [More Info](https://www.w3.org/TR/vc-data-model/#status) -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct Status { /// A Url identifying the credential status. pub id: Url, diff --git a/identity_credential/src/credential/subject.rs b/identity_credential/src/credential/subject.rs index f33c8d2d4b..3559a2dceb 100644 --- a/identity_credential/src/credential/subject.rs +++ b/identity_credential/src/credential/subject.rs @@ -7,7 +7,7 @@ use identity_core::common::Url; /// An entity who is the target of a set of claims. /// /// [More Info](https://www.w3.org/TR/vc-data-model/#credential-subject) -#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)] pub struct Subject { /// A URI identifying the credential subject. #[serde(skip_serializing_if = "Option::is_none")] diff --git a/identity_credential/src/presentation/presentation.rs b/identity_credential/src/presentation/presentation.rs index 1b555a2a94..1fa5291b5b 100644 --- a/identity_credential/src/presentation/presentation.rs +++ b/identity_credential/src/presentation/presentation.rs @@ -26,7 +26,7 @@ use crate::error::Result; use crate::presentation::PresentationBuilder; /// Represents a bundle of one or more [Credential]s. -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct Presentation { /// The JSON-LD context(s) applicable to the `Presentation`. #[serde(rename = "@context")] diff --git a/identity_did/src/diff/method_data.rs b/identity_did/src/diff/method_data.rs index 76133af4ec..121ffb7bbb 100644 --- a/identity_did/src/diff/method_data.rs +++ b/identity_did/src/diff/method_data.rs @@ -7,7 +7,7 @@ use identity_core::diff::Result; use crate::verification::MethodData; -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub enum DiffMethodData { PublicKeyMultibase(#[serde(skip_serializing_if = "Option::is_none")] Option), PublicKeyBase58(#[serde(skip_serializing_if = "Option::is_none")] Option), diff --git a/identity_did/src/document/core_document.rs b/identity_did/src/document/core_document.rs index 2a90e6afbb..56271a7b8d 100644 --- a/identity_did/src/document/core_document.rs +++ b/identity_did/src/document/core_document.rs @@ -45,7 +45,7 @@ use crate::verification::VerificationMethod; /// A DID Document. /// /// [Specification](https://www.w3.org/TR/did-core/#did-document-properties) -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] #[rustfmt::skip] pub struct CoreDocument where diff --git a/identity_did/src/service/service.rs b/identity_did/src/service/service.rs index aa41b9b1c8..c176b2ccdd 100644 --- a/identity_did/src/service/service.rs +++ b/identity_did/src/service/service.rs @@ -24,7 +24,7 @@ use crate::service::ServiceEndpoint; /// A DID Document Service used to enable trusted interactions associated with a DID subject. /// /// [Specification](https://www.w3.org/TR/did-core/#services) -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] #[serde(bound(deserialize = "D: DID + Deserialize<'de>, T: serde::Deserialize<'de>"))] pub struct Service where diff --git a/identity_did/src/verification/method_data.rs b/identity_did/src/verification/method_data.rs index 2f90017b81..345c0e10a7 100644 --- a/identity_did/src/verification/method_data.rs +++ b/identity_did/src/verification/method_data.rs @@ -9,7 +9,7 @@ use crate::error::Error; use crate::error::Result; /// Supported verification method data formats. -#[derive(Clone, PartialEq, Deserialize, Serialize)] +#[derive(Clone, PartialEq, Eq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] #[non_exhaustive] pub enum MethodData { diff --git a/identity_did/src/verification/method_ref.rs b/identity_did/src/verification/method_ref.rs index 95265e99e2..d1b7692e91 100644 --- a/identity_did/src/verification/method_ref.rs +++ b/identity_did/src/verification/method_ref.rs @@ -13,7 +13,7 @@ use crate::did::DID; use crate::verification::VerificationMethod; /// A reference to a verification method, either a `DID` or embedded `Method`. -#[derive(Clone, PartialEq, Deserialize, Serialize)] +#[derive(Clone, PartialEq, Eq, Deserialize, Serialize)] #[serde(untagged)] pub enum MethodRef where diff --git a/identity_did/src/verification/verification_method.rs b/identity_did/src/verification/verification_method.rs index 8bdcc93fac..53d2308939 100644 --- a/identity_did/src/verification/verification_method.rs +++ b/identity_did/src/verification/verification_method.rs @@ -27,7 +27,7 @@ use crate::verification::MethodType; /// A DID Document Verification Method. /// /// [Specification](https://www.w3.org/TR/did-core/#verification-method-properties) -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct VerificationMethod where D: DID, diff --git a/identity_diff/tests/derive_enum_test.rs b/identity_diff/tests/derive_enum_test.rs index 9d98c8fdc0..2fcd971d56 100644 --- a/identity_diff/tests/derive_enum_test.rs +++ b/identity_diff/tests/derive_enum_test.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 #![cfg(feature = "derive")] +#![allow(clippy::derive_partial_eq_without_eq)] #![allow(unused_variables)] #![allow(deprecated)] diff --git a/identity_iota_client/src/document/resolved_iota_document.rs b/identity_iota_client/src/document/resolved_iota_document.rs index 0095700c20..e6030ae64f 100644 --- a/identity_iota_client/src/document/resolved_iota_document.rs +++ b/identity_iota_client/src/document/resolved_iota_document.rs @@ -21,7 +21,7 @@ use crate::tangle::TangleRef; /// An IOTA DID document resolved from the Tangle. Represents an integration chain message possibly /// merged with one or more diff messages. -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct ResolvedIotaDocument { #[serde(flatten)] pub document: IotaDocument, diff --git a/identity_iota_client/src/tangle/message/message_ext.rs b/identity_iota_client/src/tangle/message/message_ext.rs index 5b3a4e9aec..6c35186e78 100644 --- a/identity_iota_client/src/tangle/message/message_ext.rs +++ b/identity_iota_client/src/tangle/message/message_ext.rs @@ -49,7 +49,7 @@ fn parse_payload(message_id: MessageId, payload: Option // TODO: allow this to return errors? fn parse_data(message_id: MessageId, data: &[u8]) -> Option { // Check version. - let version: DIDMessageVersion = DIDMessageVersion::try_from(*data.get(0)?).ok()?; + let version: DIDMessageVersion = DIDMessageVersion::try_from(*data.first()?).ok()?; if version != DIDMessageVersion::V1 { return None; } diff --git a/identity_iota_client/src/tangle/receipt.rs b/identity_iota_client/src/tangle/receipt.rs index 288623f9a6..cc00c82825 100644 --- a/identity_iota_client/src/tangle/receipt.rs +++ b/identity_iota_client/src/tangle/receipt.rs @@ -9,7 +9,7 @@ use identity_iota_core::tangle::Message; use identity_iota_core::tangle::MessageId; use identity_iota_core::tangle::Network; -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct Receipt { network: Network, #[serde(rename = "messageId")] diff --git a/identity_iota_core/src/did/segments.rs b/identity_iota_core/src/did/segments.rs index 982ee3af6c..8ff185903c 100644 --- a/identity_iota_core/src/did/segments.rs +++ b/identity_iota_core/src/did/segments.rs @@ -21,7 +21,7 @@ macro_rules! get { } #[doc(hidden)] -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Segments<'id>(pub(crate) &'id str); impl<'id> Segments<'id> { diff --git a/identity_iota_core/src/document/iota_document.rs b/identity_iota_core/src/document/iota_document.rs index db47e19ed6..7016a711a2 100644 --- a/identity_iota_core/src/document/iota_document.rs +++ b/identity_iota_core/src/document/iota_document.rs @@ -61,7 +61,7 @@ pub type IotaCoreDocument = CoreDocument; /// A DID Document adhering to the IOTA DID method specification. /// /// This extends [`CoreDocument`]. -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct IotaDocument { #[serde(rename = "doc")] pub(crate) document: IotaCoreDocument, diff --git a/identity_iota_core/src/document/iota_document_metadata.rs b/identity_iota_core/src/document/iota_document_metadata.rs index 45095c336d..8d4b466e5c 100644 --- a/identity_iota_core/src/document/iota_document_metadata.rs +++ b/identity_iota_core/src/document/iota_document_metadata.rs @@ -17,7 +17,7 @@ use crate::tangle::MessageId; use crate::tangle::MessageIdExt; /// Additional attributes related to an IOTA DID Document. -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct IotaDocumentMetadata { #[serde(skip_serializing_if = "Option::is_none")] pub created: Option, diff --git a/identity_stardust/examples/ex3_deactivate_did.rs b/identity_stardust/examples/ex3_deactivate_did.rs index 6bc80196ba..4d7aa83cf4 100644 --- a/identity_stardust/examples/ex3_deactivate_did.rs +++ b/identity_stardust/examples/ex3_deactivate_did.rs @@ -22,9 +22,6 @@ async fn main() -> anyhow::Result<()> { // Deactivation can only be done by the state controller of the Alias Output. client.deactivate_did_output(&secret_manager, document.id()).await?; - // Wait for the node to index the new state. - tokio::time::sleep(std::time::Duration::from_secs(5)).await; - // Attempting to resolve a deactivated DID results in an document // where the metadata's `deactivated` field is `true`. let deactivated_document: StardustDocument = client.resolve_did(document.id()).await?; diff --git a/identity_stardust/examples/ex4_delete_did.rs b/identity_stardust/examples/ex4_delete_did.rs index 8a9dbb8195..baa7fc6209 100644 --- a/identity_stardust/examples/ex4_delete_did.rs +++ b/identity_stardust/examples/ex4_delete_did.rs @@ -24,9 +24,6 @@ async fn main() -> anyhow::Result<()> { .delete_did_output(&secret_manager, address, document.id()) .await?; - // Wait for the node to index the new state. - tokio::time::sleep(std::time::Duration::from_secs(5)).await; - // Attempting to resolve a deleted DID results in a `NotFound` error. let error: Error = client.resolve_did(document.id()).await.unwrap_err(); diff --git a/identity_stardust/src/client/client_ext.rs b/identity_stardust/src/client/client_ext.rs index 27858dac74..1d65b979ce 100644 --- a/identity_stardust/src/client/client_ext.rs +++ b/identity_stardust/src/client/client_ext.rs @@ -178,7 +178,7 @@ pub trait StardustClientExt: Sync { .finish_output() .map_err(Error::BasicOutputBuildError)?; - client + let block: Block = client .block() .with_secret_manager(secret_manager) .with_input(output_id.into()) @@ -189,6 +189,11 @@ pub trait StardustClientExt: Sync { .await .map_err(Error::DIDUpdateError)?; + let _ = client + .retry_until_included(&block.id(), None, None) + .await + .map_err(Error::DIDUpdateError)?; + Ok(()) } diff --git a/identity_stardust/src/document/stardust_document.rs b/identity_stardust/src/document/stardust_document.rs index 4dd237a93a..a43f0e5897 100644 --- a/identity_stardust/src/document/stardust_document.rs +++ b/identity_stardust/src/document/stardust_document.rs @@ -48,7 +48,7 @@ pub type StardustCoreDocument = CoreDocument; /// A DID Document adhering to the IOTA UTXO DID method specification. /// /// This extends [`CoreDocument`]. -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct StardustDocument { #[serde(rename = "doc")] pub(crate) document: StardustCoreDocument, diff --git a/libjose/src/jwe/header.rs b/libjose/src/jwe/header.rs index dfd11bb32b..e5d434c15e 100644 --- a/libjose/src/jwe/header.rs +++ b/libjose/src/jwe/header.rs @@ -17,7 +17,7 @@ use crate::lib::*; /// JSON Web Encryption JOSE Header. /// /// [More Info](https://tools.ietf.org/html/rfc7516#section-4) -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct JweHeader { /// Common JOSE Header Parameters. #[serde(flatten)] diff --git a/libjose/src/jwk/key.rs b/libjose/src/jwk/key.rs index 915175843b..1fea87e583 100644 --- a/libjose/src/jwk/key.rs +++ b/libjose/src/jwk/key.rs @@ -39,7 +39,7 @@ pub type JwkThumbprint = [u8; SHA256_LEN]; /// JSON Web Key. /// /// [More Info](https://tools.ietf.org/html/rfc7517#section-4) -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct Jwk { /// Key Type. /// diff --git a/libjose/src/jwk/key_set.rs b/libjose/src/jwk/key_set.rs index a7bd8e188c..f765aaa381 100644 --- a/libjose/src/jwk/key_set.rs +++ b/libjose/src/jwk/key_set.rs @@ -14,7 +14,7 @@ use crate::lib::*; /// JSON Web Key Set. /// /// [More Info](https://tools.ietf.org/html/rfc7517#section-5) -#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)] #[repr(transparent)] pub struct JwkSet { /// An array of JWK values. diff --git a/libjose/src/jwm/attributes.rs b/libjose/src/jwm/attributes.rs index 3bd0d3a2da..dcc615c740 100644 --- a/libjose/src/jwm/attributes.rs +++ b/libjose/src/jwm/attributes.rs @@ -10,7 +10,7 @@ use crate::lib::*; /// JSON Web Message Attributes Set /// /// [More Info](https://tools.ietf.org/id/draft-looker-jwm-01.html#rfc.section.3) -#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)] pub struct JwmAttributes { /// A unique identifier for the JWM. /// diff --git a/libjose/src/jws/decoder.rs b/libjose/src/jws/decoder.rs index 0032ab78c2..b580040284 100644 --- a/libjose/src/jws/decoder.rs +++ b/libjose/src/jws/decoder.rs @@ -38,7 +38,7 @@ type Ed25519Signature = crypto::signatures::ed25519::Signature; const COMPACT_SEGMENTS: usize = 3; -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct Token<'a> { pub protected: Option, pub unprotected: Option, diff --git a/libjose/src/jws/header.rs b/libjose/src/jws/header.rs index 039377de73..d4be0454f5 100644 --- a/libjose/src/jws/header.rs +++ b/libjose/src/jws/header.rs @@ -12,7 +12,7 @@ use crate::lib::*; /// JSON Web Signature JOSE Header. /// /// [More Info](https://tools.ietf.org/html/rfc7515#section-4) -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct JwsHeader { /// Common JOSE Header Parameters. #[serde(flatten)] diff --git a/libjose/src/jwt/claims.rs b/libjose/src/jwt/claims.rs index 69ffe6c531..aede10c063 100644 --- a/libjose/src/jwt/claims.rs +++ b/libjose/src/jwt/claims.rs @@ -9,7 +9,7 @@ use crate::lib::*; /// JSON Web Token Claims /// /// [More Info](https://tools.ietf.org/html/rfc7519#section-4) -#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)] pub struct JwtClaims { /// Identifies the principal that issued the JWT /// diff --git a/libjose/src/jwt/header.rs b/libjose/src/jwt/header.rs index bfc11aee79..203b8a9fe8 100644 --- a/libjose/src/jwt/header.rs +++ b/libjose/src/jwt/header.rs @@ -11,7 +11,7 @@ use crate::lib::*; /// /// [More Info (JWS)](https://tools.ietf.org/html/rfc7515#section-4) /// [More Info (JWE)](https://tools.ietf.org/html/rfc7516#section-4) -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct JwtHeader { /// JWK Set URL. /// From e722ba7c20d1f9c8032aaf44f70fc95b530c771a Mon Sep 17 00:00:00 2001 From: cycraig Date: Fri, 19 Aug 2022 15:24:44 +0200 Subject: [PATCH 36/89] Fix DID TypeScript references (#977) --- bindings/wasm/docs/api-reference.md | 96 +++++++++---------- .../wasm_account/update/set_controller.rs | 2 +- .../wasm/src/credential/credential_builder.rs | 2 +- .../src/credential/presentation_builder.rs | 2 +- bindings/wasm/src/credential/types.rs | 2 +- bindings/wasm/src/did/wasm_document.rs | 16 ++-- 6 files changed, 60 insertions(+), 60 deletions(-) diff --git a/bindings/wasm/docs/api-reference.md b/bindings/wasm/docs/api-reference.md index 80918f86d2..af8d157509 100644 --- a/bindings/wasm/docs/api-reference.md +++ b/bindings/wasm/docs/api-reference.md @@ -177,11 +177,6 @@ See IVerifierOptions.

DIDMessageEncoding
-
StateMetadataEncoding
-
-
DIDType
-

Supported types representing a DID that can be generated by the storage interface.

-
StatusCheck

Controls validation behaviour when checking whether or not a credential has been revoked by its credentialStatus.

@@ -224,10 +219,15 @@ This variant is the default used if no other variant is specified when construct
FirstError

Return after the first error occurs.

-
KeyType
-
MethodRelationship
+
StateMetadataEncoding
+
+
DIDType
+

Supported types representing a DID that can be generated by the storage interface.

+
+
KeyType
+
## Functions @@ -250,8 +250,8 @@ publishing to the Tangle. * [Account](#Account) * [.createService(options)](#Account+createService) ⇒ Promise.<void> - * [.createMethod(options)](#Account+createMethod) ⇒ Promise.<void> * [.attachMethodRelationships(options)](#Account+attachMethodRelationships) ⇒ Promise.<void> + * [.createMethod(options)](#Account+createMethod) ⇒ Promise.<void> * [.detachMethodRelationships(options)](#Account+detachMethodRelationships) ⇒ Promise.<void> * [.did()](#Account+did) ⇒ [IotaDID](#IotaDID) * [.autopublish()](#Account+autopublish) ⇒ boolean @@ -270,9 +270,9 @@ publishing to the Tangle. * [.unrevokeCredentials(fragment, indices)](#Account+unrevokeCredentials) ⇒ Promise.<void> * [.encryptData(plaintext, associated_data, encryption_algorithm, cek_algorithm, public_key)](#Account+encryptData) ⇒ [Promise.<EncryptedData>](#EncryptedData) * [.decryptData(data, encryption_algorithm, cek_algorithm, fragment)](#Account+decryptData) ⇒ Promise.<Uint8Array> - * [.setAlsoKnownAs(options)](#Account+setAlsoKnownAs) ⇒ Promise.<void> * [.deleteMethod(options)](#Account+deleteMethod) ⇒ Promise.<void> * [.deleteService(options)](#Account+deleteService) ⇒ Promise.<void> + * [.setAlsoKnownAs(options)](#Account+setAlsoKnownAs) ⇒ Promise.<void> * [.setController(options)](#Account+setController) ⇒ Promise.<void> @@ -286,17 +286,6 @@ Adds a new Service to the DID Document. | --- | --- | | options | CreateServiceOptions | - - -### account.createMethod(options) ⇒ Promise.<void> -Adds a new verification method to the DID document. - -**Kind**: instance method of [Account](#Account) - -| Param | Type | -| --- | --- | -| options | CreateMethodOptions | - ### account.attachMethodRelationships(options) ⇒ Promise.<void> @@ -311,6 +300,17 @@ it cannot be an embedded method. | --- | --- | | options | AttachMethodRelationshipOptions | + + +### account.createMethod(options) ⇒ Promise.<void> +Adds a new verification method to the DID document. + +**Kind**: instance method of [Account](#Account) + +| Param | Type | +| --- | --- | +| options | CreateMethodOptions | + ### account.detachMethodRelationships(options) ⇒ Promise.<void> @@ -513,17 +513,6 @@ Returns the decrypted text. | cek_algorithm | [CekAlgorithm](#CekAlgorithm) | | fragment | string | - - -### account.setAlsoKnownAs(options) ⇒ Promise.<void> -Sets the `alsoKnownAs` property in the DID document. - -**Kind**: instance method of [Account](#Account) - -| Param | Type | -| --- | --- | -| options | SetAlsoKnownAsOptions | - ### account.deleteMethod(options) ⇒ Promise.<void> @@ -546,6 +535,17 @@ Deletes a Service if it exists. | --- | --- | | options | DeleteServiceOptions | + + +### account.setAlsoKnownAs(options) ⇒ Promise.<void> +Sets the `alsoKnownAs` property in the DID document. + +**Kind**: instance method of [Account](#Account) + +| Param | Type | +| --- | --- | +| options | SetAlsoKnownAsOptions | + ### account.setController(options) ⇒ Promise.<void> @@ -1753,7 +1753,7 @@ Deserializes an instance from a JSON object. * _instance_ * [.id()](#Document+id) ⇒ [IotaDID](#IotaDID) * [.setController(controllers)](#Document+setController) - * [.controller()](#Document+controller) ⇒ Array.<DID> + * [.controller()](#Document+controller) ⇒ [Array.<IotaDID>](#IotaDID) * [.setAlsoKnownAs(urls)](#Document+setAlsoKnownAs) * [.alsoKnownAs()](#Document+alsoKnownAs) ⇒ Array.<string> * [.setPropertyUnchecked(key, value)](#Document+setPropertyUnchecked) @@ -1845,11 +1845,11 @@ Use `null` to remove all controllers. | Param | Type | | --- | --- | -| controllers | DID \| Array.<DID> \| null | +| controllers | [IotaDID](#IotaDID) \| [Array.<IotaDID>](#IotaDID) \| null | -### document.controller() ⇒ Array.<DID> +### document.controller() ⇒ [Array.<IotaDID>](#IotaDID) Returns a copy of the list of document controllers. **Kind**: instance method of [Document](#Document) @@ -6136,16 +6136,6 @@ This is possible because Ed25519 is birationally equivalent to Curve25519 used b ## DIDMessageEncoding -**Kind**: global variable - - -## StateMetadataEncoding -**Kind**: global variable - - -## DIDType -Supported types representing a DID that can be generated by the storage interface. - **Kind**: global variable @@ -6224,15 +6214,25 @@ Return all errors that occur during validation. ## FirstError Return after the first error occurs. -**Kind**: global variable - - -## KeyType **Kind**: global variable ## MethodRelationship **Kind**: global variable + + +## StateMetadataEncoding +**Kind**: global variable + + +## DIDType +Supported types representing a DID that can be generated by the storage interface. + +**Kind**: global variable + + +## KeyType +**Kind**: global variable ## start() diff --git a/bindings/wasm/src/account/wasm_account/update/set_controller.rs b/bindings/wasm/src/account/wasm_account/update/set_controller.rs index 5f6bf8a287..30da579b15 100644 --- a/bindings/wasm/src/account/wasm_account/update/set_controller.rs +++ b/bindings/wasm/src/account/wasm_account/update/set_controller.rs @@ -81,6 +81,6 @@ const TS_SET_CONTROLLER_OPTIONS: &'static str = r#" /** * List of DIDs to be set as controllers, use `null` to remove all controllers. */ - controllers: DID | DID[] | null, + controllers: IotaDID | IotaDID[] | null, }; "#; diff --git a/bindings/wasm/src/credential/credential_builder.rs b/bindings/wasm/src/credential/credential_builder.rs index 9928f8e5a2..898c698c04 100644 --- a/bindings/wasm/src/credential/credential_builder.rs +++ b/bindings/wasm/src/credential/credential_builder.rs @@ -125,7 +125,7 @@ struct ICredentialHelper { #[typescript(optional = false, name = "credentialSubject", type = "Subject | Array")] credential_subject: Option>, /// A reference to the issuer of the `Credential`. - #[typescript(optional = false, type = "string | DID | Issuer")] + #[typescript(optional = false, type = "string | CoreDID | IotaDID | StardustDID | Issuer")] issuer: Option, /// A timestamp of when the `Credential` becomes valid. Defaults to the current datetime. #[typescript(name = "issuanceDate", type = "Timestamp")] diff --git a/bindings/wasm/src/credential/presentation_builder.rs b/bindings/wasm/src/credential/presentation_builder.rs index 75dd61b097..ea5a7f331a 100644 --- a/bindings/wasm/src/credential/presentation_builder.rs +++ b/bindings/wasm/src/credential/presentation_builder.rs @@ -95,7 +95,7 @@ struct IPresentationHelper { )] verifiable_credential: Option>, /// The entity that generated the `Presentation`. - #[typescript(type = "string | DID")] + #[typescript(type = "string | CoreDID | IotaDID | StardustDID")] holder: Option, /// Service(s) used to refresh an expired {@link Credential} in the `Presentation`. #[typescript(name = "refreshService", type = "RefreshService | Array")] diff --git a/bindings/wasm/src/credential/types.rs b/bindings/wasm/src/credential/types.rs index eaf8070dd3..5e4b476f51 100644 --- a/bindings/wasm/src/credential/types.rs +++ b/bindings/wasm/src/credential/types.rs @@ -119,7 +119,7 @@ const I_SUBJECT: &'static str = r#" [More Info](https://www.w3.org/TR/vc-data-model/#credential-subject) */ interface Subject { /** A URI identifying the credential subject. */ - readonly id?: string | DID; + readonly id?: string | CoreDID | IotaDID | StardustDID; /** Additional properties of the credential subject. */ readonly [properties: string]: unknown; }"#; diff --git a/bindings/wasm/src/did/wasm_document.rs b/bindings/wasm/src/did/wasm_document.rs index cd1b8ecce6..1b272bf55f 100644 --- a/bindings/wasm/src/did/wasm_document.rs +++ b/bindings/wasm/src/did/wasm_document.rs @@ -110,7 +110,7 @@ impl WasmDocument { /// Note: Duplicates will be ignored. /// Use `null` to remove all controllers. #[wasm_bindgen(js_name = setController)] - pub fn set_controller(&mut self, controllers: &OptionOneOrManyDID) -> Result<()> { + pub fn set_controller(&mut self, controllers: &OptionOneOrManyIotaDID) -> Result<()> { let controllers: Option> = controllers.into_serde().wasm_result()?; let controller_set: Option> = if let Some(controllers) = controllers.map(OneOrMany::into_vec) { if controllers.is_empty() { @@ -127,7 +127,7 @@ impl WasmDocument { /// Returns a copy of the list of document controllers. #[wasm_bindgen] - pub fn controller(&self) -> ArrayDID { + pub fn controller(&self) -> ArrayIotaDID { match self.0.controller() { Some(controllers) => controllers .iter() @@ -135,8 +135,8 @@ impl WasmDocument { .map(WasmIotaDID::from) .map(JsValue::from) .collect::() - .unchecked_into::(), - None => js_sys::Array::new().unchecked_into::(), + .unchecked_into::(), + None => js_sys::Array::new().unchecked_into::(), } } @@ -689,11 +689,11 @@ extern "C" { #[wasm_bindgen(typescript_type = "DIDUrl | string")] pub type UDIDUrlQuery; - #[wasm_bindgen(typescript_type = "DID | DID[] | null")] - pub type OptionOneOrManyDID; + #[wasm_bindgen(typescript_type = "IotaDID | IotaDID[] | null")] + pub type OptionOneOrManyIotaDID; - #[wasm_bindgen(typescript_type = "DID[]")] - pub type ArrayDID; + #[wasm_bindgen(typescript_type = "IotaDID[]")] + pub type ArrayIotaDID; #[wasm_bindgen(typescript_type = "Service[]")] pub type ArrayService; From 2bdc4e540848d5403ce63ecd350fb94a32beb0f0 Mon Sep 17 00:00:00 2001 From: Abdulrahim Al Methiab <31316147+abdulmth@users.noreply.github.com> Date: Fri, 19 Aug 2022 16:09:12 +0200 Subject: [PATCH 37/89] Update DID Method Specification (#972) --- .../docs/specs/did/iota_did_method_spec.md | 385 +++++++----------- 1 file changed, 146 insertions(+), 239 deletions(-) diff --git a/documentation/docs/specs/did/iota_did_method_spec.md b/documentation/docs/specs/did/iota_did_method_spec.md index e44287eb92..5886729fc2 100644 --- a/documentation/docs/specs/did/iota_did_method_spec.md +++ b/documentation/docs/specs/did/iota_did_method_spec.md @@ -14,290 +14,214 @@ keywords: # IOTA DID Method Specification -Version 0.5-draft by Jelle Millenaar, IOTA Foundation +Draft 11 August 2022 ## Abstract -The IOTA DID Method Specification describes a method of implementing the [Decentralized Identifiers](https://www.w3.org/TR/did-core/) (DID) standard on the [IOTA Tangle](https://iota.org), a Distributed Ledger Technology (DLT). It conforms to the [DID specifications v1.0 Working Draft 20200731](https://www.w3.org/TR/2020/WD-did-core-20200731/) and describes how to publish DID Document Create, Read, Update and Delete (CRUD) operations to the IOTA Tangle. In addition, it lists additional non-standardized features that are built for the IOTA Identity implementation. +The IOTA DID Method Specification describes a method of implementing the [Decentralized Identifiers](https://www.w3.org/TR/did-core/) (DID) standard on [IOTA](https://iota.org), a Distributed Ledger Technology (DLT). It conforms to the [DID specification v1.0](https://www.w3.org/TR/did-core/) and describes how to perform Create, Read, Update and Delete (CRUD) operations for IOTA DID Documents using unspent transaction outputs ([UTXO](https://wiki.iota.org/IOTA-2.0-Research-Specifications/5.1UTXO)) on the IOTA and [Shimmer](https://shimmer.network/) networks, introduced with the [Stardust upgrade](https://blog.shimmer.network/stardust-upgrade-in-a-nutshell/). -## Introduction - -### The IOTA Tangle - -This specification defines a method of implementing DID on top of the [IOTA Tangle](https://iota.org), which is a Distributed Ledger Technology (DLT) using a Tangle data structure. In contrast to a Blockchain, the Tangle does not store messages in blocks and chain them together, but rather creates a data structure where a message references between one and eight previous messages (used to be two, as the gif shows), creating a parallel structure. - -Blockchain | Tangle -:---------:|:---------: +## Data Types & Subschema Notation -![Blockchain bottleneck](/img/blockchain-bottleneck.gif) | ![Tangle Bottleneck](/img/tangle-bottleneck.gif) +Data types and subschemas used throughout this TIP are defined in [draft TIP-21](https://github.com/iotaledger/tips/pull/41). -For this method, the most important features of IOTA are: +## Introduction -* The lack of fees, requiring no cryptocurrency tokens to be owned in order to submit a message to the DLT. -* The DLT has a public and permissionless network which runs the IOTA cryptocurrency. -* Pure data messages are possible to be stored immutably. -* Few nodes store the entire Tangle, requiring additional logic to prove the immutability of data. +### UTXO Ledger -## DID Method Name +The unspent transaction output ([UTXO](https://wiki.iota.org/IOTA-2.0-Research-Specifications/5.1UTXO)) model defines a ledger state where outputs are created by a transaction consuming outputs of previous transactions as inputs. IOTA and Shimmer have several output types, the relevant ones for the IOTA DID Method are: Basic Outputs for value transactions, and Alias Outputs for storage of DID documents. -The namestring to identify this DID method is: `iota`. +All outputs must hold a minimum amount of coins to be stored on the ledger. For output types that can hold arbitrary data, for instance the Alias Output, the amount of coins held by the output must cover the byte cost of the data stored. This helps control the ledger size from growing uncontrollably while guaranteeing that the data is not pruned from the nodes, which is important for resolving DID Documents. This deposit is fully refundable and can be reclaimed when the output is destroyed. -A DID that uses this method MUST begin with the following prefix: `did:iota`. Following the generic DID specification, this string MUST be lowercase. +Data saved in an output and covered by the storage deposit will be stored in *all* nodes on the network and can be retrieved from any node. This provides strong guarantees for any data stored in the ledger. -## DID Format -The DIDs that follow this method have the following format: -``` -iota-did = "did:iota:" iota-specific-idstring -iota-specific-idstring = [ iota-network ":" ] iota-tag -iota-network = char{,6} -iota-tag = base-char{44} -char = 0-9 a-z -base-char = 1-9 A-H J-N P-Z a-k m-z -``` +### Alias Output -### IOTA-Network +The [Alias Output](https://github.com/lzpap/tips/blob/master/tips/TIP-0018/tip-0018.md#alias-output) is a specific implementation of the [UTXO state machine](https://github.com/lzpap/tips/blob/master/tips/TIP-0018/tip-0018.md#chain-constraint-in-utxo). Some of its relevant properties are: -The iota-network is an identifier of the network where the DID is stored. This network must be an IOTA Tangle, but can either be a public or private network, permissionless or permissioned. +* **Amount**: the amount of IOTA coins held by the output. +* **Alias ID**: 32 byte array, a unique identifier of the alias, which is the BLAKE2b-256 hash + of the Output ID that created it. +* **State Index**: A counter that must increase by 1 every time the alias is state transitioned. +* **State Metadata**: Dynamically sized array of arbitrary bytes with a length up to `Max Metadata Length`, as defined in [TIP-22](https://github.com/iotaledger/tips/blob/main/tips/TIP-0022/tip-0022.md). Can only be changed by the state controller. +* **Unlock Conditions**: + * State Controller Address Unlock Condition + * Governor Address Unlock Condition -The following values are reserved and cannot reference other networks: -1. `main` references the main network which refers to the Tangle known to host the IOTA cryptocurrency. -2. `dev` references the development network known as "devnet" maintained by the IOTA Foundation. +Consuming an Alias Output in a transaction means that the alias is transitioned into the next state. The current state is defined as the consumed Alias Output, while the next state is defined as the **Alias Output with the same explicit `Alias ID` on the output side**. There are two types of transitions: `state transition` and `governance transition`. -When no IOTA network is specified, it is assumed that the DID is located on the `main` network. This means that the following DIDs will resolve to the same DID Document: -``` -did:iota:main:H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV -did:iota:H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV -``` +All outputs include an `Unlock Conditions` property. This feature defines how the output can be unlocked and spent. The Alias Output supports two types of unlock conditions that can be set: the state controller and governor. Each of these can be either an Ed25519 Address, Alias Address or an NFT Address. An Alias Output can have at most one of each unlock condition. -### IOTA-Tag +The state controller can unlock a state transition. It is identified by an incremented `State Index` and can change the fields `Amount`, `State Index`, `State Metadata` among other properties. -The IOTA tag references an indexation which resolves to the initial DID Messages. +The governor, on the other hand, can unlock a governance transition indicated by an unchanged `State Index`. A governance transition can change the addresses of the state controller and governor. It also allows destroying the Alias Output. -#### Generation +### Ledger and DID +Storing DID Documents in the ledger state means they inherently benefit from the guarantees the ledger provides. -The following steps MUST be taken to generate a valid tag: -* Generate an asymmetric keypair using a supported verification method type. -* Hash the public key using `BLAKE2b-256` then encode it using [Base58-BTC](https://tools.ietf.org/id/draft-msporny-base58-01.html). +1. Conflicts among nodes are sorted out and dealt with by the ledger. +2. Replay attacks are not possible since transactions need to be confirmed by the ledger. +3. The Alias Output includes the `State Index` which provides linear history for updates of a DID Document. -This public key MUST be embedded into the DID Document (see [CRUD: Create](#create)). - -## DID Messages +## DID Method Name -DID Documents associated with the `did:iota` method consist of a chain of data messages, called "DID messages", published to a Tangle. The Tangle has no understanding of DID messages and acts purely as an immutable database. The chain of DID messages and the resulting DID Document must therefore be validated on the client side. +The `method-name` to identify this DID method is: `iota`. -A DID message can be part of one of two different message chains, the "Integration Chain" (Int Chain) and the "Differentiation Chain" (Diff Chain). The Integration Chain is a chain of "Integration DID Messages" that contain JSON formatted DID Documents as per the W3C standard for DID. The Diff Chain is a chain of "DID Diff Messages" that contain JSON objects which only list the differences between the previous DID Document and the next state. +A DID that uses this method MUST begin with the following prefix: `did:iota`. Following the generic DID specification, this string MUST be lowercase. -### Previous Message Id +## DID Format -All DID message uploaded to the Tangle, with the exception of the very first DID message that creates the DID, MUST contain a `previousMessageId` field. This field MUST carry the MessageId, an IOTA indexation for a single message, of the previous DID Document that is updated with this DID message. This value SHOULD be used to order DID messages during the resolving procedure. If two or more DID messages reference the same `previousMessageId` an ordering conflict is identified and is resolved using a [deterministic ordering mechanism](#determining-order). +The DIDs that follow this method have the following ABNF syntax. It uses the syntax in [RFC5234](https://www.rfc-editor.org/rfc/rfc5234) and the corresponding definition for `digit`. -Example of an IOTA MessageId: -```json -"previousMessageId": "cd8bb7baca6bbfa1de7813bd1753a2de026b6ec75dba8a3cf32c0d4cf6038917" +``` +iota-did = "did:iota:" iota-specific-idstring +iota-specific-idstring = [ iota-network ":" ] iota-tag +iota-network = 0*6lowercase-alpha +iota-tag = "0x" 64lowercase-hex +lowercase-alpha = %x61-7A ; corresponds to the character range from "a" to "z". +lowercase-hex = digit / "a" / "b" / "c" / "d" / "e" / "f" ``` -### Signing Keys - -DID messages published to the Tangle must be cryptographically signed. As such, the DID Document MUST include at least one public key with a [capability invocation relationship](https://www.w3.org/TR/did-core/#capability-invocation). Only verification methods with a [capability invocation relationship](https://www.w3.org/TR/did-core/#capability-invocation) are allowed to sign IOTA DID Document updates. It is recommended, for security reasons, not to use signing keys for other purposes as control over them is vital for controlling the identity. It is RECOMMENDED to name the initial verification method `#sign-x`, where `x` is the index of the signing key, which is incremented every time the signing key is updated, starting at index 1. +It starts with the string "did:iota:", followed by an optional network name (0 to 6 lowercase alpha characters) and a colon, then the tag. +The tag starts with "0x" followed by a hex-encoded `Alias ID` with lower case a-f. -The initial DID message containing a newly-generated DID Document MUST be signed with the same keypair used to derive its [IOTA-Tag](#iota-tag) (see [CRUD: Create](#create)). +### IOTA-Network -See [Standardized Verification Method Types](#standardized-verification-method-types) for which cryptographic key types are supported. +The iota-network is an identifier of the network where the DID is stored. This network must be an IOTA Ledger, but can either be a public or private network, permissionless or permissioned. -### Anatomy of Integration DID Messages +The following values are reserved and cannot reference other networks: -An Integration (Int) DID message MUST contain both a valid DID Document and a [DID Document Metadata](https://www.w3.org/TR/did-core/#did-document-metadata) according to the W3C DID standard. In addition, the message has further restrictions: +1. `iota` references the main network which refers to the ledger known to host the IOTA cryptocurrency. +2. `atoi` references the development network of IOTA. +3. `smr` references the shimmer network. +4. `rms` references the development network of Shimmer. -* The DID Document MUST contain one or more verification methods capable of signing updates as defined in the [Signing Keys](#signing-keys) section. -* The first DID Document in the chain MUST contain a `verificationMethod` that contains a public key that, when hashed using the `Blake2b-256` hashing function, equals the tag section of the DID. This prevents the creation of conflicting entry messages of the chain by adversaries. -* An Integration DID message must be published to an IOTA Tangle on an index that is generated by the `BLAKE2b-256` of the public key, created in the [generation](#generation) event, encoded in `hex`. -* Integration DID messages SHOULD contain all cumulative changes from the Diff Chain associated to the last Integration Chain message. Any changes added in the Diff Chain that are not added to the new Integration DID message will be lost. -* The DID Document Metadata MUST include a `previousMessageId` attribute. This field provides an immutable link to the previous integration DID message that is used for basic ordering of the DID messages, creating a chain. The value of `previousMessageId` MUST be a string that contains an IOTA MessageId from the previous DID message it updates, which MUST reference an integration DID message. The field SHOULD be omitted if the DID message is the start of the Int chain, otherwise the field is REQUIRED. Read the [Previous Message Id](#previous-message-id) section for more information. -* The DID Document MUST include a `proof` attribute. This field provides a cryptographic proof on the message that proves ownership over the DID Document. The value of the `proof` object MUST contain an object as defined by [Anatomy of the Proof object](#anatomy-of-the-proof-object). +When no IOTA network is specified, it is assumed that the DID is located on the `iota` network. This means that the following DIDs will resolve to the same DID Document: -Example of an Integration DID Message: -```json -{ - "doc": { - "id": "did:iota:ERtmNv3hYnWU7fZMGKpMLy7QBJqPovCSYyewtoHUGmpf", - "capabilityInvocation": [ - { - "id": "did:iota:ERtmNv3hYnWU7fZMGKpMLy7QBJqPovCSYyewtoHUGmpf#sign-0", - "controller": "did:iota:ERtmNv3hYnWU7fZMGKpMLy7QBJqPovCSYyewtoHUGmpf", - "type": "Ed25519VerificationKey2018", - "publicKeyMultibase": "zETX79R6G5fkTMZhHXaCMhjC3Xpx3NLJVSNurat8Ls9Tn" - } - ] - }, - "meta": { - "created": "2022-03-18T06:59:50Z", - "updated": "2022-03-18T06:59:50Z" - }, - "proof": { - "type": "JcsEd25519Signature2020", - "verificationMethod": "did:iota:ERtmNv3hYnWU7fZMGKpMLy7QBJqPovCSYyewtoHUGmpf#sign-0", - "signatureValue": "278R6WyoG359VjnGm2GJK6XAjBNh6AM59BXJmjfRQerVQMTG3EjWGXw64CKgGKvVBbP98QMVUw1YXBbuGuDbJW6A" - } -} ``` - -### Anatomy of Diff DID Messages - -A Differentiation (Diff) DID message does not contain a valid DID Document. Instead, the chain creates a list of incremental changes compared to the Integration DID message that is used as a basis. The Diff DID messages are hosted on a different index on the Tangle, which allows skipping older Diff DID messages during a query, optimizing the client verification speed significantly. - -* A Diff DID message is NOT allowed to add, remove or update any [signing keys](#signing-keys). This must be done via an Integration DID message. -* A Diff DID message must be published to an IOTA Tangle on an index that is generated by the hash, generated by the `BLAKE2b-256` hashing algorithm, of the `previousMessageId` of the latest Integration DID message and encoded in `hex`. -* A Diff DID message MUST have at least the following attributes: - * `id` (REQUIRED): This field helps link the update to a DID. The value of `id` MUST be a string that references the DID that this update applies to. - * `previousMessageId` (REQUIRED): This field provides an immutable link to the previous DID document that is updated and is used for basic ordering of the DID messages, creating a chain. The value of `previousMessageId` MUST be a string that contains an IOTA MessageId from the previous DID message it updates, which references either a Diff or Int Chain message. Read the [Previous Message Id](#previous-message-id) section for more information. - * `diff` (REQUIRED): A Differentiation object containing all the changes compared to the DID Document it references in the `previousMessageId` field. The value of `diff` MUST be a JSON object following the [Anatomy of the Diff object](#anatomy-of-the-diff-object) definition. - * `proof` (REQUIRED): This field provides a cryptographic proof on the message that proves ownership over the DID Document. The value of the `proof` object MUST contain an object as defined by [Anatomy of the Proof object](#anatomy-of-the-proof-object). - -Example of a Diff DID message: -```json -{ - "id": "did:iota:X7U84ez4YeaLwpfdnhdgFyPLa53twvAuMSYdRQas54e", - "diff": { - "doc": { - "service": [ - { - "id": "did:iota:X7U84ez4YeaLwpfdnhdgFyPLa53twvAuMSYdRQas54e#linked-domain-1", - "type_": "LinkedDomains", - "service_endpoint": "https://example.com/" - } - ] - }, - "meta": { - "updated": "2022-01-21T09:50:28Z" - } - }, - "previousMessageId": "5dfff82c34c75b3436e7e03370e220e4693d39026fee315e6db7b7815305df4a", - "proof": { - "type": "JcsEd25519Signature2020", - "verificationMethod": "#sign-0", - "signatureValue": "3TG8LiXbWPcH3ecg4is5mswENVuQBakhv8R5TMXopHgPg578oLoczySvZMsEdjRHYgJGihK2VHEqyCHPjQyXW61m" - } -} +did:iota:iota:0xe4edef97da1257e83cbeb49159cfdd2da6ac971ac447f233f8439cf29376ebfe +did:iota:0xe4edef97da1257e83cbeb49159cfdd2da6ac971ac447f233f8439cf29376ebfe ``` -### Anatomy of the Proof object +### IOTA-Tag +An IOTA-tag is a hex-encoded `Alias ID`. The `Alias ID` itself is a unique identifier of the alias, which is the BLAKE2b-256 hash of the Output ID that created it. +This tag identifies the Alias Output where the DID Document is stored, and it will not be known before the generation of the DID since it will be assigned when the Alias Output is created. -Following the proof format in the [Verifiable Credential standard](https://www.w3.org/TR/vc-data-model/#proofs-signatures), at least one proof mechanism, and the details necessary to evaluate that, MUST be expressed for a DID Document uploaded to the Tangle. +### Anatomy of the State Metadata -The proof object is an embedded proof that contains all information to be verifiable. It contains one or more cryptographic proofs that can be used to detect tampering and verify the authorship of a DID creation or update. It mostly follows LD-Proofs standard. +In the `State Metadata` of the Alias Output must be a byte packed payload with header fields as follows: -**Type** +| Name | Type | Description | +|---------------|--------------|----------------------------------------------------------------------------------------------------------------------------------------------------------| +| Document Type | ByteArray[3] | Set to value **DID** to denote a DID Document. | +| Version | uint8 | Set value **1** to denote the version number of this method | +| Encoding | uint8 | Set to value to **0** to denote JSON encoding without compression. | +| Payload | ByteArray | A DID document and its metadata, where every occurrence of the DID in the document is replaced by `did:0:0`. It must be encoded according to `Encoding`. | -The proof object MUST include a `type` property. This property references a verification method type's signature algorithm, as standardized in the [DID spec registries](https://www.w3.org/TR/did-spec-registries/#verification-method-types) or standardized via the method specification. +Next to [TIP-21](#data-types--subschema-notation), we use the following type definitions: -The IOTA Identity implementation currently supports: -- `JcsEd25519Signature2020` +| Name | Description | +|-----------|-------------------------------------------------| +| ByteArray | A dynamically sized, but unprefixed byte array. | -It must be emphasized that the IOTA Identity implementation of `JcsEd25519Signature2020` follows the [official specification](https://identity.foundation/JcsEd25519Signature2020/) with the single exception that the SHA-256 pre-hash of the input, the third step of the [proof generation algorithm](https://identity.foundation/JcsEd25519Signature2020/#ProofGeneration), is skipped. See the following two GitHub issues [#22](https://github.com/decentralized-identity/JcsEd25519Signature2020/issues/22) and [#26](https://github.com/decentralized-identity/JcsEd25519Signature2020/issues/26) for more context on why this deviation is deemed necessary. -**Verification Method** +#### Payload -The proof object MUST include a `verificationMethod` which references a verification method embedded in the same DID Document. The verification method must be capable of signing DID messages as per the [Signing Keys](#signing-keys) section. +The payload must contain the following fields: -**Signature** +* `metadata`: contains metadata about the DID document. For example, `created` to indicate the time of + creation, and `updated` to indicate the time of the last update to the document. It can also include other properties. +* `document`: which contains the DID document. In the example below, the document only contains one verification method. The `id` and `controller` is specified by `did:0:0` which references the DID of the document itself, since the DID is unknown at the time of publishing. It also deduplicates the DID of the document to reduce the size of the state metadata, in turn reducing the required storage deposit. -Depending on the verification method, a set of fields are REQUIRED to provide the cryptographic proof of the DID Document. For example, the `JcsEd25519Signature2020` method has a `signatureValue` field. +Example State Metadata Document: -Example `proof` using the `JcsEd25519Signature2020` method: -```json -"proof": { - "type": "JcsEd25519Signature2020", - "verificationMethod": "did:iota:GzXeqBXGCbuebiFtKo4JDNo6CmYmGbqxyh2fDVKadiBG#authentication", - "signatureValue": "3fLtv3KUU4T5bHNLprV3UQ2Te3bcRZ9uUYSFouEA7fmYthieV35NNLqbKUu8t2QmzYgnfp1KMzCqPzGNi3RjU822" +```Json +{ + "document":{ + "id":"did:0:0", + "verificationMethod":[ + { + "id":"did:0:0#key-1", + "controller":"did:0:0", + "type":"Ed25519VerificationKey2018", + "publicKeyMultibase":"z6BNtMbKY78XDVuqfh4u15bZkByu94XNVr9RpqEGCNncn" + } + ] + }, + "metadata":{ + "created":"2022-08-02T21:39:48Z", + "updated":"2022-08-02T21:39:48Z" + } } ``` -### Anatomy of the Diff object -The `diff` object MUST contain all the differences between the current and previous DID Document and its DID Document Metadata. The differentiation is formatted as a JSON object that includes the differences between the two DID Document objects and the two DID Document Metadata objects. Exact details of how this is generated will be added later. +## Controllers -Example `diff` of adding a new service entry to the document and changing the `updated` field in the metadata: -```json -{ - "diff": { - "doc": { - "service": [ - { - "id": "did:iota:X7U84ez4YeaLwpfdnhdgFyPLa53twvAuMSYdRQas54e#linked-domain-1", - "type_": "LinkedDomains", - "service_endpoint": "https://example.com/" - } - ] - }, - "meta": { - "updated": "2022-01-21T09:50:28Z" - } - } -} -``` +A state controller can directly update the DID Document and the amount of coins held by the Alias Output, but it cannot destroy the output. A governor, on the other hand, can indirectly update the DID Document by updating the state controller. The governor can also destroy the output by performing a governance transition without producing an Alias Output with the same `Alias ID`. + +As of now, only one state controller and one governor can be set for an Alias Output. Support for multiple controllers may be possible depending on future updates of the protocol. ## CRUD Operations -Create, Read, Update and Delete (CRUD) operations that change the DID Documents are to be submitted to an IOTA Tangle in order to be publicly available. They will either have to be a valid Int DID message or Diff DID message, submitted on the correct index of the identity. +Create, Read, Update and Delete (CRUD) operations that change the DID Documents are done through state or governance transitions of the Alias Output. + +**These operations require fund transfer to cover byte cost. Transactions must be carefully done in order to avoid fund loss.** For example, the amount of funds in the inputs should equal these in the outputs. Additionally, private keys of controllers must be stored securely. ### Create +In order to create a simple self controlled DID two things are required: +1. An Ed25519 Address for which the private key is available, or control over an Alias or NFT Output. +2. A Basic, Alias or NFT Output with enough coins to cover the byte cost. -To generate a new DID, the method described in [generation](#generation) must be followed. A basic DID Document must be created that includes the public key used in the DID creation process as a `verificationMethod` with a capability invocation verification relationship. This DID Document must be formatted as an Integration DID message, signed using the same keypair used to generate the tag, and published to an IOTA Tangle on the index generated out of the public key used in the DID creation process. +Creation steps: +1. Create the content of the DID Document like verification methods, services, etc. +2. Create the payload and the headers as described in the [Anatomy of the State Metadata](#anatomy-of-the-state-metadata). +3. Create a new Alias Output with the payload and the headers stored in its `State Metadata`. +4. Set the state controller and the governor unlock conditions to the addresses that should control state and governance transitions, respectively. +5. Set enough coins in the output to cover the byte cost. +6. Publish a new transaction with an existing output that contains at least the storage deposit from step 6 as input, and the newly created Alias Output as output. + +Once the transaction is confirmed, the DID is published and can be formatted by using the `Alias ID` as the tag in [DID Format](#did-format). ### Read -To read the latest DID Document associated with a DID, the following steps are to be performed: -1. Query all the Integration DID messages from the index, which is the `tag` part of the DID. -2. Order the messages based on `previousMessageId` linkages. See [Determining Order](#determining-order) for more details. -3. Validate the first Integration DID message containing a verification method with a public key that, when hashed using the `BLAKE2b-256` hashing algorithm, equals the `tag` field of the DID as per [generation](#generation). This first Integration DID message MUST also contain a valid signature referencing the same verification method. -4. Verify the signatures of all the DID messages. Signatures must be created using a public key available in the previous DID message linked to a verification method capable of signing DID messages. See [Signing Keys](#signing-keys). -5. Ignore any messages that are not signed correctly, afterwards ignore any messages that are not first in the ordering for their specific location in the chain. -6. Query all the Diff DID messages from the index, generated by the MessageId from the last valid Integration DID message, hashed using `Blake2b-256` and encoded in `hex`. -7. Order messages and validate signatures in a similar manner to steps 2, 4 and 5 above. The first valid Diff DID message MUST have a `previousMessageId` referencing the `messageId` of the last Integration DID message. -8. Ignore Diff DID messages with illegal operations such as removing or updating a signing key. -9. Apply all valid Diff updates to the state generated from the Integration DID messages. This will result in the latest DID Document. - -#### Determining Order - -To determine the order of any DID messages, the following steps are to be performed: -1. Order is initially established by recreating the chain based on the `previousMessageId` linkages. - 1. For Int DID messages, the one with no `previousMessageId` is first. - 2. For Diff DID messages, the one with a `previousMessageId` referencing the IOTA MessageId of the corresponding Integration DID message is first. -2. When two or more Messages compete, an order must be established between them. -3. To determine the order, check which milestone confirmed the messages. -4. If multiple messages are confirmed by the same milestone, we order based on the IOTA MessageId with alphabetical ordering. +The following steps can be used to read the latest DID document associated with a DID. +1. Obtain the `Alias ID` from the DID by extracting the `iota-tag` from the DID, see [DID Format](#did-format). +2. Obtain the network of the DID by extracting the `iota-network` from the DID, see [DID Format](#did-format). +3. Query the Alias Output corresponding to the `Alias ID` using a node running the [inx indexer](https://github.com/iotaledger/inx-indexer). Nodes usually include this indexer by default. +4. Assert that the extracted network matches the one returned from the node. Return an error otherwise. +5. Assert that the `Alias ID` of the returned output matches the `Alias ID` extracted from the DID. Return an error otherwise. +6. Retrieve the value of the `State Metadata` field from the returned output. +7. Check if its content matches the [Anatomy of the State Metadata](#anatomy-of-the-state-metadata). Return an error otherwise. +8. Decode the DID document from the `State Metadata`. +9. Replace the placeholder `did:0:0` with the DID given as input. ### Update -In order to update a DID Document, either an Integration or a Diff DID message needs to be generated. It is RECOMMENDED to use only Integration DID messages if the DID Document is updated very infrequently and it is expected to never go beyond 100 updates in the lifetime of the DID. If that is not the case, it is RECOMMENDED to use as many Diff DID messages instead with a maximum of around 100 updates per Diff chain. - -#### Creating an Integration DID message - -An Integration DID message is unrestricted in its operations and may add or remove any fields to the DID Document. In order to query the DID Document, every Integration DID message must be processed, therefore it is RECOMMENDED to reduce the usage of these messages unless the DID Document is updated very infrequently. +Updating a DID Document can be achieved by a state transition of the Alias Output. A state controller can update the +`State Metadata` in the Alias Output which reflects an update to the DID Document. -In order to create a valid Integration DID message, the following steps are to be performed: -1. Create a new DID Document that contains all the new target state. This SHOULD include the new desired changes and all the changes inside the previous Diff Chain, otherwise these changes are lost! -2. Retrieve the IOTA MessageId from the previous Integration DID message and a keypair for signing the DID Document. -3. Set the `previousMessageId` field to the IOTA MessageId value. -4. Create a `proof` object referencing the public key and signature suite from a verification method in the DID Document able to sign updates as per the [Signing Keys](#signing-keys) section. -5. Create a cryptographic signature using the same keypair and add the result to the appropriate field(s) inside the `proof` object. +1. Create the content of the updated DID Document. +2. Create the payload and the headers as described in the [Anatomy of the State Metadata](#anatomy-of-the-state-metadata). +3. Create a new Alias Output with the same `Alias ID` as the current output. +4. Increment the `State Index`. +5. Leave the unlock conditions unchanged. +6. Set enough coins in the output to cover the byte cost. +7. Publish a new transaction that includes the current Alias Output as input and the newly created one as output. If the Alias Output to be updated is state-controlled by other Alias or NFT Outputs, those outputs will have to be unlocked in the same transaction. -#### Creating a Diff DID message - -A Differentiation DID message is restricted in its usage. It may not update any signing keys that are used in the Diff chain. If this is desired, it is REQUIRED to use an Integration DID message. If the current Diff chain becomes too long (currently RECOMMENDED to end at a length of 100), it is RECOMMENDED to use a single Integration DID message to reset its length. +### Delete -In order to create a valid Integration DID message, the following steps are to be performed: -1. Create and serialize a `diff` JSON object that contains all the changes. -2. Set the `did` field to the DID value that this update applies to. -3. Retrieve the IOTA MessageId from the previous Diff chain message, or Integration DID message if this message is the first in the Diff chain. In addition, retrieve a keypair for signing the DID Document. -4. Set the `previousMessageId` field to the IOTA MessageId value. -5. Create a `proof` object referencing the public key and signature suite from a verification method in the DID Document able to sign updates as per the [Signing Keys](#signing-keys) section. -6. Create a cryptographic signature using the same keypair and add the result to the appropriate field(s) inside the `proof` object. +#### Deactivate +Temporarily deactivating a DID can be done by deleting the content of the `State Meadata` in the Alias Output. +1. Set the Alias Output's `State Metadata` field to an empty byte array. +2. Increment the `State Index`. +3. Set the state controller and the governor. +4. Publish a new transaction with the current Alias Output as input and the newly created one as output. -### Delete +Another option is to update the DID Document and set the `deactivated` property in `metadata` to true. In this case the deactivated DID Document will be deactivated, but it can still be resolved, see [Update](#update). -In order to deactivate a DID document, a valid Integration DID message must be published that removes all content from a DID Document, effectively deactivating the DID Document. Keep in mind that this operation is irreversible. +#### Destroy +In order to permanently destroy a DID, a new transaction can be published that consumes the Alias Output without having an Alias Output on the output side with a corresponding explicit `Alias ID`. This results in destroying the Alias Output and the DID. Note that this operation is irreversible resulting in permanently deleting the DID. ## IOTA Identity standards @@ -312,49 +236,32 @@ The IOTA Identity framework currently supports two Verification Method Types: ### Revocation -Revocation of Verifiable Credentials and signatures is currently achieved through revoking public keys in the IOTA Identity framework. Alternatively, developers should consider using the [`credentialStatus` property](https://www.w3.org/TR/vc-data-model/#status) when issuing and revoking Verifiable Credentials at scale. +Revocation of Verifiable Credentials and signatures can be achieved using the [Revocation Bitmap 2022](../revocation_bitmap_2022.md) where issuers store a bitmap of indices in the DID Document. These indices correspond to verifiable credentials they have issued. If the binary value of the index in the bitmap is 1 (one), the verifiable credential is revoked, if it is 0 (zero) it is not revoked. ### Standardized Services The IOTA Identity framework also standardized certain `services` that are embedded in the DID Document. It is RECOMMENDED to implement these when implementing the `did:iota` method. Currently standardized `services`: + * Nothing yet. ## Security Considerations +The `did:iota` method is implemented on the [IOTA](https://iota.org), a public permissionless and feeless Distributed Ledger Technology (DLT), making it resistant against almost all censorship attack vectors. Up until the `Coordicide` update for the IOTA network, a reliability on the coordinator exists for resolving ordering conflicts. This has a minor censorship possibility, that, in the wrost case, can prevent transactions from getting confirmed. -The `did:iota` method is implemented on the [IOTA Tangle](https://iota.org), a public permissionless and feeless Distributed Ledger Technology (DLT), making it resistant against almost all censorship attack vectors. Up until the `Coordicide` update for the IOTA network, a reliability on the coordinator exists for resolving ordering conflicts. This has a minor censorship possibility, that, in the worst case, can prevent ordering conflicts from resolving to a DID. However, these can only be published by the owner of the private key and therefore does not constitute a usable attack vector. Lastly, a node may decide to censor DID messages locally, however a user can easily use another node. - -Since DID messages are always to be signed and the 'chain of custody' of the signing key can be traced throughout the identity lifespan, replay, message insertion, deletion, modification and man-in-the-middle attacks are prevented. - -### Stateless Identities - -Unlike for-purpose blockchains or Smart Contract based DID methods, the IOTA Tangle does not track and store the state of DID Document. This prevents dusting attacks against the nodes. However, the client that resolves the DID Document has the responsibility to always validate the entire history of the DID Document as to validate the 'chain of custody' of the signing keys. If a client does not do this, identity hijacking is trivially possible. The IOTA Identity framework, which implements the `did:iota` method, provides an easy-to-use client-side validation implementation. - -### Snapshotting - -IOTA allows feeless data and value messages. As such, it has a large history of messages amounting to several TBs at the time of writing. Most nodes 'snapshot' (forget/delete) older transactions, while they keep the UTXOs stored in the ledger. The snapshot settings are local, therefore all nodes may store a different length of history. As such, older DID messages would not be available at every node. Since the entire history of the DID Document is required for client-side validation, this may become problematic. It is currently recommended to either use your own node that does not snapshot, or use the [Chronicle Permanode](https://github.com/iotaledger/chronicle.rs), which keeps the entire history of the Tangle. This is not a longterm scalable solution, which is why other methods for either keeping state or selective storage are considered. - -### Denial of Service Attacks - -There is little to no barrier for users to send any message to the Tangle. While this is a great feature of IOTA, it has its drawbacks in terms of spam. Anyone can post messages on the index of an Identity and therefore decrease the query speed. To reduce a user's own load on their identity verification speed, the Diff chain was introduced. Other messages that are spammed are identified and dropped as early as possible in the resolution process without triggering any errors or warnings. These are, for example, random data messages or incorrectly signed DID messages. - -IOTA nodes provide pagination for more then 100 messages on an index, requiring multiple queries for identities with more messages. Since the queries are the bottleneck in the resolution process, it is recommended to run an IOTA node in a nearby location if fast verification is required for the use case. Verification may, in the future, also be outsourced to nodes directly, to reduce the effect of spam attacks. If an identity is significantly spammed, it might be best to create a new identity. - -### Quantum Computer Threats - -The `did:iota` method is no more vulnerable to quantum computers than other DID methods. The IOTA Identity framework currently utilizes elliptic curve based cryptographic suites, however adding support for quantum secure cryptographic suites in the future is very easy. ### Private Key Management -All private keys or seeds used for the `did:iota` method should be equally well protected by the users. The signing key is especially important as it controls how keys are added or removed, providing full control over the identity. The IOTA Identity framework utilizes the [Stronghold project](https://github.com/iotaledger/stronghold.rs), a secure software implementation isolating digital secrets from exposure to hacks or leaks. Developers may choose to add other ways to manage the private keys in a different manner. +All private keys or seeds used for the `did:iota` method should be equally well protected by the users. Private keys of the state controller and the governor are especially important as they control how keys are added or removed, providing full control over the identity. The IOTA Identity framework utilizes the [Stronghold project](https://github.com/iotaledger/stronghold.rs), a secure software implementation isolating digital secrets from exposure to hacks or leaks. Developers may choose to add other ways to manage the private keys in a different manner. ## Privacy Considerations ### Personal Identifiable Information -The public IOTA Tangle networks are immutable networks. This means that once something is uploaded, it can never be completely removed. That directly conflicts with certain privacy laws such as GDPR, which have a 'right-to-be-forgotten' for Personal Identifiable Information (PII). As such, users should NEVER upload any PII to the Tangle, including inside DID Documents. The IOTA Identity framework allows Verifiable Credentials to be published to the Tangle directly, however this feature should only be utilized by Identity for Organisation and Identity for Things. +The public IOTA Tangle networks are immutable networks. This means that once something is included, it can never be completely removed. For example, destroying an Alias Output will remove it from the ledger state, but it can still be stored in permanodes or by any party that records historical ledger states. + +That directly conflicts with certain privacy laws such as GDPR, which have a 'right-to-be-forgotten' for Personal Identifiable Information (PII). As such, users should NEVER upload any PII to the Tangle, including inside DID Documents. The IOTA Identity framework allows Verifiable Credentials to be published to the Tangle directly, however this feature should only be utilized by Identity for Organisations and Identity for Things. ### Correlation Risks -As with any DID method, identities can be linked if they are used too often and their usage somehow becomes public. IOTA provides a simple solution for this problem: as creating identities is completely feeless, it is recommended to make new identities on a per interaction basis (Pairwise DIDs). The IOTA Identity framework will provide support for this in the future. +As with any DID method, identities can be linked if they are used too often and their usage somehow becomes public. See [DID Correlation Risks](DID Correlation Risks). Additionally, a DID can be correlated with funds if the Alias Output used to store the DID Document or any of its controllers is used for holding, transferring or controlling coins or NFTs. \ No newline at end of file From 0d5ed61508f80bc600577c1c9d78a37c2b5e18bc Mon Sep 17 00:00:00 2001 From: cycraig Date: Mon, 22 Aug 2022 12:32:01 +0200 Subject: [PATCH 38/89] Add Wasm Stardust Client (#975) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Split StardustClientExt into StardustIdentityClient/Base traits * Add network checks to StardustClientExt * Add context to example errors, increase faucet timeout * Fix compilation without `iota-client` feature, relax Send restriction * Re-export bee_block as the block module * Add IStardustIdentityClient, IStardustIdentityClientExt interfaces * [WIP] TypeScript client extension attempt * Remove expect, make error messages more verbose * Fix AliasOutput serialization, update example * Move deactivate_did_output to StardustIdentityClient Add allow_empty to StardustDocument::unpack. * Update Wasm bindings * Implement publishDidOutput for Wasm bindings * Clean Wasm ex0_create_did example * Implement deleteDidOutput for Wasm bindings * Add ex1_update_did Wasm example, add examples-stardust directory * Add ex2_resolve_did example for Wasm * Add metadata deactivated field Wasm bindings * Fix input commitment hash calculation for multiple inputs Sort inputs. Add some block validation checks. Fix unlocking logic. Fix consumeAmount, remainderAmount calculations. * Add ex4_delete_did Wasm example * Fix StateMetadataDocument serialised fieldnames Remove timestamps when unpacking an empty DID Document. * Fix dust outputs when reclaiming Alias Output amount * Update iota.js to latest version * Start iota.rs Node.js client intergration * Use unofficia iota-client Wasm bindings * Fix formatting * Add StardustDocument::unpack_from_block, simplify TypeScript * Apply suggestions from code review * Wait for faucet in Wasm Stardust examples * Change TypeScript `import` to `import type` * Improve Wasm TypeScript build (#976) * Remove sleep in Wasm deactivate example * Revert change to txm README.md example Co-authored-by: Eike Haß --- bindings/wasm/.gitignore | 3 +- bindings/wasm/Cargo.toml | 3 +- bindings/wasm/README.md | 33 +- bindings/wasm/build/node.js | 6 +- bindings/wasm/build/utils/generatePackage.js | 6 +- bindings/wasm/build/web.js | 16 +- bindings/wasm/docs/api-reference.md | 210 +- bindings/wasm/examples-stardust/README.md | 45 + .../examples-stardust/src/ex0_create_did.ts | 148 + .../examples-stardust/src/ex1_update_did.ts | 41 + .../examples-stardust/src/ex2_resolve_did.ts | 21 + .../src/ex3_deactivate_did.ts | 49 + .../examples-stardust/src/ex4_delete_did.ts | 28 + bindings/wasm/examples-stardust/src/main.ts | 36 + .../src/tests/ex0_create_did.ts | 8 + .../src/tests/ex1_update_did.ts | 8 + .../src/tests/ex2_resolve_did.ts | 8 + .../src/tests/ex3_deactivate_did.ts | 8 + .../src/tests/ex4_delete_did.ts | 8 + bindings/wasm/lib/index.ts | 6 + bindings/wasm/lib/stardust_identity_client.ts | 162 + bindings/wasm/lib/tsconfig.json | 12 + bindings/wasm/lib/tsconfig.web.json | 13 + bindings/wasm/package-lock.json | 5897 ++++++----------- bindings/wasm/package.json | 25 +- bindings/wasm/rustfmt.toml | 8 - bindings/wasm/src/error.rs | 24 + bindings/wasm/src/stardust/identity_client.rs | 114 + .../wasm/src/stardust/identity_client_ext.rs | 173 + bindings/wasm/src/stardust/mod.rs | 2 + .../wasm/src/stardust/stardust_document.rs | 57 +- .../stardust/stardust_document_metadata.rs | 6 + bindings/wasm/tests/stardust.ts | 8 + bindings/wasm/tsconfig.json | 19 +- bindings/wasm/tsconfig.node.json | 8 + identity_stardust/Cargo.toml | 10 +- identity_stardust/examples/ex0_create_did.rs | 77 +- identity_stardust/examples/ex1_update_did.rs | 32 +- identity_stardust/examples/ex2_resolve_did.rs | 22 +- .../examples/ex3_deactivate_did.rs | 65 +- identity_stardust/examples/ex4_delete_did.rs | 19 +- identity_stardust/src/client/client_ext.rs | 354 - .../src/client/identity_client.rs | 200 + identity_stardust/src/client/iota_client.rs | 153 + identity_stardust/src/client/mod.rs | 10 +- .../src/document/stardust_document.rs | 85 +- identity_stardust/src/error.rs | 11 +- identity_stardust/src/lib.rs | 8 +- .../src/state_metadata/document.rs | 30 +- 49 files changed, 3981 insertions(+), 4314 deletions(-) create mode 100644 bindings/wasm/examples-stardust/README.md create mode 100644 bindings/wasm/examples-stardust/src/ex0_create_did.ts create mode 100644 bindings/wasm/examples-stardust/src/ex1_update_did.ts create mode 100644 bindings/wasm/examples-stardust/src/ex2_resolve_did.ts create mode 100644 bindings/wasm/examples-stardust/src/ex3_deactivate_did.ts create mode 100644 bindings/wasm/examples-stardust/src/ex4_delete_did.ts create mode 100644 bindings/wasm/examples-stardust/src/main.ts create mode 100644 bindings/wasm/examples-stardust/src/tests/ex0_create_did.ts create mode 100644 bindings/wasm/examples-stardust/src/tests/ex1_update_did.ts create mode 100644 bindings/wasm/examples-stardust/src/tests/ex2_resolve_did.ts create mode 100644 bindings/wasm/examples-stardust/src/tests/ex3_deactivate_did.ts create mode 100644 bindings/wasm/examples-stardust/src/tests/ex4_delete_did.ts create mode 100644 bindings/wasm/lib/index.ts create mode 100644 bindings/wasm/lib/stardust_identity_client.ts create mode 100644 bindings/wasm/lib/tsconfig.json create mode 100644 bindings/wasm/lib/tsconfig.web.json delete mode 120000 bindings/wasm/rustfmt.toml create mode 100644 bindings/wasm/src/stardust/identity_client.rs create mode 100644 bindings/wasm/src/stardust/identity_client_ext.rs create mode 100644 bindings/wasm/tsconfig.node.json delete mode 100644 identity_stardust/src/client/client_ext.rs create mode 100644 identity_stardust/src/client/identity_client.rs create mode 100644 identity_stardust/src/client/iota_client.rs diff --git a/bindings/wasm/.gitignore b/bindings/wasm/.gitignore index 9a40d48505..ad9f336890 100644 --- a/bindings/wasm/.gitignore +++ b/bindings/wasm/.gitignore @@ -6,11 +6,10 @@ Cargo.lock node_modules/ # Build artifacts -wasm-web/ -wasm-node/ web/ node/ pkg/ +**/dist/ # cypress cypress/screenshots/ diff --git a/bindings/wasm/Cargo.toml b/bindings/wasm/Cargo.toml index 1836dddda9..75b4f3831b 100644 --- a/bindings/wasm/Cargo.toml +++ b/bindings/wasm/Cargo.toml @@ -17,6 +17,7 @@ crate-type = ["cdylib", "rlib"] [dependencies] async-trait = { version = "0.1", default-features = false } +bee-block = { version = "1.0.0-beta.5", default-features = false, features = ["dto"] } console_error_panic_hook = { version = "0.1" } futures = { version = "0.3" } js-sys = { version = "0.3" } @@ -37,7 +38,7 @@ features = ["account", "storage-test-suite", "unstable-encryption", "revocation- version = "=0.6.0" path = "../../identity_stardust" default-features = false -features = ["revocation-bitmap"] +features = ["client", "revocation-bitmap"] [dev-dependencies] wasm-bindgen-test = { version = "0.3" } diff --git a/bindings/wasm/README.md b/bindings/wasm/README.md index 74dab2d016..7052baf5c0 100644 --- a/bindings/wasm/README.md +++ b/bindings/wasm/README.md @@ -9,19 +9,19 @@ ## Install the library: -Latest Release: this version matches the main branch of this repository, is stable and will have changelogs. +Latest Release: this version matches the `main` branch of this repository, is stable and will have changelogs. ```bash npm install @iota/identity-wasm ``` -Development Release: this version matches the dev branch of this repository, may see frequent breaking changes and has the latest code changes. +Development Release: this version usually matches the latest code changes from the `dev` branch and may see frequent breaking changes. ```bash npm install @iota/identity-wasm@dev ``` ## Build -Alternatively, you can build the bindings if you have Rust installed. If not, refer to [rustup.rs](https://rustup.rs) for the installation. +Alternatively, you can build the bindings yourself if you have Rust installed. If not, refer to [rustup.rs](https://rustup.rs) for the installation. Install [`wasm-bindgen-cli`](https://github.com/rustwasm/wasm-bindgen). A manual installation is required because we use the [Weak References](https://rustwasm.github.io/wasm-bindgen/reference/weak-references.html) feature, which [`wasm-pack` does not expose](https://github.com/rustwasm/wasm-pack/issues/930). @@ -48,7 +48,7 @@ npm run build:web ## Minimum Requirements -The minimum supported version for node is: `v16.0.0` +The minimum supported version for node is: `v16` ## NodeJS Usage ```javascript -const identity = require('@iota/identity-wasm/node') +const identity = require('@iota/identity-wasm/node'); async function main() { @@ -84,7 +84,7 @@ async function main() { console.log(`Explorer Url:`, identity.ExplorerUrl.mainnet().resolverUrl(did)); } -main() +main(); ``` ## Web Setup @@ -107,7 +107,13 @@ import copy from 'rollup-plugin-copy' // Add the copy plugin to the `plugins` array of your rollup config: copy({ - targets: [{ + targets: [ + { + src: 'node_modules/@cycraig/iota-client-wasm/web/wasm/client_wasm_bg.wasm', + dest: 'public', + rename: 'client_wasm_bg.wasm' + }, + { src: 'node_modules/@iota/identity-wasm/web/identity_wasm_bg.wasm', dest: 'public', rename: 'identity_wasm_bg.wasm' @@ -131,6 +137,10 @@ const CopyWebPlugin= require('copy-webpack-plugin'); new CopyWebPlugin({ patterns: [ + { + from: 'node_modules/@cycraig/iota-client-wasm/web/wasm/client_wasm_bg.wasm', + to: 'client_wasm_bg.wasm' + }, { from: 'node_modules/@iota/identity-wasm/web/identity_wasm_bg.wasm', to: 'identity_wasm_bg.wasm' @@ -142,9 +152,10 @@ new CopyWebPlugin({ ### Web Usage ```js +import * as client from "@cycraig/iota-client-wasm/web"; import * as identity from "@iota/identity-wasm/web"; -identity.init().then(() => { +client.init().then(() => identity.init()).then(() => { // The creation step generates a keypair, builds an identity // and publishes it to the IOTA mainnet. @@ -165,8 +176,8 @@ identity.init().then(() => { // or (async () => { - - await identity.init() + await client.init(); + await identity.init(); // The creation step generates a keypair, builds an identity // and publishes it to the IOTA mainnet. @@ -188,7 +199,7 @@ identity.init().then(() => { await identity.init("./static/identity_wasm_bg.wasm"); ``` -`identity.init().then()` or `await identity.init()` is required to load the wasm file (from the server if not available, because of that it will only be slow for the first time) +Calling `identity.init().then()` or `await identity.init()` is required to load the Wasm file from the server if not available, because of that it will only be slow for the first time. ## Examples in the Wild diff --git a/bindings/wasm/build/node.js b/bindings/wasm/build/node.js index 9f27c43db5..c23f8eeaaf 100644 --- a/bindings/wasm/build/node.js +++ b/bindings/wasm/build/node.js @@ -26,9 +26,9 @@ fs.writeFileSync( changedFileNode ); +// Generate `package.json`. const newPackage = generatePackage({ - main: 'identity_wasm.js', - types: 'identity_wasm.d.ts', + main: 'index.js', + types: 'index.d.ts', }); - fs.writeFileSync(path.join(RELEASE_FOLDER, 'package.json'), JSON.stringify(newPackage, null, 2)); diff --git a/bindings/wasm/build/utils/generatePackage.js b/bindings/wasm/build/utils/generatePackage.js index 85cf5c3728..15609a2963 100644 --- a/bindings/wasm/build/utils/generatePackage.js +++ b/bindings/wasm/build/utils/generatePackage.js @@ -7,10 +7,10 @@ module.exports = (options) => { description: rootPackage.description, version: rootPackage.version, license: rootPackage.license, + homepage: rootPackage.homepage, repository: rootPackage.repository, - main: options.main, module: options.module, - homepage: rootPackage.homepage, + main: options.main, types: options.types, } @@ -19,4 +19,4 @@ module.exports = (options) => { return newPackage; -} \ No newline at end of file +} diff --git a/bindings/wasm/build/web.js b/bindings/wasm/build/web.js index 87b226cb5f..700f45b945 100644 --- a/bindings/wasm/build/web.js +++ b/bindings/wasm/build/web.js @@ -1,6 +1,7 @@ -const path = require('path') -const fs = require('fs') -const { lintAll } = require('./lints') +const path = require('path'); +const fs = require('fs'); +const fse = require('fs-extra'); +const { lintAll } = require('./lints'); const generatePackage = require('./utils/generatePackage'); const RELEASE_FOLDER = path.join(__dirname, '../web/'); @@ -47,10 +48,9 @@ fs.writeFileSync( entryFilePathTs, changedFileTs ); - +// Generate `package.json`. const newPackage = generatePackage({ - module: 'identity_wasm.js', - types: 'identity_wasm.d.ts', + module: 'index.js', + types: 'index.d.ts', }); - -fs.writeFileSync(path.join(RELEASE_FOLDER, 'package.json'), JSON.stringify(newPackage, null, 2)); \ No newline at end of file +fs.writeFileSync(path.join(RELEASE_FOLDER, 'package.json'), JSON.stringify(newPackage, null, 2)); diff --git a/bindings/wasm/docs/api-reference.md b/bindings/wasm/docs/api-reference.md index af8d157509..069c16bc0d 100644 --- a/bindings/wasm/docs/api-reference.md +++ b/bindings/wasm/docs/api-reference.md @@ -146,6 +146,10 @@ with a DID subject.

StardustDocumentMetadata

Additional attributes related to an IOTA DID Document.

+
StardustIdentityClientExt
+

An extension interface that provides helper functions for publication +and resolution of DID documents in Alias Outputs.

+
StardustService

A Service adhering to the IOTA UTXO DID method specification.

@@ -175,8 +179,6 @@ See IVerifierOptions.

## Members
-
DIDMessageEncoding
-
StatusCheck

Controls validation behaviour when checking whether or not a credential has been revoked by its credentialStatus.

@@ -219,15 +221,17 @@ This variant is the default used if no other variant is specified when construct
FirstError

Return after the first error occurs.

-
MethodRelationship
-
-
StateMetadataEncoding
-
DIDType

Supported types representing a DID that can be generated by the storage interface.

KeyType
+
MethodRelationship
+
+
DIDMessageEncoding
+
+
StateMetadataEncoding
+
## Functions @@ -249,10 +253,10 @@ publishing to the Tangle. **Kind**: global class * [Account](#Account) - * [.createService(options)](#Account+createService) ⇒ Promise.<void> * [.attachMethodRelationships(options)](#Account+attachMethodRelationships) ⇒ Promise.<void> * [.createMethod(options)](#Account+createMethod) ⇒ Promise.<void> * [.detachMethodRelationships(options)](#Account+detachMethodRelationships) ⇒ Promise.<void> + * [.createService(options)](#Account+createService) ⇒ Promise.<void> * [.did()](#Account+did) ⇒ [IotaDID](#IotaDID) * [.autopublish()](#Account+autopublish) ⇒ boolean * [.autosave()](#Account+autosave) ⇒ [AutoSave](#AutoSave) @@ -275,17 +279,6 @@ publishing to the Tangle. * [.setAlsoKnownAs(options)](#Account+setAlsoKnownAs) ⇒ Promise.<void> * [.setController(options)](#Account+setController) ⇒ Promise.<void> - - -### account.createService(options) ⇒ Promise.<void> -Adds a new Service to the DID Document. - -**Kind**: instance method of [Account](#Account) - -| Param | Type | -| --- | --- | -| options | CreateServiceOptions | - ### account.attachMethodRelationships(options) ⇒ Promise.<void> @@ -322,6 +315,17 @@ Detaches the given relationship from the given method, if the method exists. | --- | --- | | options | DetachMethodRelationshipOptions | + + +### account.createService(options) ⇒ Promise.<void> +Adds a new Service to the DID Document. + +**Kind**: instance method of [Account](#Account) + +| Param | Type | +| --- | --- | +| options | CreateServiceOptions | + ### account.did() ⇒ [IotaDID](#IotaDID) @@ -5113,6 +5117,8 @@ Deserializes an instance from a JSON object. * [.setMetadataCreated(timestamp)](#StardustDocument+setMetadataCreated) * [.metadataUpdated()](#StardustDocument+metadataUpdated) ⇒ [Timestamp](#Timestamp) \| undefined * [.setMetadataUpdated(timestamp)](#StardustDocument+setMetadataUpdated) + * [.metadataDeactivated()](#StardustDocument+metadataDeactivated) ⇒ boolean \| undefined + * [.setMetadataDeactivated(deactivated)](#StardustDocument+setMetadataDeactivated) * [.setMetadataPropertyUnchecked(key, value)](#StardustDocument+setMetadataPropertyUnchecked) * [.revokeCredentials(serviceQuery, indices)](#StardustDocument+revokeCredentials) * [.unrevokeCredentials(serviceQuery, indices)](#StardustDocument+unrevokeCredentials) @@ -5120,7 +5126,8 @@ Deserializes an instance from a JSON object. * [.clone()](#StardustDocument+clone) ⇒ [StardustDocument](#StardustDocument) * _static_ * [.newWithId(id)](#StardustDocument.newWithId) ⇒ [StardustDocument](#StardustDocument) - * [.unpack(did, stateMetadata)](#StardustDocument.unpack) ⇒ [StardustDocument](#StardustDocument) + * [.unpack(did, stateMetadata, allowEmpty)](#StardustDocument.unpack) ⇒ [StardustDocument](#StardustDocument) + * [.unpackFromBlock(network, block)](#StardustDocument.unpackFromBlock) ⇒ [Array.<StardustDocument>](#StardustDocument) * [.fromJSON(json)](#StardustDocument.fromJSON) ⇒ [StardustDocument](#StardustDocument) @@ -5422,6 +5429,23 @@ Sets the timestamp of the last DID document update. | --- | --- | | timestamp | [Timestamp](#Timestamp) \| undefined | + + +### stardustDocument.metadataDeactivated() ⇒ boolean \| undefined +Returns a copy of the deactivated status of the DID document. + +**Kind**: instance method of [StardustDocument](#StardustDocument) + + +### stardustDocument.setMetadataDeactivated(deactivated) +Sets the deactivated status of the DID document. + +**Kind**: instance method of [StardustDocument](#StardustDocument) + +| Param | Type | +| --- | --- | +| deactivated | boolean \| undefined | + ### stardustDocument.setMetadataPropertyUnchecked(key, value) @@ -5486,9 +5510,12 @@ Constructs an empty DID Document with the given identifier. -### StardustDocument.unpack(did, stateMetadata) ⇒ [StardustDocument](#StardustDocument) +### StardustDocument.unpack(did, stateMetadata, allowEmpty) ⇒ [StardustDocument](#StardustDocument) Deserializes the document from the state metadata bytes of an Alias Output. +If `allowEmpty` is true, this will return an empty DID document marked as `deactivated` +if `stateMetadata` is empty. + NOTE: `did` is required since it is omitted from the serialized DID Document and cannot be inferred from the state metadata. It also indicates the network, which is not encoded in the `AliasId` alone. @@ -5499,6 +5526,22 @@ encoded in the `AliasId` alone. | --- | --- | | did | [StardustDID](#StardustDID) | | stateMetadata | Uint8Array | +| allowEmpty | boolean | + + + +### StardustDocument.unpackFromBlock(network, block) ⇒ [Array.<StardustDocument>](#StardustDocument) +Returns all DID documents of the Alias Outputs contained in the block's transaction payload +outputs, if any. + +Errors if any Alias Output does not contain a valid or empty DID Document. + +**Kind**: static method of [StardustDocument](#StardustDocument) + +| Param | Type | +| --- | --- | +| network | string | +| block | IBlock | @@ -5522,6 +5565,7 @@ Additional attributes related to an IOTA DID Document. * _instance_ * [.created()](#StardustDocumentMetadata+created) ⇒ [Timestamp](#Timestamp) \| undefined * [.updated()](#StardustDocumentMetadata+updated) ⇒ [Timestamp](#Timestamp) \| undefined + * [.deactivated()](#StardustDocumentMetadata+deactivated) ⇒ boolean \| undefined * [.properties()](#StardustDocumentMetadata+properties) ⇒ Map.<string, any> * [.toJSON()](#StardustDocumentMetadata+toJSON) ⇒ any * [.clone()](#StardustDocumentMetadata+clone) ⇒ [StardustDocumentMetadata](#StardustDocumentMetadata) @@ -5539,6 +5583,12 @@ Returns a copy of the timestamp of when the DID document was created. ### stardustDocumentMetadata.updated() ⇒ [Timestamp](#Timestamp) \| undefined Returns a copy of the timestamp of the last DID document update. +**Kind**: instance method of [StardustDocumentMetadata](#StardustDocumentMetadata) + + +### stardustDocumentMetadata.deactivated() ⇒ boolean \| undefined +Returns a copy of the deactivated status of the DID document. + **Kind**: instance method of [StardustDocumentMetadata](#StardustDocumentMetadata) @@ -5569,6 +5619,102 @@ Deserializes an instance from a JSON object. | --- | --- | | json | any | + + +## StardustIdentityClientExt +An extension interface that provides helper functions for publication +and resolution of DID documents in Alias Outputs. + +**Kind**: global class + +* [StardustIdentityClientExt](#StardustIdentityClientExt) + * [.newDidOutput(client, address, document, rentStructure)](#StardustIdentityClientExt.newDidOutput) ⇒ Promise.<IAliasOutput> + * [.updateDidOutput(client, document)](#StardustIdentityClientExt.updateDidOutput) ⇒ Promise.<IAliasOutput> + * [.deactivateDidOutput(client, did)](#StardustIdentityClientExt.deactivateDidOutput) ⇒ Promise.<IAliasOutput> + * [.resolveDid(client, did)](#StardustIdentityClientExt.resolveDid) ⇒ [Promise.<StardustDocument>](#StardustDocument) + * [.resolveDidOutput(client, did)](#StardustIdentityClientExt.resolveDidOutput) ⇒ Promise.<IAliasOutput> + + + +### StardustIdentityClientExt.newDidOutput(client, address, document, rentStructure) ⇒ Promise.<IAliasOutput> +Create a DID with a new Alias Output containing the given `document`. + +The `address` will be set as the state controller and governor unlock conditions. +The minimum required token deposit amount will be set according to the given +`rent_structure`, which will be fetched from the node if not provided. +The returned Alias Output can be further customised before publication, if desired. + +NOTE: this does *not* publish the Alias Output. + +**Kind**: static method of [StardustIdentityClientExt](#StardustIdentityClientExt) + +| Param | Type | +| --- | --- | +| client | IStardustIdentityClient | +| address | AddressTypes | +| document | [StardustDocument](#StardustDocument) | +| rentStructure | IRent \| undefined | + + + +### StardustIdentityClientExt.updateDidOutput(client, document) ⇒ Promise.<IAliasOutput> +Fetches the associated Alias Output and updates it with `document` in its state metadata. +The storage deposit on the output is left unchanged. If the size of the document increased, +the amount should be increased manually. + +NOTE: this does *not* publish the updated Alias Output. + +**Kind**: static method of [StardustIdentityClientExt](#StardustIdentityClientExt) + +| Param | Type | +| --- | --- | +| client | IStardustIdentityClient | +| document | [StardustDocument](#StardustDocument) | + + + +### StardustIdentityClientExt.deactivateDidOutput(client, did) ⇒ Promise.<IAliasOutput> +Removes the DID document from the state metadata of its Alias Output, +effectively deactivating it. The storage deposit on the output is left unchanged, +and should be reallocated manually. + +Deactivating does not destroy the output. Hence, it can be re-activated by publishing +an update containing a DID document. + +NOTE: this does *not* publish the updated Alias Output. + +**Kind**: static method of [StardustIdentityClientExt](#StardustIdentityClientExt) + +| Param | Type | +| --- | --- | +| client | IStardustIdentityClient | +| did | [StardustDID](#StardustDID) | + + + +### StardustIdentityClientExt.resolveDid(client, did) ⇒ [Promise.<StardustDocument>](#StardustDocument) +Resolve a [StardustDocument](#StardustDocument). Returns an empty, deactivated document if the state metadata +of the Alias Output is empty. + +**Kind**: static method of [StardustIdentityClientExt](#StardustIdentityClientExt) + +| Param | Type | +| --- | --- | +| client | IStardustIdentityClient | +| did | [StardustDID](#StardustDID) | + + + +### StardustIdentityClientExt.resolveDidOutput(client, did) ⇒ Promise.<IAliasOutput> +Fetches the `IAliasOutput` associated with the given DID. + +**Kind**: static method of [StardustIdentityClientExt](#StardustIdentityClientExt) + +| Param | Type | +| --- | --- | +| client | IStardustIdentityClient | +| did | [StardustDID](#StardustDID) | + ## StardustService @@ -6133,10 +6279,6 @@ This is possible because Ed25519 is birationally equivalent to Curve25519 used b | --- | --- | | publicKey | Uint8Array | - - -## DIDMessageEncoding -**Kind**: global variable ## StatusCheck @@ -6214,14 +6356,6 @@ Return all errors that occur during validation. ## FirstError Return after the first error occurs. -**Kind**: global variable - - -## MethodRelationship -**Kind**: global variable - - -## StateMetadataEncoding **Kind**: global variable @@ -6233,6 +6367,18 @@ Supported types representing a DID that can be generated by the storage interfac ## KeyType **Kind**: global variable + + +## MethodRelationship +**Kind**: global variable + + +## DIDMessageEncoding +**Kind**: global variable + + +## StateMetadataEncoding +**Kind**: global variable ## start() diff --git a/bindings/wasm/examples-stardust/README.md b/bindings/wasm/examples-stardust/README.md new file mode 100644 index 0000000000..ecce863248 --- /dev/null +++ b/bindings/wasm/examples-stardust/README.md @@ -0,0 +1,45 @@ +![banner](./../../../.meta/identity_banner.png) + +## IOTA Identity UTXO Examples + +The following code examples demonstrate how to use the IOTA Identity Wasm bindings in JavaScript/TypeScript. + +The examples are written in TypeScript and can be run with Node.js. + +### Node.js + +Install the dependencies: + +```bash +npm install +``` + +Build the bindings: + +```bash +npm run build +``` + +Then, run an example using: + +```bash +npm run example:stardust -- +``` + +For instance, to run the `ex0_create_did` example execute: + +```bash +npm run example:stardust -- ex0_create_did +``` + +| # | Name | Details | +|-----|-------------------------------------------------|------------------------------------------------------------------| +| 0 | [ex0_create_did](src/ex0_create_did.ts) | Create a DID Document and publish it in a new Alias Output. | +| 1 | [ex1_update_did](src/ex1_update_did.ts) | Update a DID document in an existing Alias Output. | +| 2 | [ex2_resolve_did](src/ex2_resolve_did.ts) | Resolve an existing DID in an Alias Output. | +| 3 | [ex3_deactivate_did](src/ex3_deactivate_did.ts) | Deactivate a DID in an Alias Output. | +| 4 | [ex4_delete_did](src/ex4_delete_did.ts) | Delete a DID in an Alias Output, reclaiming the storage deposit. | + +## Browser + +While the examples should work in a browser environment, we do not provide browser examples yet. diff --git a/bindings/wasm/examples-stardust/src/ex0_create_did.ts b/bindings/wasm/examples-stardust/src/ex0_create_did.ts new file mode 100644 index 0000000000..ca690a9f05 --- /dev/null +++ b/bindings/wasm/examples-stardust/src/ex0_create_did.ts @@ -0,0 +1,148 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { + KeyPair, + KeyType, + MethodScope, + StardustDID, + StardustDocument, + StardustIdentityClient, + StardustVerificationMethod +} from '../../node'; +import {Bech32Helper, IAliasOutput} from '@iota/iota.js'; +import {Bip39} from "@iota/crypto.js"; +import fetch from "node-fetch"; +import {Client, MnemonicSecretManager, SecretManager} from "@cycraig/iota-client-wasm/node"; + +const EXPLORER = "https://explorer.alphanet.iotaledger.net/alphanet"; +const API_ENDPOINT = "https://api.alphanet.iotaledger.net/"; +const FAUCET = "https://faucet.alphanet.iotaledger.net/api/enqueue"; + +/** Demonstrate how to create a DID Document and publish it in a new Alias Output. */ +export async function createIdentity(): Promise<{ + didClient: StardustIdentityClient, + secretManager: SecretManager, + walletAddressBech32: string, + did: StardustDID +}> { + const client = new Client({ + primaryNode: API_ENDPOINT, + localPow: true, + }); + const didClient = new StardustIdentityClient(client); + + // Get the Bech32 human-readable part (HRP) of the network. + const networkHrp: string = await didClient.getNetworkHrp(); + + // Generate a random mnemonic for our wallet. + const secretManager: MnemonicSecretManager = { + Mnemonic: Bip39.randomMnemonic() + }; + const walletAddressBech32 = (await client.generateAddresses(secretManager, { + accountIndex: 0, + range: { + start: 0, + end: 1, + }, + }))[0]; + console.log("Wallet address Bech32:", walletAddressBech32); + + // Request funds for the wallet, if needed - only works on development networks. + await ensureAddressHasFunds(client, walletAddressBech32); + + // Create a new DID document with a placeholder DID. + // The DID will be derived from the Alias Id of the Alias Output after publishing. + const document = new StardustDocument(networkHrp); + + // Insert a new Ed25519 verification method in the DID document. + let keypair = new KeyPair(KeyType.Ed25519); + let method = new StardustVerificationMethod(document.id(), keypair.type(), keypair.public(), "#key-1"); + document.insertMethod(method, MethodScope.VerificationMethod()); + + // Construct an Alias Output containing the DID document, with the wallet address + // set as both the state controller and governor. + const address = Bech32Helper.addressFromBech32(walletAddressBech32, networkHrp); + const aliasOutput: IAliasOutput = await didClient.newDidOutput(address, document); + console.log("Alias Output:", JSON.stringify(aliasOutput, null, 2)); + + // Publish the Alias Output and get the published DID document. + const published = await didClient.publishDidOutput(secretManager, aliasOutput); + console.log("Published DID document:", JSON.stringify(published, null, 2)); + + return { + didClient, secretManager, + walletAddressBech32, + did: published.id() + }; +} + +/** Request funds from the testnet faucet API, if needed, and wait for them to show in the wallet. */ +async function ensureAddressHasFunds(client: Client, addressBech32: string) { + let balance = await getAddressBalance(client, addressBech32); + if (balance > 0) { + return; + } + + await requestFundsFromFaucet(addressBech32); + + for (let i = 0; i < 9; i++) { + // Wait for the funds to reflect. + await new Promise(f => setTimeout(f, 5000)); + + let balance = await getAddressBalance(client, addressBech32); + if (balance > 0) { + break; + } + } +} + +/** Returns the balance of the given Bech32-encoded address. */ +async function getAddressBalance(client: Client, addressBech32: string): Promise { + // TODO: use the `addresses/ed25519/` API to get the balance? + const outputIds = await client.basicOutputIds([ + {address: addressBech32}, + {hasExpiration: false}, + {hasTimelock: false}, + {hasStorageDepositReturn: false} + ]); + const outputs = await client.getOutputs(outputIds); + + let totalAmount = 0; + for (const output of outputs) { + totalAmount += Number(output.output.amount); + } + + return totalAmount; +} + +/** Request tokens from the testnet faucet API. */ +async function requestFundsFromFaucet(addressBech32: string) { + const requestObj = JSON.stringify({address: addressBech32}); + let errorMessage, data; + try { + const response = await fetch(FAUCET, { + method: "POST", + headers: { + Accept: "application/json", + "Content-Type": "application/json", + }, + body: requestObj, + }); + if (response.status === 202) { + errorMessage = "OK"; + } else if (response.status === 429) { + errorMessage = "too many requests, please try again later."; + } else { + data = await response.json(); + // @ts-ignore + errorMessage = data.error.message; + } + } catch (error) { + errorMessage = error; + } + + if (errorMessage != "OK") { + throw new Error(`failed to get funds from faucet: ${errorMessage}`); + } +} diff --git a/bindings/wasm/examples-stardust/src/ex1_update_did.ts b/bindings/wasm/examples-stardust/src/ex1_update_did.ts new file mode 100644 index 0000000000..8fea877b66 --- /dev/null +++ b/bindings/wasm/examples-stardust/src/ex1_update_did.ts @@ -0,0 +1,41 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import {MethodRelationship, StardustDocument, StardustService, Timestamp} from '../../node'; +import {IAliasOutput, IRent, TransactionHelper} from '@iota/iota.js'; + +import {createIdentity} from "./ex0_create_did"; + +/** Demonstrates how to update a DID document in an existing Alias Output. */ +export async function updateIdentity() { + // Creates a new wallet and identity (see "ex0_create_did" example). + const {didClient, secretManager, did} = await createIdentity(); + + // Resolve the latest state of the document. + // Technically this is equivalent to the document above. + const document: StardustDocument = await didClient.resolveDid(did); + + // Attach a new method relationship to the existing method. + document.attachMethodRelationship(did.join("#key-1"), MethodRelationship.Authentication); + + // Add a new Service. + const service: StardustService = new StardustService({ + id: did.join("#linked-domain"), + type: "LinkedDomains", + serviceEndpoint: "https://iota.org/" + }); + document.insertService(service); + document.setMetadataUpdated(Timestamp.nowUTC()); + + // Resolve the latest output and update it with the given document. + const aliasOutput: IAliasOutput = await didClient.updateDidOutput(document); + + // Because the size of the DID document increased, we have to increase the allocated storage deposit. + // This increases the deposit amount to the new minimum. + const rentStructure: IRent = await didClient.getRentStructure(); + aliasOutput.amount = TransactionHelper.getStorageDeposit(aliasOutput, rentStructure).toString(); + + // Publish the output. + const updated: StardustDocument = await didClient.publishDidOutput(secretManager, aliasOutput); + console.log("Updated DID document:", JSON.stringify(updated, null, 2)); +} diff --git a/bindings/wasm/examples-stardust/src/ex2_resolve_did.ts b/bindings/wasm/examples-stardust/src/ex2_resolve_did.ts new file mode 100644 index 0000000000..5f1dd5202c --- /dev/null +++ b/bindings/wasm/examples-stardust/src/ex2_resolve_did.ts @@ -0,0 +1,21 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import type {StardustDocument} from '../../node'; +import type {IAliasOutput} from '@iota/iota.js'; + +import {createIdentity} from "./ex0_create_did"; + +/** Demonstrates how to resolve an existing DID in an Alias Output. */ +export async function resolveIdentity() { + // Creates a new wallet and identity (see "ex0_create_did" example). + const {didClient, did} = await createIdentity(); + + // Resolve the associated Alias Output and extract the DID document from it. + const resolved: StardustDocument = await didClient.resolveDid(did); + console.log("Resolved DID document:", JSON.stringify(resolved, null, 2)); + + // We can also resolve the Alias Output directly. + const aliasOutput: IAliasOutput = await didClient.resolveDidOutput(did); + console.log("The Alias Output holds " + aliasOutput.amount + " tokens"); +} diff --git a/bindings/wasm/examples-stardust/src/ex3_deactivate_did.ts b/bindings/wasm/examples-stardust/src/ex3_deactivate_did.ts new file mode 100644 index 0000000000..1d49a9f684 --- /dev/null +++ b/bindings/wasm/examples-stardust/src/ex3_deactivate_did.ts @@ -0,0 +1,49 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import type {StardustDocument} from '../../node'; +import {IAliasOutput, IRent, TransactionHelper} from '@iota/iota.js'; + +import {createIdentity} from "./ex0_create_did"; + +/** Demonstrates how to deactivate a DID in an Alias Output. */ +export async function deactivateIdentity() { + // Creates a new wallet and identity (see "ex0_create_did" example). + const {didClient, secretManager, did} = await createIdentity(); + + // Resolve the latest state of the DID document, so we can reactivate it later. + let document: StardustDocument = await didClient.resolveDid(did); + + // Deactivate the DID by publishing an empty document. + // This process can be reversed since the Alias Output is not destroyed. + // Deactivation may only be performed by the state controller of the Alias Output. + let deactivatedOutput: IAliasOutput = await didClient.deactivateDidOutput(did); + + // Optional: reduce and reclaim the storage deposit, sending the tokens to the state controller. + const rentStructure: IRent = await didClient.getRentStructure(); + deactivatedOutput.amount = TransactionHelper.getStorageDeposit(deactivatedOutput, rentStructure).toString(); + + // Publish the deactivated DID document. + await didClient.publishDidOutput(secretManager, deactivatedOutput); + + // Resolving a deactivated DID returns an empty DID document + // with its `deactivated` metadata field set to `true`. + let deactivated: StardustDocument = await didClient.resolveDid(did); + console.log("Deactivated DID document:", JSON.stringify(deactivated, null, 2)); + if (deactivated.metadataDeactivated() !== true) { + throw new Error("Failed to deactivate DID document"); + } + + // Re-activate the DID by publishing a valid DID document. + let reactivatedOutput: IAliasOutput = await didClient.updateDidOutput(document); + + // Increase the storage deposit to the minimum again, if it was reclaimed during deactivation. + reactivatedOutput.amount = TransactionHelper.getStorageDeposit(reactivatedOutput, rentStructure).toString(); + await didClient.publishDidOutput(secretManager, reactivatedOutput); + + // Resolve the reactivated DID document. + let reactivated: StardustDocument = await didClient.resolveDid(did); + if (reactivated.metadataDeactivated() === true) { + throw new Error("Failed to reactivate DID document"); + } +} diff --git a/bindings/wasm/examples-stardust/src/ex4_delete_did.ts b/bindings/wasm/examples-stardust/src/ex4_delete_did.ts new file mode 100644 index 0000000000..976bc5977d --- /dev/null +++ b/bindings/wasm/examples-stardust/src/ex4_delete_did.ts @@ -0,0 +1,28 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import {createIdentity} from "./ex0_create_did"; +import {Bech32Helper} from "@iota/iota.js"; + +/** Demonstrates how to delete a DID in an Alias Output, reclaiming the storage deposit. */ +export async function deleteIdentity() { + // Creates a new wallet and identity (see "ex0_create_did" example). + const {didClient, secretManager, walletAddressBech32, did} = await createIdentity(); + + // Deletes the Alias Output and its contained DID Document, rendering the DID permanently destroyed. + // This operation is *not* reversible. + // Deletion can only be done by the governor of the Alias Output. + const destinationAddress = Bech32Helper.addressFromBech32(walletAddressBech32, await didClient.getNetworkHrp()); + await didClient.deleteDidOutput(secretManager, destinationAddress, did); + + // Attempting to resolve a deleted DID results in a `NotFound` error. + let deleted = false; + try { + await didClient.resolveDid(did); + } catch (err) { + deleted = true; + } + if (!deleted) { + throw new Error("failed to delete DID"); + } +} diff --git a/bindings/wasm/examples-stardust/src/main.ts b/bindings/wasm/examples-stardust/src/main.ts new file mode 100644 index 0000000000..7c923f40f8 --- /dev/null +++ b/bindings/wasm/examples-stardust/src/main.ts @@ -0,0 +1,36 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import {createIdentity} from "./ex0_create_did"; +import {updateIdentity} from "./ex1_update_did"; +import {resolveIdentity} from "./ex2_resolve_did"; +import {deactivateIdentity} from "./ex3_deactivate_did"; +import {deleteIdentity} from "./ex4_delete_did"; + +async function main() { + // Extract example name. + if (process.argv.length != 3) { + throw "Please specify an example name, e.g. 'ex0_create_did'"; + } + const argument = process.argv[2].toLowerCase(); + + switch (argument) { + case "ex0_create_did": + return await createIdentity(); + case "ex1_update_did": + return await updateIdentity(); + case "ex2_resolve_did": + return await resolveIdentity(); + case "ex3_deactivate_did": + return await deactivateIdentity(); + case "ex4_delete_did": + return await deleteIdentity(); + default: + throw "Unknown example name: '" + argument + "'"; + } +} + +main() + .catch((error) => { + console.log("Example error:", error); + }); diff --git a/bindings/wasm/examples-stardust/src/tests/ex0_create_did.ts b/bindings/wasm/examples-stardust/src/tests/ex0_create_did.ts new file mode 100644 index 0000000000..65dbfd86b4 --- /dev/null +++ b/bindings/wasm/examples-stardust/src/tests/ex0_create_did.ts @@ -0,0 +1,8 @@ +import {createIdentity} from "../ex0_create_did"; + +// Only verifies that no uncaught exceptions are thrown, including syntax errors etc. +describe("Test node examples", function () { + it("Create Identity", async () => { + await createIdentity(); + }); +}) diff --git a/bindings/wasm/examples-stardust/src/tests/ex1_update_did.ts b/bindings/wasm/examples-stardust/src/tests/ex1_update_did.ts new file mode 100644 index 0000000000..2051c94764 --- /dev/null +++ b/bindings/wasm/examples-stardust/src/tests/ex1_update_did.ts @@ -0,0 +1,8 @@ +import {updateIdentity} from "../ex1_update_did"; + +// Only verifies that no uncaught exceptions are thrown, including syntax errors etc. +describe("Test node examples", function () { + it("Update Identity", async () => { + await updateIdentity(); + }); +}) diff --git a/bindings/wasm/examples-stardust/src/tests/ex2_resolve_did.ts b/bindings/wasm/examples-stardust/src/tests/ex2_resolve_did.ts new file mode 100644 index 0000000000..fe9b4c6c18 --- /dev/null +++ b/bindings/wasm/examples-stardust/src/tests/ex2_resolve_did.ts @@ -0,0 +1,8 @@ +import {resolveIdentity} from "../ex2_resolve_did"; + +// Only verifies that no uncaught exceptions are thrown, including syntax errors etc. +describe("Test node examples", function () { + it("Resolve Identity", async () => { + await resolveIdentity(); + }); +}) diff --git a/bindings/wasm/examples-stardust/src/tests/ex3_deactivate_did.ts b/bindings/wasm/examples-stardust/src/tests/ex3_deactivate_did.ts new file mode 100644 index 0000000000..512178612d --- /dev/null +++ b/bindings/wasm/examples-stardust/src/tests/ex3_deactivate_did.ts @@ -0,0 +1,8 @@ +import {deleteIdentity} from "../ex4_delete_did"; + +// Only verifies that no uncaught exceptions are thrown, including syntax errors etc. +describe("Test node examples", function () { + it("Delete Identity", async () => { + await deleteIdentity(); + }); +}) diff --git a/bindings/wasm/examples-stardust/src/tests/ex4_delete_did.ts b/bindings/wasm/examples-stardust/src/tests/ex4_delete_did.ts new file mode 100644 index 0000000000..7457409522 --- /dev/null +++ b/bindings/wasm/examples-stardust/src/tests/ex4_delete_did.ts @@ -0,0 +1,8 @@ +import {deactivateIdentity} from "../ex3_deactivate_did"; + +// Only verifies that no uncaught exceptions are thrown, including syntax errors etc. +describe("Test node examples", function () { + it("Deactivate Identity", async () => { + await deactivateIdentity(); + }); +}) diff --git a/bindings/wasm/lib/index.ts b/bindings/wasm/lib/index.ts new file mode 100644 index 0000000000..beaf6af755 --- /dev/null +++ b/bindings/wasm/lib/index.ts @@ -0,0 +1,6 @@ +// Copyright 2021-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export * from './stardust_identity_client'; + +export * from '~identity_wasm'; diff --git a/bindings/wasm/lib/stardust_identity_client.ts b/bindings/wasm/lib/stardust_identity_client.ts new file mode 100644 index 0000000000..9b2a046228 --- /dev/null +++ b/bindings/wasm/lib/stardust_identity_client.ts @@ -0,0 +1,162 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import {IStardustIdentityClient, StardustDID, StardustDocument, StardustIdentityClientExt} from '~identity_wasm'; + +import type {Client, INodeInfoWrapper, SecretManager} from '~iota-client-wasm'; +import { + ADDRESS_UNLOCK_CONDITION_TYPE, + AddressTypes, + ALIAS_OUTPUT_TYPE, + IAliasOutput, + IOutputResponse, + IRent, + IUTXOInput, + TransactionHelper +} from '@iota/iota.js'; + +/** Provides operations for IOTA UTXO DID Documents with Alias Outputs. */ +export class StardustIdentityClient implements IStardustIdentityClient { + client: Client; + + constructor(client: Client) { + this.client = client; + } + + async getNetworkHrp() { + return await this.client.getBech32Hrp(); + } + + async getAliasOutput(aliasId: string) { + // Lookup latest OutputId from the indexer plugin. + const outputId = await this.client.aliasOutputId(aliasId); + + // Fetch AliasOutput. + const outputResponse: IOutputResponse = await this.client.getOutput(outputId); + const output = outputResponse.output; + if (output.type != ALIAS_OUTPUT_TYPE) { + throw new Error("AliasId '" + aliasId + "' returned incorrect output type '" + output.type + "'"); + } + // Coerce to tuple instead of an array. + const ret: [string, IAliasOutput] = [outputId, output]; + return ret; + } + + async getRentStructure() { + const info: INodeInfoWrapper = await this.client.getInfo(); + return info.nodeInfo.protocol.rentStructure; + } + + /** Create a DID with a new Alias Output containing the given `document`. + * + * The `address` will be set as the state controller and governor unlock conditions. + * The minimum required token deposit amount will be set according to the given + * `rent_structure`, which will be fetched from the node if not provided. + * The returned Alias Output can be further customised before publication, if desired. + * + * NOTE: this does *not* publish the Alias Output. + */ + async newDidOutput(address: AddressTypes, document: StardustDocument, rentStructure?: IRent): Promise { + return await StardustIdentityClientExt.newDidOutput(this, address, document, rentStructure); + } + + /** Fetches the associated Alias Output and updates it with `document` in its state metadata. + * The storage deposit on the output is left unchanged. If the size of the document increased, + * the amount should be increased manually. + * + * NOTE: this does *not* publish the updated Alias Output. + */ + async updateDidOutput(document: StardustDocument): Promise { + return await StardustIdentityClientExt.updateDidOutput(this, document); + } + + /** Removes the DID document from the state metadata of its Alias Output, + * effectively deactivating it. The storage deposit on the output is left unchanged, + * and should be reallocated manually. + * + * Deactivating does not destroy the output. Hence, it can be re-activated by publishing + * an update containing a DID document. + * + * NOTE: this does *not* publish the updated Alias Output. + */ + async deactivateDidOutput(did: StardustDID): Promise { + return await StardustIdentityClientExt.deactivateDidOutput(this, did); + } + + /** Resolve a {@link StardustDocument}. Returns an empty, deactivated document if the state + * metadata of the Alias Output is empty. + */ + async resolveDid(did: StardustDID): Promise { + return await StardustIdentityClientExt.resolveDid(this, did); + } + + /** Fetches the Alias Output associated with the given DID. */ + async resolveDidOutput(did: StardustDID): Promise { + return await StardustIdentityClientExt.resolveDidOutput(this, did); + } + + /** Publish the given `aliasOutput` with the provided ` `, and returns + * the DID document extracted from the published block. + * + * Note that only the state controller of an Alias Output is allowed to update its state. + * This will attempt to move tokens to or from the state controller address to match + * the storage deposit amount specified on `aliasOutput`. + * + * This method modifies the on-ledger state. + */ + async publishDidOutput(secretManager: SecretManager, aliasOutput: IAliasOutput): Promise { + const networkHrp = await this.getNetworkHrp(); + + // Publish block. + const [blockId, block] = await this.client.buildAndPostBlock(secretManager, { + outputs: [aliasOutput], + }); + await this.client.retryUntilIncluded(blockId); + + // Extract document with computed AliasId. + const documents = StardustDocument.unpackFromBlock(networkHrp, block); + if (documents.length < 1) { + throw new Error("publishDidOutput: no DID document in transaction payload"); + } + return documents[0]; + } + + /** Destroy the Alias Output containing the given `did`, sending its tokens to a new Basic Output + * unlockable by the given address. + * + * Note that only the governor of an Alias Output is allowed to destroy it. + * + * ### WARNING + * + * This destroys the Alias Output and DID document, rendering them permanently unrecoverable. + */ + async deleteDidOutput(secretManager: SecretManager, address: AddressTypes, did: StardustDID) { + const networkHrp = await this.getNetworkHrp(); + if (networkHrp !== did.networkStr()) { + throw new Error("deleteDidOutput: DID network mismatch, client expected `" + networkHrp + "`, DID network is `" + did.networkStr() + "`"); + } + + const aliasId: string = did.tag(); + const [outputId, aliasOutput] = await this.getAliasOutput(aliasId); + const aliasInput: IUTXOInput = TransactionHelper.inputFromOutputId(outputId); + + // Send funds to the address. + const basicOutput = await this.client.buildBasicOutput({ + amount: aliasOutput.amount, + nativeTokens: aliasOutput.nativeTokens, + unlockConditions: [ + { + type: ADDRESS_UNLOCK_CONDITION_TYPE, + address: address + } + ], + }) + + // Publish block. + const [blockId, _block] = await this.client.buildAndPostBlock(secretManager, { + inputs: [aliasInput], + outputs: [basicOutput] + }); + await this.client.retryUntilIncluded(blockId); + } +} diff --git a/bindings/wasm/lib/tsconfig.json b/bindings/wasm/lib/tsconfig.json new file mode 100644 index 0000000000..808233bc3d --- /dev/null +++ b/bindings/wasm/lib/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../tsconfig.node.json", + "compilerOptions": { + "baseUrl": "./", + "paths": { + "~identity_wasm": ["../node/identity_wasm"], + "~iota-client-wasm": ["../node_modules/@cycraig/iota-client-wasm/node"], + }, + "outDir": "../node", + "declarationDir": "../node", + }, +} diff --git a/bindings/wasm/lib/tsconfig.web.json b/bindings/wasm/lib/tsconfig.web.json new file mode 100644 index 0000000000..43e10d9865 --- /dev/null +++ b/bindings/wasm/lib/tsconfig.web.json @@ -0,0 +1,13 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "baseUrl": "./", + "paths": { + "~identity_wasm": ["../web/identity_wasm"], + "~iota-client-wasm": ["../node_modules/@cycraig/iota-client-wasm/web"], + }, + "outDir": "../web", + "declarationDir": "../web", + "module": "ES2020" + } +} diff --git a/bindings/wasm/package-lock.json b/bindings/wasm/package-lock.json index 862294593a..697755f84d 100644 --- a/bindings/wasm/package-lock.json +++ b/bindings/wasm/package-lock.json @@ -9,34 +9,74 @@ "version": "0.6.0", "license": "Apache-2.0", "dependencies": { + "@iota/types": "^1.0.0-beta.11", + "@types/node-fetch": "^2.6.2", "node-fetch": "^2.6.7" }, "devDependencies": { + "@iota/crypto.js": "^1.9.0-stardust.6", "@types/mocha": "^9.1.0", "concurrently": "^7.0.0", "copy-webpack-plugin": "^7.0.0", "cypress": "^9.3.1", "cypress-parallel": "^0.1.8", + "fs-extra": "^10.1.0", "http-server": "^14.1.0", "jsdoc-to-markdown": "^7.1.1", "mocha": "^9.2.0", "ts-mocha": "^9.0.2", - "ts-node": "^10.4.0", + "ts-node": "^10.9.1", + "tsc-alias": "^1.7.0", "txm": "^8.0.1", - "typescript": "^4.6.2", + "typescript": "^4.7.2", "wasm-opt": "^1.3.0", "wasm-pack": "0.10.1", "webpack": "^5.67.0", "webpack-cli": "^4.9.2" }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@cycraig/iota-client-wasm": "^0.5.0-alpha.1", + "@iota/iota.js": "^1.9.0-stardust.25" + } + }, + "../../../iota.rs/bindings/wasm": { + "name": "@cycraig/iota-client-wasm", + "version": "0.5.0-alpha.1", + "extraneous": true, + "license": "Apache-2.0", + "dependencies": { + "@iota/types": "^1.0.0-beta.11", + "node-fetch": "^2.6.7", + "text-encoding": "^0.7.0" + }, + "devDependencies": { + "@types/jest": "^27.5.2", + "@typescript-eslint/eslint-plugin": "^5.31.0", + "@typescript-eslint/parser": "^5.31.0", + "dotenv": "^16.0.1", + "eslint": "^8.20.0", + "eslint-config-prettier": "^8.5.0", + "fs-extra": "^10.1.0", + "jest": "^27.5.1", + "jest-matcher-utils": "^28.1.3", + "prettier": "^2.7.1", + "ts-jest": "^27.1.5", + "ts-node": "^10.9.1", + "typedoc": "^0.23.9", + "typedoc-plugin-markdown": "^3.13.4", + "typescript": "^4.7.4", + "wasm-opt": "^1.3.0" + }, "engines": { "node": ">=16" } }, "node_modules/@babel/parser": { - "version": "7.16.12", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.12.tgz", - "integrity": "sha512-VfaV15po8RiZssrkPweyvbGVSe4x2y+aciFCgn0n0/SJMR22cwofRV1mtnJQYcSB1wUTaA/X1LnA3es66MCO5A==", + "version": "7.18.11", + "integrity": "sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -45,30 +85,42 @@ "node": ">=6.0.0" } }, - "node_modules/@cspotcode/source-map-consumer": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", - "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", + "node_modules/@colors/colors": { + "version": "1.5.0", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", "dev": true, + "optional": true, "engines": { - "node": ">= 12" + "node": ">=0.1.90" } }, "node_modules/@cspotcode/source-map-support": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", - "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", + "version": "0.8.1", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, "dependencies": { - "@cspotcode/source-map-consumer": "0.8.0" + "@jridgewell/trace-mapping": "0.3.9" }, "engines": { "node": ">=12" } }, + "node_modules/@cycraig/iota-client-wasm": { + "version": "0.5.0-alpha.1", + "resolved": "https://registry.npmjs.org/@cycraig/iota-client-wasm/-/iota-client-wasm-0.5.0-alpha.1.tgz", + "integrity": "sha512-5D1l6QkOupmvaI19mROp/QjcqFNOHvjjGo2zJUbGbF7tI/v6J/CyiYpziREqWMUkCzKW3a/gEE9/YRbTRkJ7pg==", + "peer": true, + "dependencies": { + "@iota/types": "^1.0.0-beta.11", + "node-fetch": "^2.6.7", + "text-encoding": "^0.7.0" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/@cypress/request": { "version": "2.88.10", - "resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.10.tgz", "integrity": "sha512-Zp7F+R93N0yZyG34GutyTNr+okam7s/Fzc1+i3kcqOP8vk6OuajuE9qZJ6Rs+10/1JFtXFYMdyarnU1rZuJesg==", "dev": true, "dependencies": { @@ -95,27 +147,21 @@ "node": ">= 6" } }, - "node_modules/@cypress/request/node_modules/qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "node_modules/@cypress/request/node_modules/form-data": { + "version": "2.3.3", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, "engines": { - "node": ">=0.6" - } - }, - "node_modules/@cypress/request/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, - "bin": { - "uuid": "dist/bin/uuid" + "node": ">= 0.12" } }, "node_modules/@cypress/xvfb": { "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz", "integrity": "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==", "dev": true, "dependencies": { @@ -125,31 +171,64 @@ }, "node_modules/@cypress/xvfb/node_modules/debug": { "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "dependencies": { "ms": "^2.1.1" } }, - "node_modules/@cypress/xvfb/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, "node_modules/@discoveryjs/json-ext": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.3.tgz", - "integrity": "sha512-Fxt+AfXgjMoin2maPIYzFZnQjAXjAL0PHscM5pRTtatFqB+vZxAM9tLp2Optnuw3QOQC40jTNeGYFOMvyf7v9g==", + "version": "0.5.7", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", "dev": true, "engines": { "node": ">=10.0.0" } }, + "node_modules/@iota/crypto.js": { + "version": "1.9.0-stardust.6", + "resolved": "https://registry.npmjs.org/@iota/crypto.js/-/crypto.js-1.9.0-stardust.6.tgz", + "integrity": "sha512-jbruSRXlEKW2dgfPFG7e42ODBp4dVu2/CAh/oZrzMjehPdyF2tgAeN1woOqOAmVONWoGVV9GViZti3nShhGF1g==", + "dependencies": { + "@iota/util.js": "^1.9.0-stardust.5", + "big-integer": "^1.6.51" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@iota/iota.js": { + "version": "1.9.0-stardust.25", + "resolved": "https://registry.npmjs.org/@iota/iota.js/-/iota.js-1.9.0-stardust.25.tgz", + "integrity": "sha512-mx/ZpkrUEgPjd1FxVxDKGpmDF/aTuUcRXjzZWjBL8v2w9dn3/Bs2fZ3AJw7wIwMG6v48AMXWq2yPwndpwDx/qw==", + "peer": true, + "dependencies": { + "@iota/crypto.js": "^1.9.0-stardust.6", + "@iota/util.js": "^1.9.0-stardust.5", + "big-integer": "^1.6.51", + "node-fetch": "2.6.7" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@iota/types": { + "version": "1.0.0-beta.11", + "integrity": "sha512-jFma7n4dpf+AikSIvY6CzrDDQ/TVnlV3HBIeOrDUDJku3oCcOkm8Jp/i0uKSD/krP2BAcDrWrNUr5Tn9MIw7+A==" + }, + "node_modules/@iota/util.js": { + "version": "1.9.0-stardust.5", + "resolved": "https://registry.npmjs.org/@iota/util.js/-/util.js-1.9.0-stardust.5.tgz", + "integrity": "sha512-BGXzzjUQQYaV/H0bXJYKW+Zgd9PIN+HLPH8BcYhubI65X4G2ID23/osmpK/za+Ds6t/MkpdPKIiBwZPGXuCUBw==", + "dependencies": { + "big-integer": "^1.6.51" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", "dev": true, "dependencies": { @@ -163,7 +242,6 @@ }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", "dev": true, "engines": { @@ -172,7 +250,6 @@ }, "node_modules/@jridgewell/set-array": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", "dev": true, "engines": { @@ -181,7 +258,6 @@ }, "node_modules/@jridgewell/source-map": { "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", "dev": true, "dependencies": { @@ -191,14 +267,12 @@ }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.14", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", - "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", + "version": "0.3.9", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", @@ -207,7 +281,6 @@ }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "dependencies": { @@ -220,7 +293,6 @@ }, "node_modules/@nodelib/fs.stat": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, "engines": { @@ -228,9 +300,8 @@ } }, "node_modules/@nodelib/fs.walk": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.7.tgz", - "integrity": "sha512-BTIhocbPBSrRmHxOAJFtR18oLhxTtAFDAvL8hY1S3iU8k+E60W/YFs4jrixGzQjMpF4qPXxIQHcjVD9dz1C2QA==", + "version": "1.2.8", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "dependencies": { "@nodelib/fs.scandir": "2.1.5", @@ -241,32 +312,27 @@ } }, "node_modules/@tsconfig/node10": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", - "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", + "version": "1.0.9", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", "dev": true }, "node_modules/@tsconfig/node12": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", - "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", + "version": "1.0.11", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", "dev": true }, "node_modules/@tsconfig/node14": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", - "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", + "version": "1.0.3", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", "dev": true }, "node_modules/@tsconfig/node16": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", - "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", + "version": "1.0.3", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", "dev": true }, "node_modules/@types/debug": { "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", "integrity": "sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==", "dev": true, "dependencies": { @@ -274,9 +340,8 @@ } }, "node_modules/@types/eslint": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.28.0.tgz", - "integrity": "sha512-07XlgzX0YJUn4iG1ocY4IX9DzKSmMGUs6ESKlxWhZRaa0fatIWaHWUVapcuGa8r5HFnTqzj+4OCjd5f7EZ/i/A==", + "version": "8.4.5", + "integrity": "sha512-dhsC09y1gpJWnK+Ff4SGvCuSnk9DaU0BJZSzOwa6GVSg65XtTugLBITDAAzRU5duGBoXBHpdR/9jHGxJjNflJQ==", "dev": true, "dependencies": { "@types/estree": "*", @@ -284,9 +349,8 @@ } }, "node_modules/@types/eslint-scope": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.1.tgz", - "integrity": "sha512-SCFeogqiptms4Fg29WpOTk5nHIzfpKCemSN63ksBQYKTcXoJEmJagV+DhVmbapZzY4/5YaOV1nZwrsU79fFm1g==", + "version": "3.7.4", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", "dev": true, "dependencies": { "@types/eslint": "*", @@ -294,33 +358,29 @@ } }, "node_modules/@types/estree": { - "version": "0.0.50", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", - "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", + "version": "0.0.51", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", "dev": true }, "node_modules/@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "version": "7.0.11", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", "dev": true }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true, "optional": true }, "node_modules/@types/linkify-it": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.2.tgz", "integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==", "dev": true }, "node_modules/@types/markdown-it": { "version": "12.2.3", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", "dev": true, "dependencies": { @@ -330,7 +390,6 @@ }, "node_modules/@types/mdast": { "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz", "integrity": "sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==", "dev": true, "dependencies": { @@ -339,50 +398,49 @@ }, "node_modules/@types/mdurl": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", "dev": true }, "node_modules/@types/mocha": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.0.tgz", - "integrity": "sha512-QCWHkbMv4Y5U9oW10Uxbr45qMMSzl4OzijsozynUAgx3kEHUdXB00udx2dWDQ7f2TU2a2uuiFaRZjCe3unPpeg==", + "version": "9.1.1", + "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", "dev": true }, "node_modules/@types/ms": { "version": "0.7.31", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==", "dev": true }, "node_modules/@types/node": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.0.0.tgz", - "integrity": "sha512-TmCW5HoZ2o2/z2EYi109jLqIaPIi9y/lc2LmDCWzuCi35bcaQ+OtUh6nwBiFK7SOu25FAU5+YKdqFZUwtqGSdg==", - "dev": true + "version": "18.6.5", + "integrity": "sha512-Xjt5ZGUa5WusGZJ4WJPbOT8QOqp6nDynVFRKcUt32bOgvXEoc6o085WNkYTMO7ifAj2isEfQQ2cseE+wT6jsRw==" + }, + "node_modules/@types/node-fetch": { + "version": "2.6.2", + "integrity": "sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==", + "dependencies": { + "@types/node": "*", + "form-data": "^3.0.0" + } }, "node_modules/@types/sinonjs__fake-timers": { "version": "8.1.1", - "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz", "integrity": "sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==", "dev": true }, "node_modules/@types/sizzle": { "version": "2.3.3", - "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz", "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==", "dev": true }, "node_modules/@types/unist": { "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==", "dev": true }, "node_modules/@types/yauzl": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.2.tgz", - "integrity": "sha512-8uALY5LTvSuHgloDVUvWP3pIauILm+8/0pDMokuDYIoNsOkSwd5AiHBTSEJjKTDcZr5z8UpgOWZkxBF4iJftoA==", + "version": "2.10.0", + "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==", "dev": true, "optional": true, "dependencies": { @@ -391,13 +449,11 @@ }, "node_modules/@ungap/promise-all-settled": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", "dev": true }, "node_modules/@webassemblyjs/ast": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", "dev": true, "dependencies": { @@ -407,25 +463,21 @@ }, "node_modules/@webassemblyjs/floating-point-hex-parser": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", "dev": true }, "node_modules/@webassemblyjs/helper-api-error": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", "dev": true }, "node_modules/@webassemblyjs/helper-buffer": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", "dev": true }, "node_modules/@webassemblyjs/helper-numbers": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", "dev": true, "dependencies": { @@ -436,13 +488,11 @@ }, "node_modules/@webassemblyjs/helper-wasm-bytecode": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", "dev": true }, "node_modules/@webassemblyjs/helper-wasm-section": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", "dev": true, "dependencies": { @@ -454,7 +504,6 @@ }, "node_modules/@webassemblyjs/ieee754": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", "dev": true, "dependencies": { @@ -463,7 +512,6 @@ }, "node_modules/@webassemblyjs/leb128": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", "dev": true, "dependencies": { @@ -472,13 +520,11 @@ }, "node_modules/@webassemblyjs/utf8": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", "dev": true }, "node_modules/@webassemblyjs/wasm-edit": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", "dev": true, "dependencies": { @@ -494,7 +540,6 @@ }, "node_modules/@webassemblyjs/wasm-gen": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", "dev": true, "dependencies": { @@ -507,7 +552,6 @@ }, "node_modules/@webassemblyjs/wasm-opt": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", "dev": true, "dependencies": { @@ -519,7 +563,6 @@ }, "node_modules/@webassemblyjs/wasm-parser": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", "dev": true, "dependencies": { @@ -533,7 +576,6 @@ }, "node_modules/@webassemblyjs/wast-printer": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", "dev": true, "dependencies": { @@ -542,9 +584,8 @@ } }, "node_modules/@webpack-cli/configtest": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.1.1.tgz", - "integrity": "sha512-1FBc1f9G4P/AxMqIgfZgeOTuRnwZMten8E7zap5zgpPInnCrP8D4Q81+4CWIch8i/Nf7nXjP0v6CjjbHOrXhKg==", + "version": "1.2.0", + "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", "dev": true, "peerDependencies": { "webpack": "4.x.x || 5.x.x", @@ -552,9 +593,8 @@ } }, "node_modules/@webpack-cli/info": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.4.1.tgz", - "integrity": "sha512-PKVGmazEq3oAo46Q63tpMr4HipI3OPfP7LiNOEJg963RMgT0rqheag28NCML0o3GIzA3DmxP1ZIAv9oTX1CUIA==", + "version": "1.5.0", + "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", "dev": true, "dependencies": { "envinfo": "^7.7.3" @@ -564,9 +604,8 @@ } }, "node_modules/@webpack-cli/serve": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.6.1.tgz", - "integrity": "sha512-gNGTiTrjEVQ0OcVnzsRSqTxaBSr+dmTfm+qJsCDluky8uhdLWep7Gcr62QsAKHTMxjCS/8nEITsmFAhfIx+QSw==", + "version": "1.7.0", + "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", "dev": true, "peerDependencies": { "webpack-cli": "4.x.x" @@ -579,20 +618,17 @@ }, "node_modules/@xtuc/ieee754": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", "dev": true }, "node_modules/@xtuc/long": { "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "dev": true }, "node_modules/acorn": { - "version": "8.7.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", - "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", + "version": "8.8.0", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -602,9 +638,8 @@ } }, "node_modules/acorn-import-assertions": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.7.6.tgz", - "integrity": "sha512-FlVvVFA1TX6l3lp8VjDnYYq7R1nyW6x3svAt4nDgrWQ9SBaSh9CnbwgSUTasgfNfOG5HlM1ehugCvM+hjo56LA==", + "version": "1.8.0", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", "dev": true, "peerDependencies": { "acorn": "^8" @@ -612,7 +647,6 @@ }, "node_modules/acorn-walk": { "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", "dev": true, "engines": { @@ -621,7 +655,6 @@ }, "node_modules/aggregate-error": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "dev": true, "dependencies": { @@ -634,7 +667,6 @@ }, "node_modules/ajv": { "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "dependencies": { @@ -650,7 +682,6 @@ }, "node_modules/ajv-keywords": { "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "dev": true, "peerDependencies": { @@ -658,9 +689,8 @@ } }, "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "version": "4.1.3", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true, "engines": { "node": ">=6" @@ -668,7 +698,6 @@ }, "node_modules/ansi-escape-sequences": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-escape-sequences/-/ansi-escape-sequences-4.1.0.tgz", "integrity": "sha512-dzW9kHxH011uBsidTXd14JXgzye/YLb2LzeKZ4bsgl/Knwx8AtbSFkkGxagdNOoh0DlqHCmfiEjWKBaqjOanVw==", "dev": true, "dependencies": { @@ -680,7 +709,6 @@ }, "node_modules/ansi-escape-sequences/node_modules/array-back": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", "dev": true, "engines": { @@ -689,7 +717,6 @@ }, "node_modules/ansi-escapes": { "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, "dependencies": { @@ -702,18 +729,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ansi-regex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "node_modules/ansi-styles": { + "version": "4.3.0", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, "engines": { - "node": ">=4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/anymatch": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, "dependencies": { @@ -726,7 +757,6 @@ }, "node_modules/arch": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", "dev": true, "funding": [ @@ -746,19 +776,16 @@ }, "node_modules/arg": { "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", "dev": true }, "node_modules/argparse": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, "node_modules/array-back": { "version": "6.2.2", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.2.tgz", "integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==", "dev": true, "engines": { @@ -767,7 +794,6 @@ }, "node_modules/array-union": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true, "engines": { @@ -776,8 +802,7 @@ }, "node_modules/arrify": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -785,7 +810,6 @@ }, "node_modules/asn1": { "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", "dev": true, "dependencies": { @@ -794,8 +818,7 @@ }, "node_modules/assert-plus": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", "dev": true, "engines": { "node": ">=0.8" @@ -803,7 +826,6 @@ }, "node_modules/astral-regex": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true, "engines": { @@ -811,19 +833,13 @@ } }, "node_modules/async": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", - "dev": true, - "dependencies": { - "lodash": "^4.17.14" - } + "version": "3.2.4", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "dev": true }, "node_modules/asynckit": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/at-least-node": { "version": "1.0.0", @@ -836,8 +852,7 @@ }, "node_modules/aws-sign2": { "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", "dev": true, "engines": { "node": "*" @@ -845,13 +860,11 @@ }, "node_modules/aws4": { "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", "dev": true }, "node_modules/axios": { "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", "dev": true, "dependencies": { @@ -860,7 +873,6 @@ }, "node_modules/bail": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", "dev": true, "funding": { @@ -870,13 +882,11 @@ }, "node_modules/balanced-match": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, "node_modules/base64-js": { "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "dev": true, "funding": [ @@ -896,7 +906,6 @@ }, "node_modules/basic-auth": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", "dev": true, "dependencies": { @@ -906,18 +915,29 @@ "node": ">= 0.8" } }, + "node_modules/basic-auth/node_modules/safe-buffer": { + "version": "5.1.2", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, "node_modules/bcrypt-pbkdf": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", "dev": true, "dependencies": { "tweetnacl": "^0.14.3" } }, + "node_modules/big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "engines": { + "node": ">=0.6" + } + }, "node_modules/big.js": { "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", "dev": true, "engines": { @@ -926,7 +946,6 @@ }, "node_modules/binary-extensions": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true, "engines": { @@ -935,7 +954,6 @@ }, "node_modules/binary-install": { "version": "0.1.1", - "resolved": "https://registry.npmjs.org/binary-install/-/binary-install-0.1.1.tgz", "integrity": "sha512-DqED0D/6LrS+BHDkKn34vhRqOGjy5gTMgvYZsGK2TpNbdPuz4h+MRlNgGv5QBRd7pWq/jylM4eKNCizgAq3kNQ==", "dev": true, "dependencies": { @@ -949,19 +967,16 @@ }, "node_modules/blob-util": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz", "integrity": "sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==", "dev": true }, "node_modules/bluebird": { "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", "dev": true }, "node_modules/brace-expansion": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "dependencies": { @@ -971,7 +986,6 @@ }, "node_modules/braces": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, "dependencies": { @@ -983,36 +997,38 @@ }, "node_modules/browser-stdout": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, "node_modules/browserslist": { - "version": "4.16.6", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", - "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", + "version": "4.21.3", + "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], "dependencies": { - "caniuse-lite": "^1.0.30001219", - "colorette": "^1.2.2", - "electron-to-chromium": "^1.3.723", - "escalade": "^3.1.1", - "node-releases": "^1.1.71" + "caniuse-lite": "^1.0.30001370", + "electron-to-chromium": "^1.4.202", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.5" }, "bin": { "browserslist": "cli.js" }, "engines": { "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" } }, "node_modules/buffer": { "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "dev": true, "funding": [ @@ -1036,22 +1052,19 @@ }, "node_modules/buffer-crc32": { "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "dev": true, "engines": { "node": "*" } }, "node_modules/buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "version": "1.1.2", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, "node_modules/cache-point": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/cache-point/-/cache-point-2.0.0.tgz", "integrity": "sha512-4gkeHlFpSKgm3vm2gJN5sPqfmijYRFYCQ6tv5cLw0xVmT6r1z1vd4FNnpuOREco3cBs1G709sZ72LdgddKvL5w==", "dev": true, "dependencies": { @@ -1065,7 +1078,6 @@ }, "node_modules/cache-point/node_modules/array-back": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz", "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==", "dev": true, "engines": { @@ -1074,7 +1086,6 @@ }, "node_modules/cachedir": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz", "integrity": "sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==", "dev": true, "engines": { @@ -1082,18 +1093,19 @@ } }, "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "version": "6.3.0", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, "engines": { - "node": ">=6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/caniuse-lite": { - "version": "1.0.30001319", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001319.tgz", - "integrity": "sha512-xjlIAFHucBRSMUo1kb5D4LYgcN1M45qdKP++lhqowDpwJwGkpIRTt5qQqnhxjj1vHcI7nrJxWhCC1ATrCEBTcw==", + "version": "1.0.30001375", + "integrity": "sha512-kWIMkNzLYxSvnjy0hL8w1NOaWNr2rn39RTAVyIwcw8juu60bZDWiF1/loOYANzjtJmy6qPgNmn38ro5Pygagdw==", "dev": true, "funding": [ { @@ -1108,13 +1120,11 @@ }, "node_modules/caseless": { "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", "dev": true }, "node_modules/catharsis": { "version": "0.9.0", - "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", "dev": true, "dependencies": { @@ -1124,10 +1134,35 @@ "node": ">= 10" } }, + "node_modules/chalk": { + "version": "4.1.2", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/character-entities": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.1.tgz", - "integrity": "sha512-OzmutCf2Kmc+6DrFrrPS8/tDh2+DpnrfzdICHWhcVC9eOd0N1PXmQEE1a8iM4IziIAG+8tmTq3K+oo0ubH6RRQ==", + "version": "2.0.2", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", "dev": true, "funding": { "type": "github", @@ -1136,8 +1171,7 @@ }, "node_modules/check-more-types": { "version": "2.24.0", - "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", - "integrity": "sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA=", + "integrity": "sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==", "dev": true, "engines": { "node": ">= 0.8.0" @@ -1145,7 +1179,6 @@ }, "node_modules/chokidar": { "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, "funding": [ @@ -1170,18 +1203,8 @@ "fsevents": "~2.3.2" } }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/chrome-trace-event": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", "dev": true, "engines": { @@ -1189,14 +1212,12 @@ } }, "node_modules/ci-info": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz", - "integrity": "sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==", + "version": "3.3.2", + "integrity": "sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg==", "dev": true }, "node_modules/clean-stack": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true, "engines": { @@ -1205,7 +1226,6 @@ }, "node_modules/cli-cursor": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "dev": true, "dependencies": { @@ -1215,31 +1235,9 @@ "node": ">=8" } }, - "node_modules/cli-table": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.1.tgz", - "integrity": "sha1-9TsFJmqLGguTSz0IIebi3FkUriM=", - "dev": true, - "dependencies": { - "colors": "1.0.3" - }, - "engines": { - "node": ">= 0.2.0" - } - }, - "node_modules/cli-table/node_modules/colors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", - "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", - "dev": true, - "engines": { - "node": ">=0.1.90" - } - }, "node_modules/cli-table3": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.1.tgz", - "integrity": "sha512-w0q/enDHhPLq44ovMGdQeeDLvwxwavsJX7oQGYt/LrBlYsyaxyDnp6z3QzFut/6kLLKnlcUVJLrpB7KBfgG/RA==", + "version": "0.6.2", + "integrity": "sha512-QyavHCaIC80cMivimWu4aWHilIpiDpfm3hGmqAmXVL1UsnbLuBSMd21hTX6VY4ZSDSM73ESLeF8TOYId3rBTbw==", "dev": true, "dependencies": { "string-width": "^4.2.0" @@ -1248,12 +1246,11 @@ "node": "10.* || >= 12.*" }, "optionalDependencies": { - "colors": "1.4.0" + "@colors/colors": "1.5.0" } }, "node_modules/cli-truncate": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", "dev": true, "dependencies": { @@ -1269,7 +1266,6 @@ }, "node_modules/cliui": { "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, "dependencies": { @@ -1280,7 +1276,6 @@ }, "node_modules/cliui/node_modules/ansi-regex": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "engines": { @@ -1289,7 +1284,6 @@ }, "node_modules/cliui/node_modules/strip-ansi": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "dependencies": { @@ -1301,7 +1295,6 @@ }, "node_modules/clone-deep": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", "dev": true, "dependencies": { @@ -1315,7 +1308,6 @@ }, "node_modules/collect-all": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/collect-all/-/collect-all-1.0.4.tgz", "integrity": "sha512-RKZhRwJtJEP5FWul+gkSMEnaK6H3AGPTTWOiRimCcs+rc/OmQE3Yhy1Q7A7KsdkG3ZXVdZq68Y6ONSdvkeEcKA==", "dev": true, "dependencies": { @@ -1326,27 +1318,30 @@ "node": ">=0.10.0" } }, - "node_modules/colorette": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", - "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", - "dev": true - }, - "node_modules/colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "node_modules/color-convert": { + "version": "2.0.1", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "optional": true, + "dependencies": { + "color-name": "~1.1.4" + }, "engines": { - "node": ">=0.1.90" + "node": ">=7.0.0" } }, + "node_modules/color-name": { + "version": "1.1.4", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/colorette": { + "version": "2.0.19", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", + "dev": true + }, "node_modules/combined-stream": { "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -1355,12 +1350,11 @@ } }, "node_modules/command-line-args": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.1.1.tgz", - "integrity": "sha512-hL/eG8lrll1Qy1ezvkant+trihbGnaKaeEjj6Scyr3DN+RC7iQ5Rz84IeLERfAWDGo0HBSNAakczwgCilDXnWg==", + "version": "5.2.1", + "integrity": "sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==", "dev": true, "dependencies": { - "array-back": "^3.0.1", + "array-back": "^3.1.0", "find-replace": "^3.0.0", "lodash.camelcase": "^4.3.0", "typical": "^4.0.0" @@ -1371,7 +1365,6 @@ }, "node_modules/command-line-args/node_modules/array-back": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", "dev": true, "engines": { @@ -1380,7 +1373,6 @@ }, "node_modules/command-line-args/node_modules/typical": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==", "dev": true, "engines": { @@ -1389,7 +1381,6 @@ }, "node_modules/command-line-tool": { "version": "0.8.0", - "resolved": "https://registry.npmjs.org/command-line-tool/-/command-line-tool-0.8.0.tgz", "integrity": "sha512-Xw18HVx/QzQV3Sc5k1vy3kgtOeGmsKIqwtFFoyjI4bbcpSgnw2CWVULvtakyw4s6fhyAdI6soQQhXc2OzJy62g==", "dev": true, "dependencies": { @@ -1405,7 +1396,6 @@ }, "node_modules/command-line-tool/node_modules/array-back": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", "dev": true, "dependencies": { @@ -1417,7 +1407,6 @@ }, "node_modules/command-line-usage": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-4.1.0.tgz", "integrity": "sha512-MxS8Ad995KpdAC0Jopo/ovGIroV/m0KHwzKfXxKag6FHOkGsH8/lv5yjgablcRxCJJC0oJeUMuO/gmaq+Wq46g==", "dev": true, "dependencies": { @@ -1432,7 +1421,6 @@ }, "node_modules/command-line-usage/node_modules/array-back": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", "dev": true, "dependencies": { @@ -1443,14 +1431,15 @@ } }, "node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true + "version": "5.1.0", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "dev": true, + "engines": { + "node": ">= 6" + } }, "node_modules/common-sequence": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/common-sequence/-/common-sequence-2.0.2.tgz", "integrity": "sha512-jAg09gkdkrDO9EWTdXfv80WWH3yeZl5oT69fGfedBNS9pXUKYInVJ1bJ+/ht2+Moeei48TmSbQDYMc8EOx9G0g==", "dev": true, "engines": { @@ -1459,7 +1448,6 @@ }, "node_modules/common-tags": { "version": "1.8.2", - "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", "dev": true, "engines": { @@ -1468,24 +1456,23 @@ }, "node_modules/concat-map": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, "node_modules/concurrently": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-7.0.0.tgz", - "integrity": "sha512-WKM7PUsI8wyXpF80H+zjHP32fsgsHNQfPLw/e70Z5dYkV7hF+rf8q3D+ScWJIEr57CpkO3OWBko6hwhQLPR8Pw==", + "version": "7.3.0", + "integrity": "sha512-IiDwm+8DOcFEInca494A8V402tNTQlJaYq78RF2rijOrKEk/AOHTxhN4U1cp7GYKYX5Q6Ymh1dLTBlzIMN0ikA==", "dev": true, "dependencies": { "chalk": "^4.1.0", "date-fns": "^2.16.1", "lodash": "^4.17.21", - "rxjs": "^6.6.3", + "rxjs": "^7.0.0", + "shell-quote": "^1.7.3", "spawn-command": "^0.0.2-1", "supports-color": "^8.1.0", "tree-kill": "^1.2.2", - "yargs": "^16.2.0" + "yargs": "^17.3.1" }, "bin": { "concurrently": "dist/bin/concurrently.js" @@ -1494,163 +1481,50 @@ "node": "^12.20.0 || ^14.13.0 || >=16.0.0" } }, - "node_modules/concurrently/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/config-master": { + "version": "3.1.0", + "integrity": "sha512-n7LBL1zBzYdTpF1mx5DNcZnZn05CWIdsdvtPL4MosvqbBUK3Rq6VWEtGUuF3Y0s9/CIhMejezqlSkP6TnCJ/9g==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" - }, + "walk-back": "^2.0.1" + } + }, + "node_modules/config-master/node_modules/walk-back": { + "version": "2.0.1", + "integrity": "sha512-Nb6GvBR8UWX1D+Le+xUq0+Q1kFmRBIWVrfLnQAOmcpEzA9oAxwJ9gIr36t9TWYfzvWRvuMtjHiVsJYEkXWaTAQ==", + "dev": true, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=0.10.0" } }, - "node_modules/concurrently/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/copy-webpack-plugin": { + "version": "7.0.0", + "integrity": "sha512-SLjQNa5iE3BoCP76ESU9qYo9ZkEWtXoZxDurHoqPchAFRblJ9g96xTeC560UXBMre1Nx6ixIIUfiY3VcjpJw3g==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "fast-glob": "^3.2.4", + "glob-parent": "^5.1.1", + "globby": "^11.0.1", + "loader-utils": "^2.0.0", + "normalize-path": "^3.0.0", + "p-limit": "^3.0.2", + "schema-utils": "^3.0.0", + "serialize-javascript": "^5.0.1" }, "engines": { - "node": ">=10" + "node": ">= 10.13.0" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" } }, - "node_modules/concurrently/node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/concurrently/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/concurrently/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/concurrently/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/concurrently/node_modules/rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dev": true, - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/concurrently/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/concurrently/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/config-master": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/config-master/-/config-master-3.1.0.tgz", - "integrity": "sha1-ZnZjWQUFooO/JqSE1oSJ10xUhdo=", - "dev": true, - "dependencies": { - "walk-back": "^2.0.1" - } - }, - "node_modules/config-master/node_modules/walk-back": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/walk-back/-/walk-back-2.0.1.tgz", - "integrity": "sha1-VU4qnYdPrEeoywBr9EwvDEmYoKQ=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/copy-webpack-plugin": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-7.0.0.tgz", - "integrity": "sha512-SLjQNa5iE3BoCP76ESU9qYo9ZkEWtXoZxDurHoqPchAFRblJ9g96xTeC560UXBMre1Nx6ixIIUfiY3VcjpJw3g==", - "dev": true, - "dependencies": { - "fast-glob": "^3.2.4", - "glob-parent": "^5.1.1", - "globby": "^11.0.1", - "loader-utils": "^2.0.0", - "normalize-path": "^3.0.0", - "p-limit": "^3.0.2", - "schema-utils": "^3.0.0", - "serialize-javascript": "^5.0.1" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - } - }, - "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, "node_modules/corser": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", - "integrity": "sha1-jtolLsqrWEDc2XXOuQ2TcMgZ/4c=", + "integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==", "dev": true, "engines": { "node": ">= 0.4.0" @@ -1658,13 +1532,11 @@ }, "node_modules/create-require": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, "node_modules/cross-spawn": { "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "dependencies": { @@ -1677,9 +1549,8 @@ } }, "node_modules/cypress": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-9.3.1.tgz", - "integrity": "sha512-BODdPesxX6bkVUnH8BVsV8I/jn57zQtO1FEOUTiuG2us3kslW7g0tcuwiny7CKCmJUZz8S/D587ppC+s58a+5Q==", + "version": "9.7.0", + "integrity": "sha512-+1EE1nuuuwIt/N1KXRR2iWHU+OiIt7H28jJDyyI4tiUftId/DrXYEwoDa5+kH2pki1zxnA0r6HrUGHV5eLbF5Q==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -1715,15 +1586,15 @@ "listr2": "^3.8.3", "lodash": "^4.17.21", "log-symbols": "^4.0.0", - "minimist": "^1.2.5", + "minimist": "^1.2.6", "ospath": "^1.2.2", "pretty-bytes": "^5.6.0", "proxy-from-env": "1.0.0", "request-progress": "^3.0.0", + "semver": "^7.3.2", "supports-color": "^8.1.1", "tmp": "~0.2.1", "untildify": "^4.0.0", - "url": "^0.11.0", "yauzl": "^2.10.0" }, "bin": { @@ -1734,75 +1605,46 @@ } }, "node_modules/cypress-parallel": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/cypress-parallel/-/cypress-parallel-0.1.8.tgz", - "integrity": "sha512-aR0DZZeo3r/tBtXGAN3aug9G4/JGLCPy3FHHmko9MtZKwg4mqxVa+OXxh/JkxMlf+YqbZgoAfyV259LHuEBSBQ==", + "version": "0.1.10", + "integrity": "sha512-TKuJtvM0RU/i/SSkZhAy98FebpVypHqrpic+Y3Pnr3Ujj8JQlUu4R/QOILNI0bdjaPX0WX8yN5KSGmbJmk4VPg==", "dev": true, "dependencies": { - "cli-table": "0.3.1", + "cli-table3": "^0.6.0", + "cross-spawn": "^7.0.3", "is-npm": "^5.0.0", - "mocha": "^8.0.1", + "mocha": "^8.2.1", "yargs": "15.3.1" }, "bin": { "cypress-parallel": "cli.js" } }, - "node_modules/cypress-parallel/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/cypress-parallel/node_modules/ansi-colors": { + "version": "4.1.1", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true, "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/cypress-parallel/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/cypress-parallel/node_modules/ansi-regex": { + "version": "3.0.1", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/cypress-parallel/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=4" } }, - "node_modules/cypress-parallel/node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/cypress-parallel/node_modules/camelcase": { + "version": "5.3.1", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, "engines": { - "node": ">=8" + "node": ">=6" } }, "node_modules/cypress-parallel/node_modules/chokidar": { "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", "dev": true, "dependencies": { @@ -1821,27 +1663,8 @@ "fsevents": "~2.3.1" } }, - "node_modules/cypress-parallel/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/cypress-parallel/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, "node_modules/cypress-parallel/node_modules/debug": { "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, "dependencies": { @@ -1856,15 +1679,16 @@ } } }, - "node_modules/cypress-parallel/node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "node_modules/cypress-parallel/node_modules/decamelize": { + "version": "1.2.0", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, "node_modules/cypress-parallel/node_modules/escape-string-regexp": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "engines": { @@ -1876,7 +1700,6 @@ }, "node_modules/cypress-parallel/node_modules/glob": { "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "dev": true, "dependencies": { @@ -1894,33 +1717,38 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/cypress-parallel/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/cypress-parallel/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", "dev": true, "engines": { - "node": ">=8" + "node": ">=4" + } + }, + "node_modules/cypress-parallel/node_modules/js-yaml": { + "version": "4.0.0", + "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, "node_modules/cypress-parallel/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "version": "5.0.0", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "dependencies": { - "p-locate": "^5.0.0" + "p-locate": "^4.1.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/cypress-parallel/node_modules/log-symbols": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", "dev": true, "dependencies": { @@ -1930,9 +1758,19 @@ "node": ">=10" } }, + "node_modules/cypress-parallel/node_modules/minimatch": { + "version": "3.0.4", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/cypress-parallel/node_modules/mocha": { "version": "8.4.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz", "integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==", "dev": true, "dependencies": { @@ -1974,25 +1812,13 @@ "url": "https://opencollective.com/mochajs" } }, - "node_modules/cypress-parallel/node_modules/mocha/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "node_modules/cypress-parallel/node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true }, "node_modules/cypress-parallel/node_modules/mocha/node_modules/yargs": { "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "dependencies": { @@ -2008,15 +1834,8 @@ "node": ">=10" } }, - "node_modules/cypress-parallel/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, "node_modules/cypress-parallel/node_modules/nanoid": { "version": "3.1.20", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", "dev": true, "bin": { @@ -2026,315 +1845,212 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/cypress-parallel/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "node_modules/cypress-parallel/node_modules/p-limit": { + "version": "2.3.0", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "dependencies": { - "p-limit": "^3.0.2" + "p-try": "^2.0.0" }, "engines": { - "node": ">=10" + "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cypress-parallel/node_modules/readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", - "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/cypress-parallel/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/cypress-parallel/node_modules/p-locate": { + "version": "4.1.0", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "dependencies": { - "ansi-regex": "^5.0.1" + "p-limit": "^2.2.0" }, "engines": { "node": ">=8" } }, - "node_modules/cypress-parallel/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/cypress-parallel/node_modules/workerpool": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz", - "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==", - "dev": true - }, - "node_modules/cypress-parallel/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "node_modules/cypress-parallel/node_modules/readdirp": { + "version": "3.5.0", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", "dev": true, "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "picomatch": "^2.2.1" }, "engines": { - "node": ">=8" + "node": ">=8.10.0" } }, - "node_modules/cypress-parallel/node_modules/yargs": { - "version": "15.3.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz", - "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==", + "node_modules/cypress-parallel/node_modules/strip-ansi": { + "version": "4.0.0", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", "dev": true, "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.1" + "ansi-regex": "^3.0.0" }, "engines": { - "node": ">=8" - } - }, - "node_modules/cypress-parallel/node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true, - "engines": { - "node": ">=10" + "node": ">=4" } }, - "node_modules/cypress-parallel/node_modules/yargs/node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "node_modules/cypress-parallel/node_modules/wide-align": { + "version": "1.1.3", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dev": true, "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" + "string-width": "^1.0.2 || 2" } }, - "node_modules/cypress-parallel/node_modules/yargs/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true - }, - "node_modules/cypress-parallel/node_modules/yargs/node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "node_modules/cypress-parallel/node_modules/wide-align/node_modules/string-width": { + "version": "2.1.1", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" }, "engines": { - "node": ">=6" + "node": ">=4" } }, - "node_modules/cypress/node_modules/@types/node": { - "version": "14.18.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.9.tgz", - "integrity": "sha512-j11XSuRuAlft6vLDEX4RvhqC0KxNxx6QIyMXNb0vHHSNPXTPeiy3algESWmOOIzEtiEL0qiowPU3ewW9hHVa7Q==", + "node_modules/cypress-parallel/node_modules/workerpool": { + "version": "6.1.0", + "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==", "dev": true }, - "node_modules/cypress/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/cypress-parallel/node_modules/wrap-ansi": { + "version": "6.2.0", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/cypress/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/cypress-parallel/node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "5.0.1", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=8" } }, - "node_modules/cypress/node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/cypress-parallel/node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "6.0.1", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" } }, - "node_modules/cypress/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/cypress-parallel/node_modules/yargs": { + "version": "15.3.1", + "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.1" }, "engines": { - "node": ">=7.0.0" + "node": ">=8" } }, - "node_modules/cypress/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/cypress/node_modules/commander": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "node_modules/cypress-parallel/node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "engines": { - "node": ">= 6" + "node": ">=8" } }, - "node_modules/cypress/node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "node_modules/cypress-parallel/node_modules/yargs/node_modules/cliui": { + "version": "6.0.0", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", "dev": true, "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" } }, - "node_modules/cypress/node_modules/execa": { + "node_modules/cypress-parallel/node_modules/yargs/node_modules/find-up": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", - "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "dependencies": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" + "node": ">=8" } }, - "node_modules/cypress/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "node_modules/cypress-parallel/node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "dependencies": { - "pump": "^3.0.0" + "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cypress/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } + "node_modules/cypress-parallel/node_modules/yargs/node_modules/y18n": { + "version": "4.0.3", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true }, - "node_modules/cypress/node_modules/human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "node_modules/cypress-parallel/node_modules/yargs/node_modules/yargs-parser": { + "version": "18.1.3", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, "engines": { - "node": ">=8.12.0" + "node": ">=6" } }, - "node_modules/cypress/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "node_modules/cypress/node_modules/@types/node": { + "version": "14.18.23", + "integrity": "sha512-MhbCWN18R4GhO8ewQWAFK4TGQdBpXWByukz7cWyJmXhvRuCIaM/oWytGPqVmDzgEnnaIc9ss6HbU5mUi+vyZPA==", "dev": true }, - "node_modules/cypress/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "node_modules/cypress/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" } }, "node_modules/dashdash": { "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", "dev": true, "dependencies": { "assert-plus": "^1.0.0" @@ -2344,9 +2060,8 @@ } }, "node_modules/date-fns": { - "version": "2.28.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.28.0.tgz", - "integrity": "sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==", + "version": "2.29.1", + "integrity": "sha512-dlLD5rKaKxpFdnjrs+5azHDFOPEu4ANy/LTh04A1DTzMM7qoajmKCBc8pkKRFT41CNzw+4gQh79X5C+Jq27HAw==", "dev": true, "engines": { "node": ">=0.11" @@ -2357,24 +2072,40 @@ } }, "node_modules/dayjs": { - "version": "1.10.7", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.7.tgz", - "integrity": "sha512-P6twpd70BcPK34K26uJ1KT3wlhpuOAPoMwJzpsIWUxHZ7wpmbdZL/hQqBDfz7hGurYSa5PhzdhDHtt319hL3ig==", + "version": "1.11.4", + "integrity": "sha512-Zj/lPM5hOvQ1Bf7uAvewDaUcsJoI6JmNqmHhHl3nyumwe0XHwt8sWdOVAPACJzCebL8gQCi+K49w7iKWnGwX9g==", "dev": true }, + "node_modules/debug": { + "version": "4.3.4", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "version": "4.0.0", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/decode-named-character-reference": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.1.tgz", - "integrity": "sha512-YV/0HQHreRwKb7uBopyIkLG17jG6Sv2qUchk9qSoVJ2f+flwRsPNBO0hAnjt6mTNYUT+vw9Gy2ihXg4sUWPi2w==", + "version": "1.0.2", + "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", "dev": true, "dependencies": { "character-entities": "^2.0.0" @@ -2386,7 +2117,6 @@ }, "node_modules/deep-extend": { "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true, "engines": { @@ -2395,17 +2125,14 @@ }, "node_modules/delayed-stream": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true, + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "engines": { "node": ">=0.4.0" } }, "node_modules/dequal": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.2.tgz", - "integrity": "sha512-q9K8BlJVxK7hQYqa6XISGmBZbtQQWVXSrRrWreHC94rMt1QL/Impruc+7p2CYSYuVIUr+YCt6hjrs1kkdJRTug==", + "version": "2.0.3", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", "dev": true, "engines": { "node": ">=6" @@ -2413,7 +2140,6 @@ }, "node_modules/diff": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", "dev": true, "engines": { @@ -2422,13 +2148,11 @@ }, "node_modules/diff-match-patch": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==", "dev": true }, "node_modules/dir-glob": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, "dependencies": { @@ -2440,7 +2164,6 @@ }, "node_modules/dmd": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/dmd/-/dmd-6.1.0.tgz", "integrity": "sha512-0zQIJ873gay1scCTFZvHPWM9mVJBnaylB2NQDI8O9u8O32m00Jb6uxDKexZm8hjTRM7RiWe0FJ32pExHoXdwoQ==", "dev": true, "dependencies": { @@ -2461,19 +2184,9 @@ "node": ">=12" } }, - "node_modules/dmd/node_modules/reduce-flatten": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-3.0.1.tgz", - "integrity": "sha512-bYo+97BmUUOzg09XwfkwALt4PQH1M5L0wzKerBt6WLm3Fhdd43mMS89HiT1B9pJIqko/6lWx3OnV4J9f2Kqp5Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/ecc-jsbn": { "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", "dev": true, "dependencies": { "jsbn": "~0.1.0", @@ -2481,20 +2194,17 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.3.768", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.768.tgz", - "integrity": "sha512-I4UMZHhVSK2pwt8jOIxTi3GIuc41NkddtKT/hpuxp9GO5UWJgDKTBa4TACppbVAuKtKbMK6BhQZvT5tFF1bcNA==", + "version": "1.4.213", + "integrity": "sha512-+3DbGHGOCHTVB/Ms63bGqbyC1b8y7Fk86+7ltssB8NQrZtSCvZG6eooSl9U2Q0yw++fL2DpHKOdTU0NVEkFObg==", "dev": true }, "node_modules/emoji-regex": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, "node_modules/emojis-list": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", "dev": true, "engines": { @@ -2503,7 +2213,6 @@ }, "node_modules/end-of-stream": { "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "dev": true, "dependencies": { @@ -2511,9 +2220,8 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz", - "integrity": "sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA==", + "version": "5.10.0", + "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", "dev": true, "dependencies": { "graceful-fs": "^4.2.4", @@ -2525,7 +2233,6 @@ }, "node_modules/enquirer": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", "dev": true, "dependencies": { @@ -2537,7 +2244,6 @@ }, "node_modules/entities": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", "dev": true, "funding": { @@ -2546,7 +2252,6 @@ }, "node_modules/envinfo": { "version": "7.8.1", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", "dev": true, "bin": { @@ -2558,13 +2263,11 @@ }, "node_modules/es-module-lexer": { "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", "dev": true }, "node_modules/escalade": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true, "engines": { @@ -2573,8 +2276,7 @@ }, "node_modules/escape-string-regexp": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, "engines": { "node": ">=0.8.0" @@ -2582,7 +2284,6 @@ }, "node_modules/eslint-scope": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, "dependencies": { @@ -2595,7 +2296,6 @@ }, "node_modules/esrecurse": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "dependencies": { @@ -2606,9 +2306,8 @@ } }, "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "version": "5.3.0", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "engines": { "node": ">=4.0" @@ -2616,7 +2315,6 @@ }, "node_modules/estraverse": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, "engines": { @@ -2624,20 +2322,17 @@ } }, "node_modules/eventemitter2": { - "version": "6.4.5", - "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.5.tgz", - "integrity": "sha512-bXE7Dyc1i6oQElDG0jMRZJrRAn9QR2xyyFGmBdZleNmyQX0FqGYmhZIrIrpPfm/w//LTo4tVQGOGQcGCb5q9uw==", + "version": "6.4.7", + "integrity": "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==", "dev": true }, "node_modules/eventemitter3": { "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", "dev": true }, "node_modules/events": { "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true, "engines": { @@ -2645,19 +2340,18 @@ } }, "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "version": "4.1.0", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", "dev": true, "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", "strip-final-newline": "^2.0.0" }, "engines": { @@ -2669,7 +2363,6 @@ }, "node_modules/executable": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", "dev": true, "dependencies": { @@ -2679,24 +2372,13 @@ "node": ">=4" } }, - "node_modules/executable/node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/extend": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "dev": true }, "node_modules/extract-zip": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", "dev": true, "dependencies": { @@ -2714,48 +2396,9 @@ "@types/yauzl": "^2.9.1" } }, - "node_modules/extract-zip/node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/extract-zip/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/extract-zip/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/extsprintf": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", "dev": true, "engines": [ "node >=0.6.0" @@ -2763,14 +2406,12 @@ }, "node_modules/fast-deep-equal": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, "node_modules/fast-glob": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.6.tgz", - "integrity": "sha512-GnLuqj/pvQ7pX8/L4J84nijv6sAnlwvSDpMkJi9i7nPmPxGtRPkBSStfvDW5l6nMdX9VWe+pkKWFTgD+vF2QSQ==", + "version": "3.2.11", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -2780,25 +2421,25 @@ "micromatch": "^4.0.4" }, "engines": { - "node": ">=8" + "node": ">=8.6.0" } }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, "node_modules/fastest-levenshtein": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", - "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", - "dev": true + "version": "1.0.16", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, + "engines": { + "node": ">= 4.9.1" + } }, "node_modules/fastq": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.1.tgz", - "integrity": "sha512-HOnr8Mc60eNYl1gzwp6r5RoUyAn5/glBolUzP/Ez6IFVPMPirxn/9phgL6zhOtaTy7ISwPvQ+wT+hfcRZh/bzw==", + "version": "1.13.0", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", "dev": true, "dependencies": { "reusify": "^1.0.4" @@ -2806,8 +2447,7 @@ }, "node_modules/fd-slicer": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", "dev": true, "dependencies": { "pend": "~1.2.0" @@ -2815,7 +2455,6 @@ }, "node_modules/figures": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", "dev": true, "dependencies": { @@ -2830,7 +2469,6 @@ }, "node_modules/file-set": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/file-set/-/file-set-4.0.2.tgz", "integrity": "sha512-fuxEgzk4L8waGXaAkd8cMr73Pm0FxOVkn8hztzUW7BAHhOGH90viQNXbiOsnecCWmfInqU6YmAMwxRMdKETceQ==", "dev": true, "dependencies": { @@ -2843,7 +2481,6 @@ }, "node_modules/file-set/node_modules/array-back": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-5.0.0.tgz", "integrity": "sha512-kgVWwJReZWmVuWOQKEOohXKJX+nD02JAZ54D1RRWlv8L0NebauKAaFxACKzB74RTclt1+WNz5KHaLRDAPZbDEw==", "dev": true, "engines": { @@ -2852,7 +2489,6 @@ }, "node_modules/fill-range": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, "dependencies": { @@ -2864,7 +2500,6 @@ }, "node_modules/find-replace": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", "dev": true, "dependencies": { @@ -2876,7 +2511,6 @@ }, "node_modules/find-replace/node_modules/array-back": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", "dev": true, "engines": { @@ -2884,21 +2518,22 @@ } }, "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "version": "5.0.0", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "dependencies": { - "locate-path": "^5.0.0", + "locate-path": "^6.0.0", "path-exists": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/flat": { "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "dev": true, "bin": { @@ -2906,9 +2541,8 @@ } }, "node_modules/follow-redirects": { - "version": "1.14.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", - "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==", + "version": "1.15.1", + "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", "dev": true, "funding": [ { @@ -2927,45 +2561,40 @@ }, "node_modules/forever-agent": { "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", "dev": true, "engines": { "node": "*" } }, "node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, + "version": "3.0.1", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", "dependencies": { "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", + "combined-stream": "^1.0.8", "mime-types": "^2.1.12" }, "engines": { - "node": ">= 0.12" + "node": ">= 6" } }, - "node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, "dependencies": { - "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" }, "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/fs-minipass": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", "dev": true, "dependencies": { @@ -2977,8 +2606,7 @@ }, "node_modules/fs-then-native": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fs-then-native/-/fs-then-native-2.0.0.tgz", - "integrity": "sha1-GaEk2U2QwiyOBF8ujdbr6jbUjGc=", + "integrity": "sha512-X712jAOaWXkemQCAmWeg5rOT2i+KOpWz1Z/txk/cW0qlOu2oQ9H61vc5w3X/iyuUEfq/OyaFJ78/cZAQD1/bgA==", "dev": true, "engines": { "node": ">=4.0.0" @@ -2986,19 +2614,16 @@ }, "node_modules/fs.realpath": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, "node_modules/function-bind": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, "node_modules/get-caller-file": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, "engines": { @@ -3006,12 +2631,14 @@ } }, "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "version": "5.2.0", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, "engines": { - "node": ">=10" + "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -3019,38 +2646,29 @@ }, "node_modules/getos": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz", "integrity": "sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==", "dev": true, "dependencies": { "async": "^3.2.0" } }, - "node_modules/getos/node_modules/async": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", - "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==", - "dev": true - }, "node_modules/getpass": { "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", "dev": true, "dependencies": { "assert-plus": "^1.0.0" } }, "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "7.2.3", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" }, @@ -3063,7 +2681,6 @@ }, "node_modules/glob-parent": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "dependencies": { @@ -3075,13 +2692,11 @@ }, "node_modules/glob-to-regexp": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", "dev": true }, "node_modules/global-dirs": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz", "integrity": "sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==", "dev": true, "dependencies": { @@ -3095,16 +2710,15 @@ } }, "node_modules/globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "version": "11.1.0", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", "slash": "^3.0.0" }, "engines": { @@ -3115,14 +2729,12 @@ } }, "node_modules/graceful-fs": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", - "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", + "version": "4.2.10", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, "node_modules/growl": { "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", "dev": true, "engines": { @@ -3131,7 +2743,6 @@ }, "node_modules/handlebars": { "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", "dev": true, "dependencies": { @@ -3152,7 +2763,6 @@ }, "node_modules/has": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, "dependencies": { @@ -3162,9 +2772,16 @@ "node": ">= 0.4.0" } }, + "node_modules/has-flag": { + "version": "4.0.0", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/he": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true, "bin": { @@ -3173,7 +2790,6 @@ }, "node_modules/html-encoding-sniffer": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", "dev": true, "dependencies": { @@ -3185,7 +2801,6 @@ }, "node_modules/http-proxy": { "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", "dev": true, "dependencies": { @@ -3198,9 +2813,8 @@ } }, "node_modules/http-server": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/http-server/-/http-server-14.1.0.tgz", - "integrity": "sha512-5lYsIcZtf6pdR8tCtzAHTWrAveo4liUlJdWc7YafwK/maPgYHs+VNP6KpCClmUnSorJrARVMXqtT055zBv11Yg==", + "version": "14.1.1", + "integrity": "sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==", "dev": true, "dependencies": { "basic-auth": "^2.0.1", @@ -3210,7 +2824,7 @@ "html-encoding-sniffer": "^3.0.0", "http-proxy": "^1.18.1", "mime": "^1.6.0", - "minimist": "^1.2.5", + "minimist": "^1.2.6", "opener": "^1.5.1", "portfinder": "^1.0.28", "secure-compare": "3.0.1", @@ -3224,79 +2838,8 @@ "node": ">=12" } }, - "node_modules/http-server/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/http-server/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/http-server/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/http-server/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/http-server/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/http-server/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/http-signature": { "version": "1.3.6", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz", "integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==", "dev": true, "dependencies": { @@ -3309,17 +2852,26 @@ } }, "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "version": "1.1.1", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", "dev": true, "engines": { - "node": ">=10.17.0" + "node": ">=8.12.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, "node_modules/ieee754": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "dev": true, "funding": [ @@ -3338,18 +2890,16 @@ ] }, "node_modules/ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "version": "5.2.0", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "dev": true, "engines": { "node": ">= 4" } }, "node_modules/import-local": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", - "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", + "version": "3.1.0", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", "dev": true, "dependencies": { "pkg-dir": "^4.2.0", @@ -3360,11 +2910,13 @@ }, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/indent-string": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true, "engines": { @@ -3373,8 +2925,7 @@ }, "node_modules/inflight": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, "dependencies": { "once": "^1.3.0", @@ -3383,13 +2934,11 @@ }, "node_modules/inherits": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, "node_modules/ini": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", "dev": true, "engines": { @@ -3398,7 +2947,6 @@ }, "node_modules/interpret": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", "dev": true, "engines": { @@ -3407,7 +2955,6 @@ }, "node_modules/is-binary-path": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, "dependencies": { @@ -3419,7 +2966,6 @@ }, "node_modules/is-buffer": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", "dev": true, "funding": [ @@ -3442,7 +2988,6 @@ }, "node_modules/is-ci": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", "dev": true, "dependencies": { @@ -3453,9 +2998,8 @@ } }, "node_modules/is-core-module": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", - "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", + "version": "2.10.0", + "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", "dev": true, "dependencies": { "has": "^1.0.3" @@ -3466,8 +3010,7 @@ }, "node_modules/is-extglob": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "engines": { "node": ">=0.10.0" @@ -3475,7 +3018,6 @@ }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, "engines": { @@ -3483,9 +3025,8 @@ } }, "node_modules/is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "version": "4.0.3", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "dependencies": { "is-extglob": "^2.1.1" @@ -3496,7 +3037,6 @@ }, "node_modules/is-installed-globally": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", "dev": true, "dependencies": { @@ -3512,7 +3052,6 @@ }, "node_modules/is-npm": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz", "integrity": "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==", "dev": true, "engines": { @@ -3524,7 +3063,6 @@ }, "node_modules/is-number": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, "engines": { @@ -3533,7 +3071,6 @@ }, "node_modules/is-path-inside": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true, "engines": { @@ -3541,17 +3078,18 @@ } }, "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "version": "4.1.0", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", "dev": true, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/is-plain-object": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, "dependencies": { @@ -3562,23 +3100,23 @@ } }, "node_modules/is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "version": "2.0.1", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/is-typedarray": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", "dev": true }, "node_modules/is-unicode-supported": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true, "engines": { @@ -3590,14 +3128,12 @@ }, "node_modules/isexe": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "node_modules/isobject": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", "dev": true, "engines": { "node": ">=0.10.0" @@ -3605,14 +3141,12 @@ }, "node_modules/isstream": { "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", "dev": true }, "node_modules/jest-worker": { - "version": "27.0.6", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.0.6.tgz", - "integrity": "sha512-qupxcj/dRuA3xHPMUd40gr2EaAurFbkwzOh7wfPaeE9id7hyjURRQoqNfHifHK3XjJU6YJJUQKILGUnwGPEOCA==", + "version": "27.5.1", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "dev": true, "dependencies": { "@types/node": "*", @@ -3623,34 +3157,9 @@ "node": ">= 10.13.0" } }, - "node_modules/jest-worker/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, "node_modules/js-yaml": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", - "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", + "version": "4.1.0", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "dependencies": { "argparse": "^2.0.1" @@ -3661,7 +3170,6 @@ }, "node_modules/js2xmlparser": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", "dev": true, "dependencies": { @@ -3670,14 +3178,12 @@ }, "node_modules/jsbn": { "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", "dev": true }, "node_modules/jsdoc": { - "version": "3.6.10", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.10.tgz", - "integrity": "sha512-IdQ8ppSo5LKZ9o3M+LKIIK8i00DIe5msDvG3G81Km+1dhy0XrOWD0Ji8H61ElgyEj/O9KRLokgKbAM9XX9CJAg==", + "version": "3.6.11", + "integrity": "sha512-8UCU0TYeIYD9KeLzEcAu2q8N/mx9O3phAGl32nmHlE0LpaJL71mMkP4d+QE5zWfNt50qheHtOZ0qoxVrsX5TUg==", "dev": true, "dependencies": { "@babel/parser": "^7.9.4", @@ -3686,7 +3192,7 @@ "catharsis": "^0.9.0", "escape-string-regexp": "^2.0.0", "js2xmlparser": "^4.0.2", - "klaw": "^4.0.1", + "klaw": "^3.0.0", "markdown-it": "^12.3.2", "markdown-it-anchor": "^8.4.1", "marked": "^4.0.10", @@ -3700,12 +3206,11 @@ "jsdoc": "jsdoc.js" }, "engines": { - "node": ">=8.15.0" + "node": ">=12.0.0" } }, "node_modules/jsdoc-api": { "version": "7.1.1", - "resolved": "https://registry.npmjs.org/jsdoc-api/-/jsdoc-api-7.1.1.tgz", "integrity": "sha512-0pkuPCzVXiqsDAsVrNFXCkHzlyNepBIDVtwwehry4RJAnZmXtlAz7rh8F9FRz53u3NeynGbex+bpYWwi8lE66A==", "dev": true, "dependencies": { @@ -3725,7 +3230,6 @@ }, "node_modules/jsdoc-parse": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsdoc-parse/-/jsdoc-parse-6.1.0.tgz", "integrity": "sha512-n/hDGQJa69IBun1yZAjqzV4gVR41+flZ3bIlm9fKvNe2Xjsd1/+zCo2+R9ls8LxtePgIWbpA1jU7xkB2lRdLLg==", "dev": true, "dependencies": { @@ -3742,7 +3246,6 @@ }, "node_modules/jsdoc-to-markdown": { "version": "7.1.1", - "resolved": "https://registry.npmjs.org/jsdoc-to-markdown/-/jsdoc-to-markdown-7.1.1.tgz", "integrity": "sha512-CI86d63xAVNO+ENumWwmJ034lYe5iGU5GwjtTA11EuphP9tpnoi4hrKgR/J8uME0D+o4KUpVfwX1fjZhc8dEtg==", "dev": true, "dependencies": { @@ -3763,45 +3266,36 @@ }, "node_modules/jsdoc/node_modules/escape-string-regexp": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", "dev": true, "engines": { "node": ">=8" } }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, "node_modules/json-schema": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", "dev": true }, "node_modules/json-schema-traverse": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, "node_modules/json-stringify-safe": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", "dev": true }, "node_modules/json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "version": "2.2.1", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, "bin": { "json5": "lib/cli.js" }, @@ -3811,7 +3305,6 @@ }, "node_modules/jsonfile": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, "dependencies": { @@ -3823,7 +3316,6 @@ }, "node_modules/jsprim": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==", "dev": true, "engines": [ @@ -3838,7 +3330,6 @@ }, "node_modules/kind-of": { "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true, "engines": { @@ -3846,18 +3337,16 @@ } }, "node_modules/klaw": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/klaw/-/klaw-4.0.1.tgz", - "integrity": "sha512-pgsE40/SvC7st04AHiISNewaIMUbY5V/K8b21ekiPiFoYs/EYSdsGa+FJArB1d441uq4Q8zZyIxvAzkGNlBdRw==", + "version": "3.0.0", + "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", "dev": true, - "engines": { - "node": ">=14.14.0" + "dependencies": { + "graceful-fs": "^4.1.9" } }, "node_modules/kleur": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.4.tgz", - "integrity": "sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA==", + "version": "4.1.5", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", "dev": true, "engines": { "node": ">=6" @@ -3865,8 +3354,7 @@ }, "node_modules/lazy-ass": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", - "integrity": "sha1-eZllXoZGwX8In90YfRUNMyTVRRM=", + "integrity": "sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==", "dev": true, "engines": { "node": "> 0.8" @@ -3874,7 +3362,6 @@ }, "node_modules/linkify-it": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", "dev": true, "dependencies": { @@ -3883,7 +3370,6 @@ }, "node_modules/listr2": { "version": "3.14.0", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.14.0.tgz", "integrity": "sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==", "dev": true, "dependencies": { @@ -3908,25 +3394,17 @@ } } }, - "node_modules/listr2/node_modules/colorette": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", - "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", - "dev": true - }, "node_modules/loader-runner": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", - "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", + "version": "4.3.0", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", "dev": true, "engines": { "node": ">=6.11.5" } }, "node_modules/loader-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", - "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "version": "2.0.2", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", "dev": true, "dependencies": { "big.js": "^5.2.2", @@ -3938,56 +3416,51 @@ } }, "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "version": "6.0.0", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "dependencies": { - "p-locate": "^4.1.0" + "p-locate": "^5.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/lodash": { "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, "node_modules/lodash.camelcase": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", "dev": true }, "node_modules/lodash.omit": { "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", - "integrity": "sha1-brGa5aHuHdnfC5aeZs4Lf6MLXmA=", + "integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==", "dev": true }, "node_modules/lodash.once": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", "dev": true }, "node_modules/lodash.padend": { "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz", - "integrity": "sha1-U8y6BH0G4VjTEfRdpiX05J5vFm4=", + "integrity": "sha512-sOQs2aqGpbl27tmCS1QNZA09Uqp01ZzWfDUoD+xzTii0E7dSQfRKcRetFwa+uXaxaqL+TKm7CgD2JdKP7aZBSw==", "dev": true }, "node_modules/lodash.pick": { "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", - "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=", + "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==", "dev": true }, "node_modules/log-symbols": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "dependencies": { @@ -4001,79 +3474,8 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/log-symbols/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/log-symbols/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/log-symbols/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/log-symbols/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/log-symbols/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/log-symbols/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/log-update": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", "dev": true, "dependencies": { @@ -4091,49 +3493,14 @@ }, "node_modules/log-update/node_modules/ansi-regex": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/log-update/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/log-update/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, + "dev": true, "engines": { - "node": ">=7.0.0" + "node": ">=8" } }, - "node_modules/log-update/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, "node_modules/log-update/node_modules/slice-ansi": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", "dev": true, "dependencies": { @@ -4150,7 +3517,6 @@ }, "node_modules/log-update/node_modules/strip-ansi": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "dependencies": { @@ -4162,7 +3528,6 @@ }, "node_modules/log-update/node_modules/wrap-ansi": { "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, "dependencies": { @@ -4174,15 +3539,24 @@ "node": ">=8" } }, + "node_modules/lru-cache": { + "version": "6.0.0", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/make-error": { "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, "node_modules/markdown-it": { "version": "12.3.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", "dev": true, "dependencies": { @@ -4197,9 +3571,8 @@ } }, "node_modules/markdown-it-anchor": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.4.1.tgz", - "integrity": "sha512-sLODeRetZ/61KkKLJElaU3NuU2z7MhXf12Ml1WJMSdwpngeofneCRF+JBbat8HiSqhniOMuTemXMrsI7hA6XyA==", + "version": "8.6.4", + "integrity": "sha512-Ul4YVYZNxMJYALpKtu+ZRdrryYt/GlQ5CK+4l1bp/gWXOG2QWElt6AqF3Mih/wfUKdZbNAZVXGR73/n6U/8img==", "dev": true, "peerDependencies": { "@types/markdown-it": "*", @@ -4207,9 +3580,8 @@ } }, "node_modules/marked": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.12.tgz", - "integrity": "sha512-hgibXWrEDNBWgGiK18j/4lkS6ihTe9sxtV4Q1OQppb/0zzyPSzoFANBa5MfsG/zgsWklmNnhm0XACZOH/0HBiQ==", + "version": "4.0.18", + "integrity": "sha512-wbLDJ7Zh0sqA0Vdg6aqlbT+yPxqLblpAZh1mK2+AO2twQkPywvvqQNfEPVwSSRjZ7dZcdeVBIAgiO7MMp3Dszw==", "dev": true, "bin": { "marked": "bin/marked.js" @@ -4220,7 +3592,6 @@ }, "node_modules/mdast-util-from-markdown": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.2.0.tgz", "integrity": "sha512-iZJyyvKD1+K7QX1b5jXdE7Sc5dtoTry1vzV28UZZe8Z1xVnB/czKntJ7ZAkG0tANqRnBF6p3p7GpU1y19DTf2Q==", "dev": true, "dependencies": { @@ -4244,7 +3615,6 @@ }, "node_modules/mdast-util-to-string": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.1.0.tgz", "integrity": "sha512-n4Vypz/DZgwo0iMHLQL49dJzlp7YtAJP+N07MZHpjPf/5XJuHUWstviF4Mn2jEiR/GNmtnRRqnwsXExk3igfFA==", "dev": true, "funding": { @@ -4254,19 +3624,16 @@ }, "node_modules/mdurl": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", "dev": true }, "node_modules/merge-stream": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, "node_modules/merge2": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, "engines": { @@ -4275,7 +3642,6 @@ }, "node_modules/micromark": { "version": "3.0.10", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.0.10.tgz", "integrity": "sha512-ryTDy6UUunOXy2HPjelppgJ2sNfcPz1pLlMdA6Rz9jPzhLikWXv/irpWV/I2jd68Uhmny7hHxAlAhk4+vWggpg==", "dev": true, "funding": [ @@ -4310,7 +3676,6 @@ }, "node_modules/micromark-core-commonmark": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.0.6.tgz", "integrity": "sha512-K+PkJTxqjFfSNkfAhp4GB+cZPfQd6dxtTXnf+RjZOV7T4EEXnvgzOcnp+eSTmpGk9d1S9sL6/lqrgSNn/s0HZA==", "dev": true, "funding": [ @@ -4344,7 +3709,6 @@ }, "node_modules/micromark-factory-destination": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.0.0.tgz", "integrity": "sha512-eUBA7Rs1/xtTVun9TmV3gjfPz2wEwgK5R5xcbIM5ZYAtvGF6JkyaDsj0agx8urXnO31tEO6Ug83iVH3tdedLnw==", "dev": true, "funding": [ @@ -4365,7 +3729,6 @@ }, "node_modules/micromark-factory-label": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.0.2.tgz", "integrity": "sha512-CTIwxlOnU7dEshXDQ+dsr2n+yxpP0+fn271pu0bwDIS8uqfFcumXpj5mLn3hSC8iw2MUr6Gx8EcKng1dD7i6hg==", "dev": true, "funding": [ @@ -4387,7 +3750,6 @@ }, "node_modules/micromark-factory-space": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.0.0.tgz", "integrity": "sha512-qUmqs4kj9a5yBnk3JMLyjtWYN6Mzfcx8uJfi5XAveBniDevmZasdGBba5b4QsvRcAkmvGo5ACmSUmyGiKTLZew==", "dev": true, "funding": [ @@ -4407,7 +3769,6 @@ }, "node_modules/micromark-factory-title": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.0.2.tgz", "integrity": "sha512-zily+Nr4yFqgMGRKLpTVsNl5L4PMu485fGFDOQJQBl2NFpjGte1e86zC0da93wf97jrc4+2G2GQudFMHn3IX+A==", "dev": true, "funding": [ @@ -4430,7 +3791,6 @@ }, "node_modules/micromark-factory-whitespace": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.0.0.tgz", "integrity": "sha512-Qx7uEyahU1lt1RnsECBiuEbfr9INjQTGa6Err+gF3g0Tx4YEviPbqqGKNv/NrBaE7dVHdn1bVZKM/n5I/Bak7A==", "dev": true, "funding": [ @@ -4452,7 +3812,6 @@ }, "node_modules/micromark-util-character": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.1.0.tgz", "integrity": "sha512-agJ5B3unGNJ9rJvADMJ5ZiYjBRyDpzKAOk01Kpi1TKhlT1APx3XZk6eN7RtSz1erbWHC2L8T3xLZ81wdtGRZzg==", "dev": true, "funding": [ @@ -4472,7 +3831,6 @@ }, "node_modules/micromark-util-chunked": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.0.0.tgz", "integrity": "sha512-5e8xTis5tEZKgesfbQMKRCyzvffRRUX+lK/y+DvsMFdabAicPkkZV6gO+FEWi9RfuKKoxxPwNL+dFF0SMImc1g==", "dev": true, "funding": [ @@ -4491,7 +3849,6 @@ }, "node_modules/micromark-util-classify-character": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.0.0.tgz", "integrity": "sha512-F8oW2KKrQRb3vS5ud5HIqBVkCqQi224Nm55o5wYLzY/9PwHGXC01tr3d7+TqHHz6zrKQ72Okwtvm/xQm6OVNZA==", "dev": true, "funding": [ @@ -4512,7 +3869,6 @@ }, "node_modules/micromark-util-combine-extensions": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.0.0.tgz", "integrity": "sha512-J8H058vFBdo/6+AsjHp2NF7AJ02SZtWaVUjsayNFeAiydTxUwViQPxN0Hf8dp4FmCQi0UUFovFsEyRSUmFH3MA==", "dev": true, "funding": [ @@ -4532,7 +3888,6 @@ }, "node_modules/micromark-util-decode-numeric-character-reference": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.0.0.tgz", "integrity": "sha512-OzO9AI5VUtrTD7KSdagf4MWgHMtET17Ua1fIpXTpuhclCqD8egFWo85GxSGvxgkGS74bEahvtM0WP0HjvV0e4w==", "dev": true, "funding": [ @@ -4551,7 +3906,6 @@ }, "node_modules/micromark-util-decode-string": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.0.2.tgz", "integrity": "sha512-DLT5Ho02qr6QWVNYbRZ3RYOSSWWFuH3tJexd3dgN1odEuPNxCngTCXJum7+ViRAd9BbdxCvMToPOD/IvVhzG6Q==", "dev": true, "funding": [ @@ -4573,7 +3927,6 @@ }, "node_modules/micromark-util-encode": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.0.1.tgz", "integrity": "sha512-U2s5YdnAYexjKDel31SVMPbfi+eF8y1U4pfiRW/Y8EFVCy/vgxk/2wWTxzcqE71LHtCuCzlBDRU2a5CQ5j+mQA==", "dev": true, "funding": [ @@ -4588,9 +3941,8 @@ ] }, "node_modules/micromark-util-html-tag-name": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.0.0.tgz", - "integrity": "sha512-NenEKIshW2ZI/ERv9HtFNsrn3llSPZtY337LID/24WeLqMzeZhBEE6BQ0vS2ZBjshm5n40chKtJ3qjAbVV8S0g==", + "version": "1.1.0", + "integrity": "sha512-BKlClMmYROy9UiV03SwNmckkjn8QHVaWkqoAqzivabvdGcwNGMMMH/5szAnywmsTBUzDsU57/mFi0sp4BQO6dA==", "dev": true, "funding": [ { @@ -4605,7 +3957,6 @@ }, "node_modules/micromark-util-normalize-identifier": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.0.0.tgz", "integrity": "sha512-yg+zrL14bBTFrQ7n35CmByWUTFsgst5JhA4gJYoty4Dqzj4Z4Fr/DHekSS5aLfH9bdlfnSvKAWsAgJhIbogyBg==", "dev": true, "funding": [ @@ -4624,7 +3975,6 @@ }, "node_modules/micromark-util-resolve-all": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.0.0.tgz", "integrity": "sha512-CB/AGk98u50k42kvgaMM94wzBqozSzDDaonKU7P7jwQIuH2RU0TeBqGYJz2WY1UdihhjweivStrJ2JdkdEmcfw==", "dev": true, "funding": [ @@ -4643,7 +3993,6 @@ }, "node_modules/micromark-util-sanitize-uri": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.0.0.tgz", "integrity": "sha512-cCxvBKlmac4rxCGx6ejlIviRaMKZc0fWm5HdCHEeDWRSkn44l6NdYVRyU+0nT1XC72EQJMZV8IPHF+jTr56lAg==", "dev": true, "funding": [ @@ -4664,7 +4013,6 @@ }, "node_modules/micromark-util-subtokenize": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.0.2.tgz", "integrity": "sha512-d90uqCnXp/cy4G881Ub4psE57Sf8YD0pim9QdjCRNjfas2M1u6Lbt+XZK9gnHL2XFhnozZiEdCa9CNfXSfQ6xA==", "dev": true, "funding": [ @@ -4686,7 +4034,6 @@ }, "node_modules/micromark-util-symbol": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.0.1.tgz", "integrity": "sha512-oKDEMK2u5qqAptasDAwWDXq0tG9AssVwAx3E9bBF3t/shRIGsWIRG+cGafs2p/SnDSOecnt6hZPCE2o6lHfFmQ==", "dev": true, "funding": [ @@ -4702,7 +4049,6 @@ }, "node_modules/micromark-util-types": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.0.2.tgz", "integrity": "sha512-DCfg/T8fcrhrRKTPjRrw/5LLvdGV7BHySf/1LOZx7TzWZdYRjogNtyNq885z3nNallwr3QUKARjqvHqX1/7t+w==", "dev": true, "funding": [ @@ -4716,37 +4062,13 @@ } ] }, - "node_modules/micromark/node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/micromark/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "version": "4.0.5", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" + "braces": "^3.0.2", + "picomatch": "^2.3.1" }, "engines": { "node": ">=8.6" @@ -4754,7 +4076,6 @@ }, "node_modules/mime": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "dev": true, "bin": { @@ -4765,21 +4086,17 @@ } }, "node_modules/mime-db": { - "version": "1.48.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz", - "integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==", - "dev": true, + "version": "1.52.0", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.31", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz", - "integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==", - "dev": true, + "version": "2.1.35", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dependencies": { - "mime-db": "1.48.0" + "mime-db": "1.52.0" }, "engines": { "node": ">= 0.6" @@ -4787,7 +4104,6 @@ }, "node_modules/mimic-fn": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true, "engines": { @@ -4795,9 +4111,8 @@ } }, "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { "brace-expansion": "^1.1.7" @@ -4808,14 +4123,12 @@ }, "node_modules/minimist": { "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, "node_modules/minipass": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", - "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", + "version": "3.3.4", + "integrity": "sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw==", "dev": true, "dependencies": { "yallist": "^4.0.0" @@ -4826,7 +4139,6 @@ }, "node_modules/minizlib": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", "dev": true, "dependencies": { @@ -4839,7 +4151,6 @@ }, "node_modules/mkdirp": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true, "bin": { @@ -4851,14 +4162,12 @@ }, "node_modules/mkdirp2": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/mkdirp2/-/mkdirp2-1.0.5.tgz", "integrity": "sha512-xOE9xbICroUDmG1ye2h4bZ8WBie9EGmACaco8K8cx6RlkJJrxGIqjGqztAI+NMhexXBcdGbSEzI6N3EJPevxZw==", "dev": true }, "node_modules/mocha": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.0.tgz", - "integrity": "sha512-kNn7E8g2SzVcq0a77dkphPsDSN7P+iYkqE0ZsGCYWRsoiKjOt+NvXfaagik8vuDa6W5Zw3qxe8Jfpt5qKf+6/Q==", + "version": "9.2.2", + "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", "dev": true, "dependencies": { "@ungap/promise-all-settled": "1.1.2", @@ -4874,9 +4183,9 @@ "he": "1.2.0", "js-yaml": "4.1.0", "log-symbols": "4.1.0", - "minimatch": "3.0.4", + "minimatch": "4.2.1", "ms": "2.1.3", - "nanoid": "3.2.0", + "nanoid": "3.3.1", "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", @@ -4898,9 +4207,16 @@ "url": "https://opencollective.com/mochajs" } }, + "node_modules/mocha/node_modules/ansi-colors": { + "version": "4.1.1", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/mocha/node_modules/debug": { "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "dev": true, "dependencies": { @@ -4917,13 +4233,11 @@ }, "node_modules/mocha/node_modules/debug/node_modules/ms": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, "node_modules/mocha/node_modules/escape-string-regexp": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "engines": { @@ -4933,125 +4247,106 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mocha/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "node_modules/mocha/node_modules/glob": { + "version": "7.2.0", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, "engines": { - "node": ">=10" + "node": "*" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mocha/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/mocha/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "node_modules/mocha/node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { - "argparse": "^2.0.1" + "brace-expansion": "^1.1.7" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": "*" } }, - "node_modules/mocha/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "node_modules/mocha/node_modules/minimatch": { + "version": "4.2.1", + "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", "dev": true, "dependencies": { - "p-locate": "^5.0.0" + "brace-expansion": "^1.1.7" }, "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/mocha/node_modules/ms": { "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, - "node_modules/mocha/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/mocha/node_modules/serialize-javascript": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", "dev": true, "dependencies": { "randombytes": "^2.1.0" } }, - "node_modules/mocha/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "node_modules/mocha/node_modules/yargs": { + "version": "16.2.0", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/mocha/node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true, "engines": { "node": ">=10" } }, "node_modules/mri": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", "dev": true, "engines": { "node": ">=4" } }, + "node_modules/ms": { + "version": "2.1.2", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/mylas": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/mylas/-/mylas-2.1.11.tgz", + "integrity": "sha512-krnPUl3n9/k52FGCltWMYcqp9SttxjRJEy0sWLk+g7mIa7wnZrmNSZ40Acx7ghzRSOsxt2rEqMbaq4jWlnTDKg==", + "dev": true, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/raouldeheer" + } + }, "node_modules/nanoid": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz", - "integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==", + "version": "3.3.1", + "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", "dev": true, "bin": { "nanoid": "bin/nanoid.cjs" @@ -5062,13 +4357,11 @@ }, "node_modules/neo-async": { "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, "node_modules/node-fetch": { "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", "dependencies": { "whatwg-url": "^5.0.0" @@ -5085,34 +4378,13 @@ } } }, - "node_modules/node-fetch/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" - }, - "node_modules/node-fetch/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" - }, - "node_modules/node-fetch/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/node-releases": { - "version": "1.1.73", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.73.tgz", - "integrity": "sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg==", + "version": "2.0.6", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", "dev": true }, "node_modules/normalize-path": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, "engines": { @@ -5121,7 +4393,6 @@ }, "node_modules/npm-run-path": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, "dependencies": { @@ -5133,13 +4404,11 @@ }, "node_modules/object-get": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/object-get/-/object-get-2.1.1.tgz", "integrity": "sha512-7n4IpLMzGGcLEMiQKsNR7vCe+N5E9LORFrtNUVy4sO3dj9a3HedZCxEL2T7QuLhcHN1NBuBsMOKaOsAYI9IIvg==", "dev": true }, "node_modules/object-to-spawn-args": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/object-to-spawn-args/-/object-to-spawn-args-2.0.1.tgz", "integrity": "sha512-6FuKFQ39cOID+BMZ3QaphcC8Y4cw6LXBLyIgPU+OhIYwviJamPAn+4mITapnSBQrejB+NNp+FMskhD8Cq+Ys3w==", "dev": true, "engines": { @@ -5148,8 +4417,7 @@ }, "node_modules/once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "dependencies": { "wrappy": "1" @@ -5157,7 +4425,6 @@ }, "node_modules/onetime": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, "dependencies": { @@ -5172,7 +4439,6 @@ }, "node_modules/opener": { "version": "1.5.2", - "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", "dev": true, "bin": { @@ -5181,13 +4447,11 @@ }, "node_modules/ospath": { "version": "1.2.2", - "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz", - "integrity": "sha1-EnZjl3Sj+O8lcvf+QoDg6kVQwHs=", + "integrity": "sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==", "dev": true }, "node_modules/p-limit": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "dependencies": { @@ -5201,27 +4465,14 @@ } }, "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-locate/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "version": "5.0.0", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "dependencies": { - "p-try": "^2.0.0" + "p-limit": "^3.0.2" }, "engines": { - "node": ">=6" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -5229,7 +4480,6 @@ }, "node_modules/p-map": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", "dev": true, "dependencies": { @@ -5244,7 +4494,6 @@ }, "node_modules/p-try": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true, "engines": { @@ -5253,7 +4502,6 @@ }, "node_modules/path-exists": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "engines": { @@ -5262,8 +4510,7 @@ }, "node_modules/path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, "engines": { "node": ">=0.10.0" @@ -5271,7 +4518,6 @@ }, "node_modules/path-key": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, "engines": { @@ -5280,13 +4526,11 @@ }, "node_modules/path-parse": { "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, "node_modules/path-type": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true, "engines": { @@ -5295,20 +4539,22 @@ }, "node_modules/pend": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", "dev": true }, "node_modules/performance-now": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "dev": true + }, + "node_modules/picocolors": { + "version": "1.0.0", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "dev": true }, "node_modules/picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "version": "2.3.1", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "engines": { "node": ">=8.6" @@ -5317,9 +4563,16 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pify": { + "version": "2.3.0", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/pkg-dir": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, "dependencies": { @@ -5329,23 +4582,86 @@ "node": ">=8" } }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/plimit-lit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/plimit-lit/-/plimit-lit-1.3.0.tgz", + "integrity": "sha512-63qOoSzggsjBHPVbaKeEtsO8oWTeyJxUfVRLkoc59pRkDiCCLvqn2tCgAkfbejrx+V5zJtlG2wqkk/Db6cwlVQ==", + "dev": true, + "dependencies": { + "queue-lit": "^1.3.0" + } + }, "node_modules/portfinder": { - "version": "1.0.28", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", - "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", + "version": "1.0.29", + "integrity": "sha512-Z5+DarHWCKlufshB9Z1pN95oLtANoY5Wn9X3JGELGyQ6VhEcBfT2t+1fGUBq7MwUant6g/mqowH+4HifByPbiQ==", "dev": true, "dependencies": { - "async": "^2.6.2", - "debug": "^3.1.1", - "mkdirp": "^0.5.5" + "async": "^2.6.4", + "debug": "^3.2.7", + "mkdirp": "^0.5.6" }, "engines": { "node": ">= 0.12.0" } }, + "node_modules/portfinder/node_modules/async": { + "version": "2.6.4", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dev": true, + "dependencies": { + "lodash": "^4.17.14" + } + }, "node_modules/portfinder/node_modules/debug": { "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "dependencies": { @@ -5353,26 +4669,18 @@ } }, "node_modules/portfinder/node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "version": "0.5.6", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, "dependencies": { - "minimist": "^1.2.5" + "minimist": "^1.2.6" }, "bin": { "mkdirp": "bin/cmd.js" } }, - "node_modules/portfinder/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, "node_modules/pretty-bytes": { "version": "5.6.0", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", "dev": true, "engines": { @@ -5384,19 +4692,16 @@ }, "node_modules/proxy-from-env": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", - "integrity": "sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=", + "integrity": "sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==", "dev": true }, "node_modules/psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "version": "1.9.0", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", "dev": true }, "node_modules/pump": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, "dependencies": { @@ -5406,7 +4711,6 @@ }, "node_modules/punycode": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true, "engines": { @@ -5414,27 +4718,21 @@ } }, "node_modules/qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "version": "6.5.3", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", "dev": true, "engines": { "node": ">=0.6" } }, - "node_modules/querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", - "dev": true, - "engines": { - "node": ">=0.4.x" - } + "node_modules/queue-lit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/queue-lit/-/queue-lit-1.3.0.tgz", + "integrity": "sha512-3HpQ7bD2ct56qPithPr5IzH2Oij3WVPcgevCJ+mI9HAfVJKCqQ2yiFYgb4AUkLfsCmmbVDy9luvE97Ztzr5eBg==", + "dev": true }, "node_modules/queue-microtask": { "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true, "funding": [ @@ -5454,7 +4752,6 @@ }, "node_modules/randombytes": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, "dependencies": { @@ -5463,7 +4760,6 @@ }, "node_modules/readdirp": { "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "dependencies": { @@ -5474,9 +4770,8 @@ } }, "node_modules/rechoir": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.0.tgz", - "integrity": "sha512-ADsDEH2bvbjltXEP+hTIAmeFekTFK0V2BTxMkok6qILyAJEXV0AFfoWcAq4yfll5VdIMd/RVXq0lR+wQi5ZU3Q==", + "version": "0.7.1", + "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", "dev": true, "dependencies": { "resolve": "^1.9.0" @@ -5487,8 +4782,7 @@ }, "node_modules/reduce-extract": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/reduce-extract/-/reduce-extract-1.0.0.tgz", - "integrity": "sha1-Z/I4W+2mUGG19fQxJmLosIDKFSU=", + "integrity": "sha512-QF8vjWx3wnRSL5uFMyCjDeDc5EBMiryoT9tz94VvgjKfzecHAVnqmXAwQDcr7X4JmLc2cjkjFGCVzhMqDjgR9g==", "dev": true, "dependencies": { "test-value": "^1.0.1" @@ -5499,8 +4793,7 @@ }, "node_modules/reduce-extract/node_modules/array-back": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz", - "integrity": "sha1-ZEun8JX3/898Q7Xw3DnTwfA8Bjs=", + "integrity": "sha512-1WxbZvrmyhkNoeYcizokbmh5oiOCIfyvGtcqbK3Ls1v1fKcquzxnQSceOx6tzq7jmai2kFLWIpGND2cLhH6TPw==", "dev": true, "dependencies": { "typical": "^2.6.0" @@ -5511,8 +4804,7 @@ }, "node_modules/reduce-extract/node_modules/test-value": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/test-value/-/test-value-1.1.0.tgz", - "integrity": "sha1-oJE29y7AQ9J8iTcHwrFZv6196T8=", + "integrity": "sha512-wrsbRo7qP+2Je8x8DsK8ovCGyxe3sYfQwOraIY/09A2gFXU9DYKiTF14W4ki/01AEh56kMzAmlj9CaHGDDUBJA==", "dev": true, "dependencies": { "array-back": "^1.0.2", @@ -5523,17 +4815,15 @@ } }, "node_modules/reduce-flatten": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-1.0.1.tgz", - "integrity": "sha1-JYx479FT3fk8tWEjf2EYTzaW4yc=", + "version": "3.0.1", + "integrity": "sha512-bYo+97BmUUOzg09XwfkwALt4PQH1M5L0wzKerBt6WLm3Fhdd43mMS89HiT1B9pJIqko/6lWx3OnV4J9f2Kqp5Q==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/reduce-unique": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/reduce-unique/-/reduce-unique-2.0.1.tgz", "integrity": "sha512-x4jH/8L1eyZGR785WY+ePtyMNhycl1N2XOLxhCbzZFaqF4AXjLzqSxa2UHgJ2ZVR/HHyPOvl1L7xRnW8ye5MdA==", "dev": true, "engines": { @@ -5542,8 +4832,7 @@ }, "node_modules/reduce-without": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/reduce-without/-/reduce-without-1.0.1.tgz", - "integrity": "sha1-aK0OrRGFXJo31OglbBW7+Hly/Iw=", + "integrity": "sha512-zQv5y/cf85sxvdrKPlfcRzlDn/OqKFThNimYmsS3flmkioKvkUGn2Qg9cJVoQiEvdxFGLE0MQER/9fZ9sUqdxg==", "dev": true, "dependencies": { "test-value": "^2.0.0" @@ -5554,8 +4843,7 @@ }, "node_modules/reduce-without/node_modules/array-back": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz", - "integrity": "sha1-ZEun8JX3/898Q7Xw3DnTwfA8Bjs=", + "integrity": "sha512-1WxbZvrmyhkNoeYcizokbmh5oiOCIfyvGtcqbK3Ls1v1fKcquzxnQSceOx6tzq7jmai2kFLWIpGND2cLhH6TPw==", "dev": true, "dependencies": { "typical": "^2.6.0" @@ -5566,8 +4854,7 @@ }, "node_modules/reduce-without/node_modules/test-value": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/test-value/-/test-value-2.1.0.tgz", - "integrity": "sha1-Edpv9nDzRxpztiXKTz/c97t0gpE=", + "integrity": "sha512-+1epbAxtKeXttkGFMTX9H42oqzOTufR1ceCF+GYA5aOmvaPq9wd4PUS8329fn2RRLGNeUkgRLnVpycjx8DsO2w==", "dev": true, "dependencies": { "array-back": "^1.0.3", @@ -5579,7 +4866,6 @@ }, "node_modules/remark-parse": { "version": "10.0.1", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-10.0.1.tgz", "integrity": "sha512-1fUyHr2jLsVOkhbvPRBJ5zTKZZyD6yZzYaWCS6BPBdQ8vEMBCH+9zNCDA6tET/zHCi/jLqjCWtlJZUPk+DbnFw==", "dev": true, "dependencies": { @@ -5594,8 +4880,7 @@ }, "node_modules/request-progress": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", - "integrity": "sha1-TKdUCBx/7GP1BeT6qCWqBs1mnb4=", + "integrity": "sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==", "dev": true, "dependencies": { "throttleit": "^1.0.0" @@ -5603,8 +4888,7 @@ }, "node_modules/require-directory": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, "engines": { "node": ">=0.10.0" @@ -5612,19 +4896,16 @@ }, "node_modules/require-main-filename": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, "node_modules/requires-port": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "dev": true }, "node_modules/requizzle": { "version": "0.2.3", - "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.3.tgz", "integrity": "sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==", "dev": true, "dependencies": { @@ -5632,13 +4913,16 @@ } }, "node_modules/resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "version": "1.22.1", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", "dev": true, "dependencies": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5646,7 +4930,6 @@ }, "node_modules/resolve-cwd": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, "dependencies": { @@ -5658,7 +4941,6 @@ }, "node_modules/resolve-from": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, "engines": { @@ -5667,7 +4949,6 @@ }, "node_modules/restore-cursor": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "dev": true, "dependencies": { @@ -5680,7 +4961,6 @@ }, "node_modules/reusify": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true, "engines": { @@ -5690,13 +4970,11 @@ }, "node_modules/rfdc": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", "dev": true }, "node_modules/rimraf": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, "dependencies": { @@ -5711,7 +4989,6 @@ }, "node_modules/run-parallel": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, "funding": [ @@ -5733,9 +5010,8 @@ } }, "node_modules/rxjs": { - "version": "7.5.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.2.tgz", - "integrity": "sha512-PwDt186XaL3QN5qXj/H9DGyHhP3/RYYgZZwqBv9Tv8rsAaiwFH1IsJJlcgD37J7UW5a6O67qX0KWKS3/pu0m4w==", + "version": "7.5.6", + "integrity": "sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw==", "dev": true, "dependencies": { "tslib": "^2.1.0" @@ -5743,7 +5019,6 @@ }, "node_modules/sade": { "version": "1.8.1", - "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", "dev": true, "dependencies": { @@ -5754,24 +5029,35 @@ } }, "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "version": "5.2.1", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, "node_modules/safer-buffer": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, "node_modules/schema-utils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.0.tgz", - "integrity": "sha512-tTEaeYkyIhEZ9uWgAjDerWov3T9MgX8dhhy2r0IGeeX4W8ngtGl1++dUve/RUqzuaASSh7shwCDJjEzthxki8w==", + "version": "3.1.1", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", "dev": true, "dependencies": { - "@types/json-schema": "^7.0.7", + "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", "ajv-keywords": "^3.5.2" }, @@ -5785,13 +5071,25 @@ }, "node_modules/secure-compare": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", - "integrity": "sha1-8aAymzCLIh+uN7mXTz1XjQypmeM=", + "integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==", "dev": true }, + "node_modules/semver": { + "version": "7.3.7", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/serialize-javascript": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", "dev": true, "dependencies": { @@ -5800,13 +5098,11 @@ }, "node_modules/set-blocking": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, "node_modules/shallow-clone": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", "dev": true, "dependencies": { @@ -5818,7 +5114,6 @@ }, "node_modules/shebang-command": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "dependencies": { @@ -5830,22 +5125,24 @@ }, "node_modules/shebang-regex": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, "engines": { "node": ">=8" } }, + "node_modules/shell-quote": { + "version": "1.7.3", + "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==", + "dev": true + }, "node_modules/signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "version": "3.0.7", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, "node_modules/slash": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, "engines": { @@ -5854,7 +5151,6 @@ }, "node_modules/slice-ansi": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", "dev": true, "dependencies": { @@ -5866,43 +5162,9 @@ "node": ">=8" } }, - "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/slice-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/slice-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, "node_modules/sort-array": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/sort-array/-/sort-array-4.1.4.tgz", - "integrity": "sha512-GVFN6Y1sHKrWaSYOJTk9093ZnrBMc9sP3nuhANU44S4xg3rE6W5Z5WyamuT8VpMBbssnetx5faKCua0LEmUnSw==", + "version": "4.1.5", + "integrity": "sha512-Ya4peoS1fgFN42RN1REk2FgdNOeLIEMKFGJvs7VTP3OklF8+kl2SkpVliZ4tk/PurWsrWRsdNdU+tgyOBkB9sA==", "dev": true, "dependencies": { "array-back": "^5.0.0", @@ -5914,7 +5176,6 @@ }, "node_modules/sort-array/node_modules/array-back": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-5.0.0.tgz", "integrity": "sha512-kgVWwJReZWmVuWOQKEOohXKJX+nD02JAZ54D1RRWlv8L0NebauKAaFxACKzB74RTclt1+WNz5KHaLRDAPZbDEw==", "dev": true, "engines": { @@ -5923,7 +5184,6 @@ }, "node_modules/sort-array/node_modules/typical": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/typical/-/typical-6.0.1.tgz", "integrity": "sha512-+g3NEp7fJLe9DPa1TArHm9QAA7YciZmWnfAqEaFrBihQ7epOv9i99rjtgb6Iz0wh3WuQDjsCTDfgRoGnmHN81A==", "dev": true, "engines": { @@ -5932,7 +5192,6 @@ }, "node_modules/source-map": { "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "engines": { @@ -5941,7 +5200,6 @@ }, "node_modules/source-map-support": { "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "dependencies": { @@ -5951,13 +5209,11 @@ }, "node_modules/spawn-command": { "version": "0.0.2-1", - "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz", - "integrity": "sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A=", + "integrity": "sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg==", "dev": true }, "node_modules/sshpk": { "version": "1.17.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", "dev": true, "dependencies": { @@ -5982,8 +5238,7 @@ }, "node_modules/stream-connect": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/stream-connect/-/stream-connect-1.0.2.tgz", - "integrity": "sha1-GLyB8u2zW4tdmoAJIAqYUxRCipc=", + "integrity": "sha512-68Kl+79cE0RGKemKkhxTSg8+6AGrqBt+cbZAXevg2iJ6Y3zX4JhA/sZeGzLpxW9cXhmqAcE7KnJCisUmIUfnFQ==", "dev": true, "dependencies": { "array-back": "^1.0.2" @@ -5994,8 +5249,7 @@ }, "node_modules/stream-connect/node_modules/array-back": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz", - "integrity": "sha1-ZEun8JX3/898Q7Xw3DnTwfA8Bjs=", + "integrity": "sha512-1WxbZvrmyhkNoeYcizokbmh5oiOCIfyvGtcqbK3Ls1v1fKcquzxnQSceOx6tzq7jmai2kFLWIpGND2cLhH6TPw==", "dev": true, "dependencies": { "typical": "^2.6.0" @@ -6006,7 +5260,6 @@ }, "node_modules/stream-via": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/stream-via/-/stream-via-1.0.4.tgz", "integrity": "sha512-DBp0lSvX5G9KGRDTkR/R+a29H+Wk2xItOF+MpZLLNDWbEV9tGPnqLPxHEYjmiz8xGtJHRIqmI+hCjmNzqoA4nQ==", "dev": true, "engines": { @@ -6015,7 +5268,6 @@ }, "node_modules/string-width": { "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "dependencies": { @@ -6029,7 +5281,6 @@ }, "node_modules/string-width/node_modules/ansi-regex": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "engines": { @@ -6038,7 +5289,6 @@ }, "node_modules/string-width/node_modules/strip-ansi": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "dependencies": { @@ -6048,22 +5298,9 @@ "node": ">=8" } }, - "node_modules/strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "dependencies": { - "ansi-regex": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/strip-bom": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, "optional": true, "engines": { @@ -6072,7 +5309,6 @@ }, "node_modules/strip-final-newline": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true, "engines": { @@ -6081,7 +5317,6 @@ }, "node_modules/strip-json-comments": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, "engines": { @@ -6091,9 +5326,33 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/supports-color": { + "version": "8.1.1", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/table-layout": { "version": "0.4.5", - "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-0.4.5.tgz", "integrity": "sha512-zTvf0mcggrGeTe/2jJ6ECkJHAQPIYEwDoqsiqBjI24mvRmQbInK5jq33fyypaCBxX08hMkfmdOqj6haT33EqWw==", "dev": true, "dependencies": { @@ -6109,7 +5368,6 @@ }, "node_modules/table-layout/node_modules/array-back": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", "dev": true, "dependencies": { @@ -6121,13 +5379,11 @@ }, "node_modules/taffydb": { "version": "2.6.2", - "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", - "integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=", + "integrity": "sha512-y3JaeRSplks6NYQuCOj3ZFMO3j60rTwbuKCvZxsAraGYH2epusatvZ0baZYA01WsGqJBq/Dl6vOrMUJqyMj8kA==", "dev": true }, "node_modules/tapable": { "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "dev": true, "engines": { @@ -6136,7 +5392,6 @@ }, "node_modules/tar": { "version": "6.1.11", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", "dev": true, "dependencies": { @@ -6151,15 +5406,21 @@ "node": ">= 10" } }, + "node_modules/tar/node_modules/chownr": { + "version": "2.0.0", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/temp-path": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/temp-path/-/temp-path-1.0.0.tgz", - "integrity": "sha1-JLFUOXOrRCiW2a02fdnL2/r+kYs=", + "integrity": "sha512-TvmyH7kC6ZVTYkqCODjJIbgvu0FKiwQpZ4D1aknE7xpcDf/qEOB8KZEK5ef2pfbVoiBhNWs3yx4y+ESMtNYmlg==", "dev": true }, "node_modules/terser": { "version": "5.14.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", "dev": true, "dependencies": { @@ -6176,17 +5437,15 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.1.4.tgz", - "integrity": "sha512-C2WkFwstHDhVEmsmlCxrXUtVklS+Ir1A7twrYzrDrQQOIMOaVAYykaoo/Aq1K0QRkMoY2hhvDQY1cm4jnIMFwA==", + "version": "5.3.3", + "integrity": "sha512-Fx60G5HNYknNTNQnzQ1VePRuu89ZVYWfjRAeT5rITuCY/1b08s49e5kSQwHDirKZWuoKOBRFS98EUUoZ9kLEwQ==", "dev": true, "dependencies": { - "jest-worker": "^27.0.2", - "p-limit": "^3.1.0", - "schema-utils": "^3.0.0", + "@jridgewell/trace-mapping": "^0.3.7", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", "serialize-javascript": "^6.0.0", - "source-map": "^0.6.1", - "terser": "^5.7.0" + "terser": "^5.7.2" }, "engines": { "node": ">= 10.13.0" @@ -6197,20 +5456,34 @@ }, "peerDependencies": { "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } } }, "node_modules/terser-webpack-plugin/node_modules/serialize-javascript": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", "dev": true, "dependencies": { "randombytes": "^2.1.0" } }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, "node_modules/test-value": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/test-value/-/test-value-3.0.0.tgz", "integrity": "sha512-sVACdAWcZkSU9x7AOmJo5TqE+GyNJknHaHsMrR6ZnhjVlVN9Yx6FjHrsKZ3BjIpPCT68zYesPWkakrNupwfOTQ==", "dev": true, "dependencies": { @@ -6223,7 +5496,6 @@ }, "node_modules/test-value/node_modules/array-back": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", "dev": true, "dependencies": { @@ -6233,21 +5505,25 @@ "node": ">=4" } }, + "node_modules/text-encoding": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.7.0.tgz", + "integrity": "sha512-oJQ3f1hrOnbRLOcwKz0Liq2IcrvDeZRHXhd9RgLrsT+DjWY/nty1Hi7v3dtkaEYbPYe0mUoOfzRrMwfXXwgPUA==", + "deprecated": "no longer maintained", + "peer": true + }, "node_modules/throttleit": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz", - "integrity": "sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw=", + "integrity": "sha512-rkTVqu6IjfQ/6+uNuuc3sZek4CEYxTJom3IktzgdSxcZqdARuebbA/f4QmAxMQIxqq9ZLEUkSYqvuk1I6VKq4g==", "dev": true }, "node_modules/through": { "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, "node_modules/tmp": { "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", "dev": true, "dependencies": { @@ -6259,7 +5535,6 @@ }, "node_modules/to-regex-range": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "dependencies": { @@ -6271,7 +5546,6 @@ }, "node_modules/tough-cookie": { "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", "dev": true, "dependencies": { @@ -6282,9 +5556,12 @@ "node": ">=0.8" } }, + "node_modules/tr46": { + "version": "0.0.3", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, "node_modules/tree-kill": { "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", "dev": true, "bin": { @@ -6292,9 +5569,8 @@ } }, "node_modules/trough": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/trough/-/trough-2.0.2.tgz", - "integrity": "sha512-FnHq5sTMxC0sk957wHDzRnemFnNBvt/gSY99HzK8F7UP5WAbvP70yX5bd7CjEQkN+TjdxwI7g7lJ6podqrG2/w==", + "version": "2.1.0", + "integrity": "sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==", "dev": true, "funding": { "type": "github", @@ -6303,7 +5579,6 @@ }, "node_modules/ts-mocha": { "version": "9.0.2", - "resolved": "https://registry.npmjs.org/ts-mocha/-/ts-mocha-9.0.2.tgz", "integrity": "sha512-WyQjvnzwrrubl0JT7EC1yWmNpcsU3fOuBFfdps30zbmFBgKniSaSOyZMZx+Wq7kytUs5CY+pEbSYEbGfIKnXTw==", "dev": true, "dependencies": { @@ -6324,20 +5599,31 @@ }, "node_modules/ts-mocha/node_modules/diff": { "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", "dev": true, "engines": { "node": ">=0.3.1" } }, + "node_modules/ts-mocha/node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "optional": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, "node_modules/ts-mocha/node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "version": "0.5.6", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, "dependencies": { - "minimist": "^1.2.5" + "minimist": "^1.2.6" }, "bin": { "mkdirp": "bin/cmd.js" @@ -6345,7 +5631,6 @@ }, "node_modules/ts-mocha/node_modules/ts-node": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz", "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==", "dev": true, "dependencies": { @@ -6365,22 +5650,33 @@ "node": ">=4.2.0" } }, + "node_modules/ts-mocha/node_modules/tsconfig-paths": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "dev": true, + "optional": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, "node_modules/ts-mocha/node_modules/yn": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", - "integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=", + "integrity": "sha512-uTv8J/wiWTgUTg+9vLTi//leUl5vDQS6uii/emeTb2ssY7vl6QWf2fFbIIGjnhjvbdKlU0ed7QPgY1htTC86jQ==", "dev": true, "engines": { "node": ">=4" } }, "node_modules/ts-node": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.5.0.tgz", - "integrity": "sha512-6kEJKwVxAJ35W4akuiysfKwKmjkbYxwQMTBaAxo9KKAx/Yd26mPUyhGz3ji+EsJoAgrLqVsYHNuuYwQe22lbtw==", + "version": "10.9.1", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", "dev": true, "dependencies": { - "@cspotcode/source-map-support": "0.7.0", + "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", "@tsconfig/node12": "^1.0.7", "@tsconfig/node14": "^1.0.0", @@ -6391,12 +5687,13 @@ "create-require": "^1.1.0", "diff": "^4.0.1", "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.0", + "v8-compile-cache-lib": "^3.0.1", "yn": "3.1.1" }, "bin": { "ts-node": "dist/bin.js", "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", "ts-node-script": "dist/bin-script.js", "ts-node-transpile-only": "dist/bin-transpile.js", "ts-script": "dist/bin-script-deprecated.js" @@ -6418,49 +5715,46 @@ }, "node_modules/ts-node/node_modules/diff": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true, "engines": { "node": ">=0.3.1" } }, - "node_modules/tsconfig-paths": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz", - "integrity": "sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg==", + "node_modules/tsc-alias": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/tsc-alias/-/tsc-alias-1.7.0.tgz", + "integrity": "sha512-n/K6g8S7Ec7Y/A2Z77Ikp2Uv1S1ERtT63ni69XV4W1YPT4rnNmz8ItgIiJYvKfFnKfqcZQ81UPjoKpMTxaC/rg==", "dev": true, - "optional": true, "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", - "minimist": "^1.2.0", - "strip-bom": "^3.0.0" + "chokidar": "^3.5.3", + "commander": "^9.0.0", + "globby": "^11.0.4", + "mylas": "^2.1.9", + "normalize-path": "^3.0.0", + "plimit-lit": "^1.2.6" + }, + "bin": { + "tsc-alias": "dist/bin/index.js" } }, - "node_modules/tsconfig-paths/node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "node_modules/tsc-alias/node_modules/commander": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.0.tgz", + "integrity": "sha512-sRPT+umqkz90UA8M1yqYfnHlZA7fF6nSphDtxeywPZ49ysjxDQybzk13CL+mXekDRG92skbcqCLVovuCusNmFw==", "dev": true, - "optional": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" + "engines": { + "node": "^12.20.0 || >=14" } }, "node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "version": "2.4.0", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", "dev": true }, "node_modules/tunnel-agent": { "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", "dev": true, "dependencies": { "safe-buffer": "^5.0.1" @@ -6471,14 +5765,12 @@ }, "node_modules/tweetnacl": { "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", "dev": true }, "node_modules/txm": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/txm/-/txm-8.0.1.tgz", - "integrity": "sha512-unvGVsgWIP10c3KYrfBMdBWILWsIjnDV4ljO32Juk1qpTzbZCNlpkjGMb+ix6VDH49JKa2V6gSzBnx0gzypp7A==", + "version": "8.0.2", + "integrity": "sha512-X6gUOeUAXzIrUvxp0zvOiJBM0l6Zk8NexaYugac6c6PW7dbrjgz6zC/rfSEjtd29VejI3VKvAnc6pDdOfSNKug==", "dev": true, "dependencies": { "async": "^3.2.1", @@ -6495,16 +5787,9 @@ "node": ">=16.0.0" } }, - "node_modules/txm/node_modules/async": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", - "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==", - "dev": true - }, "node_modules/txm/node_modules/supports-color": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.2.1.tgz", - "integrity": "sha512-Obv7ycoCTG51N7y175StI9BlAXrmgZrFhZOb0/PyjHBher/NmsdBgbbQ1Inhq+gIhz6+7Gb+jWF2Vqi7Mf1xnQ==", + "version": "9.2.2", + "integrity": "sha512-XC6g/Kgux+rJXmwokjm9ECpD6k/smUoS5LKlUCcsYr4IY3rW0XyAympon2RmxGrlnZURMpg5T18gWDP9CsHXFA==", "dev": true, "engines": { "node": ">=12" @@ -6515,7 +5800,6 @@ }, "node_modules/type-fest": { "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true, "engines": { @@ -6526,9 +5810,8 @@ } }, "node_modules/typescript": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.2.tgz", - "integrity": "sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg==", + "version": "4.7.4", + "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -6540,20 +5823,17 @@ }, "node_modules/typical": { "version": "2.6.1", - "resolved": "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz", - "integrity": "sha1-XAgOXWYcu+OCWdLnCjxyU+hziB0=", + "integrity": "sha512-ofhi8kjIje6npGozTip9Fr8iecmYfEbS06i0JnIg+rh51KakryWF4+jX8lLKZVhy6N+ID45WYSFCxPOdTWCzNg==", "dev": true }, "node_modules/uc.micro": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", "dev": true }, "node_modules/uglify-js": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.0.tgz", - "integrity": "sha512-x+xdeDWq7FiORDvyIJ0q/waWd4PhjBNOm5dQUOq2AKC0IEjxOS66Ha9tctiVDGcRQuh69K7fgU5oRuTK4cysSg==", + "version": "3.16.3", + "integrity": "sha512-uVbFqx9vvLhQg0iBaau9Z75AxWJ8tqM9AV890dIZCLApF4rTcyHwmAvLeEdYRs+BzYWu8Iw81F79ah0EfTXbaw==", "dev": true, "optional": true, "bin": { @@ -6564,15 +5844,13 @@ } }, "node_modules/underscore": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.2.tgz", - "integrity": "sha512-ekY1NhRzq0B08g4bGuX4wd2jZx5GnKz6mKSqFL4nqBlfyMGiG10gDFhDTMEfYmDL6Jy0FUIZp7wiRB+0BP7J2g==", + "version": "1.13.4", + "integrity": "sha512-BQFnUDuAQ4Yf/cYY5LNrK9NCJFKriaRbD9uR1fTeXnBeoa97W0i41qkZfGO9pSo8I5KzjAcSY2XYtdf0oKd7KQ==", "dev": true }, "node_modules/unified": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.1.tgz", - "integrity": "sha512-v4ky1+6BN9X3pQrOdkFIPWAaeDsHPE1svRDxq7YpTc2plkIqFMwukfqM+l0ewpP9EfwARlt9pPFAeWYhHm8X9w==", + "version": "10.1.2", + "integrity": "sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==", "dev": true, "dependencies": { "@types/unist": "^2.0.0", @@ -6588,21 +5866,8 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/unified/node_modules/is-plain-obj": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.0.0.tgz", - "integrity": "sha512-NXRbBtUdBioI73y/HmOhogw/U5msYPC9DAtGkJXeFcFWSFZw0mCUsPxk/snTuJHzNKA8kLBK4rH97RMB1BfCXw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/union": { "version": "0.5.0", - "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==", "dev": true, "dependencies": { @@ -6613,9 +5878,8 @@ } }, "node_modules/unist-util-stringify-position": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.0.tgz", - "integrity": "sha512-SdfAl8fsDclywZpfMDTVDxA2V7LjtRDTOFd44wUJamgl6OlVngsqWjxvermMYf60elWHbxhuRCZml7AnuXCaSA==", + "version": "3.0.2", + "integrity": "sha512-7A6eiDCs9UtjcwZOcCpM4aPII3bAAGv13E96IkawkOAW0OhH+yRxtY0lzo8KiHpzEMfH7Q+FizUmwp8Iqy5EWg==", "dev": true, "dependencies": { "@types/unist": "^2.0.0" @@ -6627,7 +5891,6 @@ }, "node_modules/universalify": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", "dev": true, "engines": { @@ -6636,48 +5899,61 @@ }, "node_modules/untildify": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", "dev": true, "engines": { "node": ">=8" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "node_modules/update-browserslist-db": { + "version": "1.0.5", + "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], "dependencies": { - "punycode": "^2.1.0" + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist-lint": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" } }, - "node_modules/url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "node_modules/uri-js": { + "version": "4.4.1", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "dependencies": { - "punycode": "1.3.2", - "querystring": "0.2.0" + "punycode": "^2.1.0" } }, "node_modules/url-join": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", "dev": true }, - "node_modules/url/node_modules/punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", - "dev": true + "node_modules/uuid": { + "version": "8.3.2", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } }, "node_modules/uvu": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.3.tgz", - "integrity": "sha512-brFwqA3FXzilmtnIyJ+CxdkInkY/i4ErvP7uV0DnUVxQcQ55reuHphorpF+tZoVHK2MniZ/VJzI7zJQoc9T9Yw==", + "version": "0.5.6", + "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", "dev": true, "dependencies": { "dequal": "^2.0.0", @@ -6694,14 +5970,12 @@ }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true }, "node_modules/verror": { "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", "dev": true, "engines": [ "node >=0.6.0" @@ -6712,10 +5986,14 @@ "extsprintf": "^1.2.0" } }, + "node_modules/verror/node_modules/core-util-is": { + "version": "1.0.2", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true + }, "node_modules/vfile": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.0.tgz", - "integrity": "sha512-Tj44nY/48OQvarrE4FAjUfrv7GZOYzPbl5OD65HxVKwLJKMPU7zmfV8cCgCnzKWnSfYG2f3pxu+ALqs7j22xQQ==", + "version": "5.3.4", + "integrity": "sha512-KI+7cnst03KbEyN1+JE504zF5bJBZa+J+CrevLeyIMq0aPU681I2rQ5p4PlnQ6exFtWiUrg26QUdFMnAKR6PIw==", "dev": true, "dependencies": { "@types/unist": "^2.0.0", @@ -6729,9 +6007,8 @@ } }, "node_modules/vfile-message": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.0.tgz", - "integrity": "sha512-4QJbBk+DkPEhBXq3f260xSaWtjE4gPKOfulzfMFF8ZNwaPZieWsg3iVlcmF04+eebzpcpeXOOFMfrYzJHVYg+g==", + "version": "3.1.2", + "integrity": "sha512-QjSNP6Yxzyycd4SVOtmKKyTsSvClqBPJcd00Z0zuPj3hOIjg0rUPG6DbFGPvUKRgYyaIWLPKpuEclcuvb3H8qA==", "dev": true, "dependencies": { "@types/unist": "^2.0.0", @@ -6744,7 +6021,6 @@ }, "node_modules/walk-back": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/walk-back/-/walk-back-5.1.0.tgz", "integrity": "sha512-Uhxps5yZcVNbLEAnb+xaEEMdgTXl9qAQDzKYejG2AZ7qPwRQ81lozY9ECDbjLPNWm7YsO1IK5rsP1KoQzXAcGA==", "dev": true, "engines": { @@ -6753,7 +6029,6 @@ }, "node_modules/wasm-opt": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/wasm-opt/-/wasm-opt-1.3.0.tgz", "integrity": "sha512-24+IOboX4Sav0bI8Krwf0Y6dnpN4KxYtqpl0qWt86qVLsmayUqx1KMBrJTlQWNC+/dsqzQJjK6QvxNJsZYOgJg==", "dev": true, "hasInstallScript": true, @@ -6767,7 +6042,6 @@ }, "node_modules/wasm-pack": { "version": "0.10.1", - "resolved": "https://registry.npmjs.org/wasm-pack/-/wasm-pack-0.10.1.tgz", "integrity": "sha512-bw480KaaJQhL6UX8wAm6YCO497uaULDrsvUey3EB86dKAfFc6qCGVN1kItcwUklEeufonwo9mwz9/fy9xLOPtQ==", "dev": true, "hasInstallScript": true, @@ -6779,9 +6053,8 @@ } }, "node_modules/watchpack": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz", - "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==", + "version": "2.4.0", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", "dev": true, "dependencies": { "glob-to-regexp": "^0.4.1", @@ -6791,35 +6064,38 @@ "node": ">=10.13.0" } }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, "node_modules/webpack": { - "version": "5.67.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.67.0.tgz", - "integrity": "sha512-LjFbfMh89xBDpUMgA1W9Ur6Rn/gnr2Cq1jjHFPo4v6a79/ypznSYbAyPgGhwsxBtMIaEmDD1oJoA7BEYw/Fbrw==", + "version": "5.74.0", + "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==", "dev": true, "dependencies": { - "@types/eslint-scope": "^3.7.0", - "@types/estree": "^0.0.50", + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", "@webassemblyjs/ast": "1.11.1", "@webassemblyjs/wasm-edit": "1.11.1", "@webassemblyjs/wasm-parser": "1.11.1", - "acorn": "^8.4.1", + "acorn": "^8.7.1", "acorn-import-assertions": "^1.7.6", "browserslist": "^4.14.5", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.8.3", + "enhanced-resolve": "^5.10.0", "es-module-lexer": "^0.9.0", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.9", - "json-parse-better-errors": "^1.0.2", + "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", "schema-utils": "^3.1.0", "tapable": "^2.1.1", "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.3.1", + "watchpack": "^2.4.0", "webpack-sources": "^3.2.3" }, "bin": { @@ -6839,18 +6115,17 @@ } }, "node_modules/webpack-cli": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.9.2.tgz", - "integrity": "sha512-m3/AACnBBzK/kMTcxWHcZFPrw/eQuY4Df1TxvIWfWM2x7mRqBQCqKEd96oCUa9jkapLBaFfRce33eGDb4Pr7YQ==", + "version": "4.10.0", + "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", "dev": true, "dependencies": { "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^1.1.1", - "@webpack-cli/info": "^1.4.1", - "@webpack-cli/serve": "^1.6.1", + "@webpack-cli/configtest": "^1.2.0", + "@webpack-cli/info": "^1.5.0", + "@webpack-cli/serve": "^1.7.0", "colorette": "^2.0.14", "commander": "^7.0.0", - "execa": "^5.0.0", + "cross-spawn": "^7.0.3", "fastest-levenshtein": "^1.0.12", "import-local": "^3.0.2", "interpret": "^2.2.0", @@ -6863,6 +6138,10 @@ "engines": { "node": ">=10.13.0" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, "peerDependencies": { "webpack": "4.x.x || 5.x.x" }, @@ -6881,15 +6160,8 @@ } } }, - "node_modules/webpack-cli/node_modules/colorette": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", - "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", - "dev": true - }, "node_modules/webpack-cli/node_modules/commander": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", "dev": true, "engines": { @@ -6898,7 +6170,6 @@ }, "node_modules/webpack-merge": { "version": "5.8.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", "dev": true, "dependencies": { @@ -6911,7 +6182,6 @@ }, "node_modules/webpack-sources": { "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", "dev": true, "engines": { @@ -6920,7 +6190,6 @@ }, "node_modules/whatwg-encoding": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", "dev": true, "dependencies": { @@ -6930,85 +6199,45 @@ "node": ">=12" } }, - "node_modules/whatwg-encoding/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, + "node_modules/whatwg-url": { + "version": "5.0.0", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" } }, "node_modules/which": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "dependencies": { "isexe": "^2.0.0" }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "node_modules/wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "dependencies": { - "string-width": "^1.0.2 || 2" - } - }, - "node_modules/wide-align/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/wide-align/node_modules/string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "dependencies": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "bin": { + "node-which": "bin/node-which" }, "engines": { - "node": ">=4" + "node": ">= 8" } }, + "node_modules/which-module": { + "version": "2.0.0", + "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", + "dev": true + }, "node_modules/wildcard": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", "dev": true }, "node_modules/wordwrap": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", "dev": true }, "node_modules/wordwrapjs": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-3.0.0.tgz", "integrity": "sha512-mO8XtqyPvykVCsrwj5MlOVWvSnCdT+C+QVbm6blradR7JExAhbkZ7hZ9A+9NUtwzSqrlUo9a67ws0EiILrvRpw==", "dev": true, "dependencies": { @@ -7019,15 +6248,21 @@ "node": ">=4.0.0" } }, + "node_modules/wordwrapjs/node_modules/reduce-flatten": { + "version": "1.0.1", + "integrity": "sha512-j5WfFJfc9CoXv/WbwVLHq74i/hdTUpy+iNC534LxczMRP67vJeK3V9JOdnL0N1cIRbn9mYhE2yVjvvKXDxvNXQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/workerpool": { "version": "6.2.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", "dev": true }, "node_modules/wrap-ansi": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "dependencies": { @@ -7044,49 +6279,14 @@ }, "node_modules/wrap-ansi/node_modules/ansi-regex": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "engines": { "node": ">=8" } }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/wrap-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, "node_modules/wrap-ansi/node_modules/strip-ansi": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "dependencies": { @@ -7098,19 +6298,16 @@ }, "node_modules/wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, "node_modules/xmlcreate": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", "dev": true }, "node_modules/y18n": { "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, "engines": { @@ -7119,32 +6316,29 @@ }, "node_modules/yallist": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "version": "17.5.1", + "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", "dev": true, "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.0", + "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "yargs-parser": "^21.0.0" }, "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "version": "20.2.4", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", "dev": true, "engines": { "node": ">=10" @@ -7152,7 +6346,6 @@ }, "node_modules/yargs-unparser": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", "dev": true, "dependencies": { @@ -7165,34 +6358,25 @@ "node": ">=10" } }, - "node_modules/yargs-unparser/node_modules/camelcase": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", - "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", + "node_modules/yargs-unparser/node_modules/is-plain-obj": { + "version": "2.1.0", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, - "node_modules/yargs-unparser/node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "node_modules/yargs/node_modules/yargs-parser": { + "version": "21.1.1", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=12" } }, "node_modules/yauzl": { "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", "dev": true, "dependencies": { "buffer-crc32": "~0.2.3", @@ -7201,7 +6385,6 @@ }, "node_modules/yn": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true, "engines": { @@ -7210,7 +6393,6 @@ }, "node_modules/yocto-queue": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, "engines": { @@ -7223,29 +6405,37 @@ }, "dependencies": { "@babel/parser": { - "version": "7.16.12", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.12.tgz", - "integrity": "sha512-VfaV15po8RiZssrkPweyvbGVSe4x2y+aciFCgn0n0/SJMR22cwofRV1mtnJQYcSB1wUTaA/X1LnA3es66MCO5A==", + "version": "7.18.11", + "integrity": "sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==", "dev": true }, - "@cspotcode/source-map-consumer": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", - "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", - "dev": true + "@colors/colors": { + "version": "1.5.0", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "optional": true }, "@cspotcode/source-map-support": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", - "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", + "version": "0.8.1", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, "requires": { - "@cspotcode/source-map-consumer": "0.8.0" + "@jridgewell/trace-mapping": "0.3.9" + } + }, + "@cycraig/iota-client-wasm": { + "version": "0.5.0-alpha.1", + "resolved": "https://registry.npmjs.org/@cycraig/iota-client-wasm/-/iota-client-wasm-0.5.0-alpha.1.tgz", + "integrity": "sha512-5D1l6QkOupmvaI19mROp/QjcqFNOHvjjGo2zJUbGbF7tI/v6J/CyiYpziREqWMUkCzKW3a/gEE9/YRbTRkJ7pg==", + "peer": true, + "requires": { + "@iota/types": "^1.0.0-beta.11", + "node-fetch": "^2.6.7", + "text-encoding": "^0.7.0" } }, "@cypress/request": { "version": "2.88.10", - "resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.10.tgz", "integrity": "sha512-Zp7F+R93N0yZyG34GutyTNr+okam7s/Fzc1+i3kcqOP8vk6OuajuE9qZJ6Rs+10/1JFtXFYMdyarnU1rZuJesg==", "dev": true, "requires": { @@ -7269,23 +6459,20 @@ "uuid": "^8.3.2" }, "dependencies": { - "qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", - "dev": true - }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true + "form-data": { + "version": "2.3.3", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } } } }, "@cypress/xvfb": { "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz", "integrity": "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==", "dev": true, "requires": { @@ -7295,30 +6482,54 @@ "dependencies": { "debug": { "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "requires": { "ms": "^2.1.1" } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true } } }, "@discoveryjs/json-ext": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.3.tgz", - "integrity": "sha512-Fxt+AfXgjMoin2maPIYzFZnQjAXjAL0PHscM5pRTtatFqB+vZxAM9tLp2Optnuw3QOQC40jTNeGYFOMvyf7v9g==", + "version": "0.5.7", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", "dev": true }, + "@iota/crypto.js": { + "version": "1.9.0-stardust.6", + "resolved": "https://registry.npmjs.org/@iota/crypto.js/-/crypto.js-1.9.0-stardust.6.tgz", + "integrity": "sha512-jbruSRXlEKW2dgfPFG7e42ODBp4dVu2/CAh/oZrzMjehPdyF2tgAeN1woOqOAmVONWoGVV9GViZti3nShhGF1g==", + "requires": { + "@iota/util.js": "^1.9.0-stardust.5", + "big-integer": "^1.6.51" + } + }, + "@iota/iota.js": { + "version": "1.9.0-stardust.25", + "resolved": "https://registry.npmjs.org/@iota/iota.js/-/iota.js-1.9.0-stardust.25.tgz", + "integrity": "sha512-mx/ZpkrUEgPjd1FxVxDKGpmDF/aTuUcRXjzZWjBL8v2w9dn3/Bs2fZ3AJw7wIwMG6v48AMXWq2yPwndpwDx/qw==", + "peer": true, + "requires": { + "@iota/crypto.js": "^1.9.0-stardust.6", + "@iota/util.js": "^1.9.0-stardust.5", + "big-integer": "^1.6.51", + "node-fetch": "2.6.7" + } + }, + "@iota/types": { + "version": "1.0.0-beta.11", + "integrity": "sha512-jFma7n4dpf+AikSIvY6CzrDDQ/TVnlV3HBIeOrDUDJku3oCcOkm8Jp/i0uKSD/krP2BAcDrWrNUr5Tn9MIw7+A==" + }, + "@iota/util.js": { + "version": "1.9.0-stardust.5", + "resolved": "https://registry.npmjs.org/@iota/util.js/-/util.js-1.9.0-stardust.5.tgz", + "integrity": "sha512-BGXzzjUQQYaV/H0bXJYKW+Zgd9PIN+HLPH8BcYhubI65X4G2ID23/osmpK/za+Ds6t/MkpdPKIiBwZPGXuCUBw==", + "requires": { + "big-integer": "^1.6.51" + } + }, "@jridgewell/gen-mapping": { "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", "dev": true, "requires": { @@ -7329,19 +6540,16 @@ }, "@jridgewell/resolve-uri": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", "dev": true }, "@jridgewell/set-array": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", "dev": true }, "@jridgewell/source-map": { "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", "dev": true, "requires": { @@ -7351,14 +6559,12 @@ }, "@jridgewell/sourcemap-codec": { "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", "dev": true }, "@jridgewell/trace-mapping": { - "version": "0.3.14", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", - "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", + "version": "0.3.9", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", "dev": true, "requires": { "@jridgewell/resolve-uri": "^3.0.3", @@ -7367,7 +6573,6 @@ }, "@nodelib/fs.scandir": { "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "requires": { @@ -7377,14 +6582,12 @@ }, "@nodelib/fs.stat": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true }, "@nodelib/fs.walk": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.7.tgz", - "integrity": "sha512-BTIhocbPBSrRmHxOAJFtR18oLhxTtAFDAvL8hY1S3iU8k+E60W/YFs4jrixGzQjMpF4qPXxIQHcjVD9dz1C2QA==", + "version": "1.2.8", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "requires": { "@nodelib/fs.scandir": "2.1.5", @@ -7392,32 +6595,27 @@ } }, "@tsconfig/node10": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", - "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", + "version": "1.0.9", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", "dev": true }, "@tsconfig/node12": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", - "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", + "version": "1.0.11", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", "dev": true }, "@tsconfig/node14": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", - "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", + "version": "1.0.3", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", "dev": true }, "@tsconfig/node16": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", - "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", + "version": "1.0.3", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", "dev": true }, "@types/debug": { "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", "integrity": "sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==", "dev": true, "requires": { @@ -7425,9 +6623,8 @@ } }, "@types/eslint": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.28.0.tgz", - "integrity": "sha512-07XlgzX0YJUn4iG1ocY4IX9DzKSmMGUs6ESKlxWhZRaa0fatIWaHWUVapcuGa8r5HFnTqzj+4OCjd5f7EZ/i/A==", + "version": "8.4.5", + "integrity": "sha512-dhsC09y1gpJWnK+Ff4SGvCuSnk9DaU0BJZSzOwa6GVSg65XtTugLBITDAAzRU5duGBoXBHpdR/9jHGxJjNflJQ==", "dev": true, "requires": { "@types/estree": "*", @@ -7435,9 +6632,8 @@ } }, "@types/eslint-scope": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.1.tgz", - "integrity": "sha512-SCFeogqiptms4Fg29WpOTk5nHIzfpKCemSN63ksBQYKTcXoJEmJagV+DhVmbapZzY4/5YaOV1nZwrsU79fFm1g==", + "version": "3.7.4", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", "dev": true, "requires": { "@types/eslint": "*", @@ -7445,33 +6641,29 @@ } }, "@types/estree": { - "version": "0.0.50", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", - "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", + "version": "0.0.51", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", "dev": true }, "@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "version": "7.0.11", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", "dev": true }, "@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true, "optional": true }, "@types/linkify-it": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.2.tgz", "integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==", "dev": true }, "@types/markdown-it": { "version": "12.2.3", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", "dev": true, "requires": { @@ -7481,7 +6673,6 @@ }, "@types/mdast": { "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz", "integrity": "sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==", "dev": true, "requires": { @@ -7490,50 +6681,49 @@ }, "@types/mdurl": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", "dev": true }, "@types/mocha": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.0.tgz", - "integrity": "sha512-QCWHkbMv4Y5U9oW10Uxbr45qMMSzl4OzijsozynUAgx3kEHUdXB00udx2dWDQ7f2TU2a2uuiFaRZjCe3unPpeg==", + "version": "9.1.1", + "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", "dev": true }, "@types/ms": { "version": "0.7.31", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==", "dev": true }, "@types/node": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.0.0.tgz", - "integrity": "sha512-TmCW5HoZ2o2/z2EYi109jLqIaPIi9y/lc2LmDCWzuCi35bcaQ+OtUh6nwBiFK7SOu25FAU5+YKdqFZUwtqGSdg==", - "dev": true + "version": "18.6.5", + "integrity": "sha512-Xjt5ZGUa5WusGZJ4WJPbOT8QOqp6nDynVFRKcUt32bOgvXEoc6o085WNkYTMO7ifAj2isEfQQ2cseE+wT6jsRw==" + }, + "@types/node-fetch": { + "version": "2.6.2", + "integrity": "sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==", + "requires": { + "@types/node": "*", + "form-data": "^3.0.0" + } }, "@types/sinonjs__fake-timers": { "version": "8.1.1", - "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz", "integrity": "sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==", "dev": true }, "@types/sizzle": { "version": "2.3.3", - "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz", "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==", "dev": true }, "@types/unist": { "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==", "dev": true }, "@types/yauzl": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.2.tgz", - "integrity": "sha512-8uALY5LTvSuHgloDVUvWP3pIauILm+8/0pDMokuDYIoNsOkSwd5AiHBTSEJjKTDcZr5z8UpgOWZkxBF4iJftoA==", + "version": "2.10.0", + "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==", "dev": true, "optional": true, "requires": { @@ -7542,13 +6732,11 @@ }, "@ungap/promise-all-settled": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", "dev": true }, "@webassemblyjs/ast": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", "dev": true, "requires": { @@ -7558,25 +6746,21 @@ }, "@webassemblyjs/floating-point-hex-parser": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", "dev": true }, "@webassemblyjs/helper-api-error": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", "dev": true }, "@webassemblyjs/helper-buffer": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", "dev": true }, "@webassemblyjs/helper-numbers": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", "dev": true, "requires": { @@ -7587,13 +6771,11 @@ }, "@webassemblyjs/helper-wasm-bytecode": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", "dev": true }, "@webassemblyjs/helper-wasm-section": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", "dev": true, "requires": { @@ -7605,7 +6787,6 @@ }, "@webassemblyjs/ieee754": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", "dev": true, "requires": { @@ -7614,7 +6795,6 @@ }, "@webassemblyjs/leb128": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", "dev": true, "requires": { @@ -7623,13 +6803,11 @@ }, "@webassemblyjs/utf8": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", "dev": true }, "@webassemblyjs/wasm-edit": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", "dev": true, "requires": { @@ -7645,7 +6823,6 @@ }, "@webassemblyjs/wasm-gen": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", "dev": true, "requires": { @@ -7658,7 +6835,6 @@ }, "@webassemblyjs/wasm-opt": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", "dev": true, "requires": { @@ -7670,7 +6846,6 @@ }, "@webassemblyjs/wasm-parser": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", "dev": true, "requires": { @@ -7684,7 +6859,6 @@ }, "@webassemblyjs/wast-printer": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", "dev": true, "requires": { @@ -7693,62 +6867,53 @@ } }, "@webpack-cli/configtest": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.1.1.tgz", - "integrity": "sha512-1FBc1f9G4P/AxMqIgfZgeOTuRnwZMten8E7zap5zgpPInnCrP8D4Q81+4CWIch8i/Nf7nXjP0v6CjjbHOrXhKg==", + "version": "1.2.0", + "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", "dev": true, "requires": {} }, "@webpack-cli/info": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.4.1.tgz", - "integrity": "sha512-PKVGmazEq3oAo46Q63tpMr4HipI3OPfP7LiNOEJg963RMgT0rqheag28NCML0o3GIzA3DmxP1ZIAv9oTX1CUIA==", + "version": "1.5.0", + "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", "dev": true, "requires": { "envinfo": "^7.7.3" } }, "@webpack-cli/serve": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.6.1.tgz", - "integrity": "sha512-gNGTiTrjEVQ0OcVnzsRSqTxaBSr+dmTfm+qJsCDluky8uhdLWep7Gcr62QsAKHTMxjCS/8nEITsmFAhfIx+QSw==", + "version": "1.7.0", + "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", "dev": true, "requires": {} }, "@xtuc/ieee754": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", "dev": true }, "@xtuc/long": { "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "dev": true }, "acorn": { - "version": "8.7.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", - "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", + "version": "8.8.0", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", "dev": true }, "acorn-import-assertions": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.7.6.tgz", - "integrity": "sha512-FlVvVFA1TX6l3lp8VjDnYYq7R1nyW6x3svAt4nDgrWQ9SBaSh9CnbwgSUTasgfNfOG5HlM1ehugCvM+hjo56LA==", + "version": "1.8.0", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", "dev": true, "requires": {} }, "acorn-walk": { "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", "dev": true }, "aggregate-error": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "dev": true, "requires": { @@ -7758,7 +6923,6 @@ }, "ajv": { "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "requires": { @@ -7770,20 +6934,17 @@ }, "ajv-keywords": { "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "dev": true, "requires": {} }, "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "version": "4.1.3", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true }, "ansi-escape-sequences": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-escape-sequences/-/ansi-escape-sequences-4.1.0.tgz", "integrity": "sha512-dzW9kHxH011uBsidTXd14JXgzye/YLb2LzeKZ4bsgl/Knwx8AtbSFkkGxagdNOoh0DlqHCmfiEjWKBaqjOanVw==", "dev": true, "requires": { @@ -7792,7 +6953,6 @@ "dependencies": { "array-back": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", "dev": true } @@ -7800,22 +6960,22 @@ }, "ansi-escapes": { "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, "requires": { "type-fest": "^0.21.3" } }, - "ansi-regex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", - "dev": true + "ansi-styles": { + "version": "4.3.0", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } }, "anymatch": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, "requires": { @@ -7825,43 +6985,36 @@ }, "arch": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", "dev": true }, "arg": { "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", "dev": true }, "argparse": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, "array-back": { "version": "6.2.2", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.2.tgz", "integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==", "dev": true }, "array-union": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, "arrify": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", "dev": true }, "asn1": { "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", "dev": true, "requires": { @@ -7870,30 +7023,22 @@ }, "assert-plus": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", "dev": true }, "astral-regex": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true }, "async": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", - "dev": true, - "requires": { - "lodash": "^4.17.14" - } + "version": "3.2.4", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "dev": true }, "asynckit": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "at-least-node": { "version": "1.0.0", @@ -7903,19 +7048,16 @@ }, "aws-sign2": { "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", "dev": true }, "aws4": { "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", "dev": true }, "axios": { "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", "dev": true, "requires": { @@ -7924,55 +7066,59 @@ }, "bail": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", "dev": true }, "balanced-match": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, "base64-js": { "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "dev": true }, "basic-auth": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", "dev": true, "requires": { "safe-buffer": "5.1.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } } }, "bcrypt-pbkdf": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", "dev": true, "requires": { "tweetnacl": "^0.14.3" } }, + "big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==" + }, "big.js": { "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", "dev": true }, "binary-extensions": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, "binary-install": { "version": "0.1.1", - "resolved": "https://registry.npmjs.org/binary-install/-/binary-install-0.1.1.tgz", "integrity": "sha512-DqED0D/6LrS+BHDkKn34vhRqOGjy5gTMgvYZsGK2TpNbdPuz4h+MRlNgGv5QBRd7pWq/jylM4eKNCizgAq3kNQ==", "dev": true, "requires": { @@ -7983,19 +7129,16 @@ }, "blob-util": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz", "integrity": "sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==", "dev": true }, "bluebird": { "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", "dev": true }, "brace-expansion": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { @@ -8005,7 +7148,6 @@ }, "braces": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, "requires": { @@ -8014,26 +7156,22 @@ }, "browser-stdout": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, "browserslist": { - "version": "4.16.6", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", - "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", + "version": "4.21.3", + "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001219", - "colorette": "^1.2.2", - "electron-to-chromium": "^1.3.723", - "escalade": "^3.1.1", - "node-releases": "^1.1.71" + "caniuse-lite": "^1.0.30001370", + "electron-to-chromium": "^1.4.202", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.5" } }, "buffer": { "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "dev": true, "requires": { @@ -8043,19 +7181,16 @@ }, "buffer-crc32": { "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "dev": true }, "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "version": "1.1.2", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, "cache-point": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/cache-point/-/cache-point-2.0.0.tgz", "integrity": "sha512-4gkeHlFpSKgm3vm2gJN5sPqfmijYRFYCQ6tv5cLw0xVmT6r1z1vd4FNnpuOREco3cBs1G709sZ72LdgddKvL5w==", "dev": true, "requires": { @@ -8066,7 +7201,6 @@ "dependencies": { "array-back": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz", "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==", "dev": true } @@ -8074,52 +7208,63 @@ }, "cachedir": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz", "integrity": "sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==", "dev": true }, "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "version": "6.3.0", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true }, "caniuse-lite": { - "version": "1.0.30001319", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001319.tgz", - "integrity": "sha512-xjlIAFHucBRSMUo1kb5D4LYgcN1M45qdKP++lhqowDpwJwGkpIRTt5qQqnhxjj1vHcI7nrJxWhCC1ATrCEBTcw==", + "version": "1.0.30001375", + "integrity": "sha512-kWIMkNzLYxSvnjy0hL8w1NOaWNr2rn39RTAVyIwcw8juu60bZDWiF1/loOYANzjtJmy6qPgNmn38ro5Pygagdw==", "dev": true }, "caseless": { "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", "dev": true }, "catharsis": { "version": "0.9.0", - "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", "dev": true, "requires": { "lodash": "^4.17.15" } }, + "chalk": { + "version": "4.1.2", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "supports-color": { + "version": "7.2.0", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "character-entities": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.1.tgz", - "integrity": "sha512-OzmutCf2Kmc+6DrFrrPS8/tDh2+DpnrfzdICHWhcVC9eOd0N1PXmQEE1a8iM4IziIAG+8tmTq3K+oo0ubH6RRQ==", + "version": "2.0.2", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", "dev": true }, "check-more-types": { "version": "2.24.0", - "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", - "integrity": "sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA=", + "integrity": "sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==", "dev": true }, "chokidar": { "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, "requires": { @@ -8133,69 +7278,40 @@ "readdirp": "~3.6.0" } }, - "chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true - }, "chrome-trace-event": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", "dev": true }, "ci-info": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz", - "integrity": "sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==", + "version": "3.3.2", + "integrity": "sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg==", "dev": true }, "clean-stack": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true }, "cli-cursor": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "dev": true, "requires": { "restore-cursor": "^3.1.0" } }, - "cli-table": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.1.tgz", - "integrity": "sha1-9TsFJmqLGguTSz0IIebi3FkUriM=", - "dev": true, - "requires": { - "colors": "1.0.3" - }, - "dependencies": { - "colors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", - "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", - "dev": true - } - } - }, "cli-table3": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.1.tgz", - "integrity": "sha512-w0q/enDHhPLq44ovMGdQeeDLvwxwavsJX7oQGYt/LrBlYsyaxyDnp6z3QzFut/6kLLKnlcUVJLrpB7KBfgG/RA==", + "version": "0.6.2", + "integrity": "sha512-QyavHCaIC80cMivimWu4aWHilIpiDpfm3hGmqAmXVL1UsnbLuBSMd21hTX6VY4ZSDSM73ESLeF8TOYId3rBTbw==", "dev": true, "requires": { - "colors": "1.4.0", + "@colors/colors": "1.5.0", "string-width": "^4.2.0" } }, "cli-truncate": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", "dev": true, "requires": { @@ -8205,7 +7321,6 @@ }, "cliui": { "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, "requires": { @@ -8216,13 +7331,11 @@ "dependencies": { "ansi-regex": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "strip-ansi": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { @@ -8233,7 +7346,6 @@ }, "clone-deep": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", "dev": true, "requires": { @@ -8244,7 +7356,6 @@ }, "collect-all": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/collect-all/-/collect-all-1.0.4.tgz", "integrity": "sha512-RKZhRwJtJEP5FWul+gkSMEnaK6H3AGPTTWOiRimCcs+rc/OmQE3Yhy1Q7A7KsdkG3ZXVdZq68Y6ONSdvkeEcKA==", "dev": true, "requires": { @@ -8252,35 +7363,37 @@ "stream-via": "^1.0.4" } }, - "colorette": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", - "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", + "color-convert": { + "version": "2.0.1", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true, - "optional": true + "colorette": { + "version": "2.0.19", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", + "dev": true }, "combined-stream": { "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "requires": { "delayed-stream": "~1.0.0" } }, "command-line-args": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.1.1.tgz", - "integrity": "sha512-hL/eG8lrll1Qy1ezvkant+trihbGnaKaeEjj6Scyr3DN+RC7iQ5Rz84IeLERfAWDGo0HBSNAakczwgCilDXnWg==", + "version": "5.2.1", + "integrity": "sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==", "dev": true, "requires": { - "array-back": "^3.0.1", + "array-back": "^3.1.0", "find-replace": "^3.0.0", "lodash.camelcase": "^4.3.0", "typical": "^4.0.0" @@ -8288,13 +7401,11 @@ "dependencies": { "array-back": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", "dev": true }, "typical": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==", "dev": true } @@ -8302,7 +7413,6 @@ }, "command-line-tool": { "version": "0.8.0", - "resolved": "https://registry.npmjs.org/command-line-tool/-/command-line-tool-0.8.0.tgz", "integrity": "sha512-Xw18HVx/QzQV3Sc5k1vy3kgtOeGmsKIqwtFFoyjI4bbcpSgnw2CWVULvtakyw4s6fhyAdI6soQQhXc2OzJy62g==", "dev": true, "requires": { @@ -8315,7 +7425,6 @@ "dependencies": { "array-back": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", "dev": true, "requires": { @@ -8326,7 +7435,6 @@ }, "command-line-usage": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-4.1.0.tgz", "integrity": "sha512-MxS8Ad995KpdAC0Jopo/ovGIroV/m0KHwzKfXxKag6FHOkGsH8/lv5yjgablcRxCJJC0oJeUMuO/gmaq+Wq46g==", "dev": true, "requires": { @@ -8338,7 +7446,6 @@ "dependencies": { "array-back": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", "dev": true, "requires": { @@ -8348,126 +7455,44 @@ } }, "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "version": "5.1.0", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", "dev": true }, "common-sequence": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/common-sequence/-/common-sequence-2.0.2.tgz", "integrity": "sha512-jAg09gkdkrDO9EWTdXfv80WWH3yeZl5oT69fGfedBNS9pXUKYInVJ1bJ+/ht2+Moeei48TmSbQDYMc8EOx9G0g==", "dev": true - }, - "common-tags": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", - "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "concurrently": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-7.0.0.tgz", - "integrity": "sha512-WKM7PUsI8wyXpF80H+zjHP32fsgsHNQfPLw/e70Z5dYkV7hF+rf8q3D+ScWJIEr57CpkO3OWBko6hwhQLPR8Pw==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "date-fns": "^2.16.1", - "lodash": "^4.17.21", - "rxjs": "^6.6.3", - "spawn-command": "^0.0.2-1", - "supports-color": "^8.1.0", - "tree-kill": "^1.2.2", - "yargs": "^16.2.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } + }, + "common-tags": { + "version": "1.8.2", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "concurrently": { + "version": "7.3.0", + "integrity": "sha512-IiDwm+8DOcFEInca494A8V402tNTQlJaYq78RF2rijOrKEk/AOHTxhN4U1cp7GYKYX5Q6Ymh1dLTBlzIMN0ikA==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "date-fns": "^2.16.1", + "lodash": "^4.17.21", + "rxjs": "^7.0.0", + "shell-quote": "^1.7.3", + "spawn-command": "^0.0.2-1", + "supports-color": "^8.1.0", + "tree-kill": "^1.2.2", + "yargs": "^17.3.1" } }, "config-master": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/config-master/-/config-master-3.1.0.tgz", - "integrity": "sha1-ZnZjWQUFooO/JqSE1oSJ10xUhdo=", + "integrity": "sha512-n7LBL1zBzYdTpF1mx5DNcZnZn05CWIdsdvtPL4MosvqbBUK3Rq6VWEtGUuF3Y0s9/CIhMejezqlSkP6TnCJ/9g==", "dev": true, "requires": { "walk-back": "^2.0.1" @@ -8475,15 +7500,13 @@ "dependencies": { "walk-back": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/walk-back/-/walk-back-2.0.1.tgz", - "integrity": "sha1-VU4qnYdPrEeoywBr9EwvDEmYoKQ=", + "integrity": "sha512-Nb6GvBR8UWX1D+Le+xUq0+Q1kFmRBIWVrfLnQAOmcpEzA9oAxwJ9gIr36t9TWYfzvWRvuMtjHiVsJYEkXWaTAQ==", "dev": true } } }, "copy-webpack-plugin": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-7.0.0.tgz", "integrity": "sha512-SLjQNa5iE3BoCP76ESU9qYo9ZkEWtXoZxDurHoqPchAFRblJ9g96xTeC560UXBMre1Nx6ixIIUfiY3VcjpJw3g==", "dev": true, "requires": { @@ -8497,27 +7520,18 @@ "serialize-javascript": "^5.0.1" } }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, "corser": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", - "integrity": "sha1-jtolLsqrWEDc2XXOuQ2TcMgZ/4c=", + "integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==", "dev": true }, "create-require": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, "cross-spawn": { "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "requires": { @@ -8527,9 +7541,8 @@ } }, "cypress": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-9.3.1.tgz", - "integrity": "sha512-BODdPesxX6bkVUnH8BVsV8I/jn57zQtO1FEOUTiuG2us3kslW7g0tcuwiny7CKCmJUZz8S/D587ppC+s58a+5Q==", + "version": "9.7.0", + "integrity": "sha512-+1EE1nuuuwIt/N1KXRR2iWHU+OiIt7H28jJDyyI4tiUftId/DrXYEwoDa5+kH2pki1zxnA0r6HrUGHV5eLbF5Q==", "dev": true, "requires": { "@cypress/request": "^2.88.10", @@ -8564,190 +7577,66 @@ "listr2": "^3.8.3", "lodash": "^4.17.21", "log-symbols": "^4.0.0", - "minimist": "^1.2.5", + "minimist": "^1.2.6", "ospath": "^1.2.2", "pretty-bytes": "^5.6.0", "proxy-from-env": "1.0.0", "request-progress": "^3.0.0", + "semver": "^7.3.2", "supports-color": "^8.1.1", "tmp": "~0.2.1", "untildify": "^4.0.0", - "url": "^0.11.0", "yauzl": "^2.10.0" }, "dependencies": { "@types/node": { - "version": "14.18.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.9.tgz", - "integrity": "sha512-j11XSuRuAlft6vLDEX4RvhqC0KxNxx6QIyMXNb0vHHSNPXTPeiy3algESWmOOIzEtiEL0qiowPU3ewW9hHVa7Q==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "commander": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", - "dev": true - }, - "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "execa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", - "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - } - }, - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "14.18.23", + "integrity": "sha512-MhbCWN18R4GhO8ewQWAFK4TGQdBpXWByukz7cWyJmXhvRuCIaM/oWytGPqVmDzgEnnaIc9ss6HbU5mUi+vyZPA==", "dev": true }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, "requires": { - "has-flag": "^4.0.0" + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" } } } }, "cypress-parallel": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/cypress-parallel/-/cypress-parallel-0.1.8.tgz", - "integrity": "sha512-aR0DZZeo3r/tBtXGAN3aug9G4/JGLCPy3FHHmko9MtZKwg4mqxVa+OXxh/JkxMlf+YqbZgoAfyV259LHuEBSBQ==", + "version": "0.1.10", + "integrity": "sha512-TKuJtvM0RU/i/SSkZhAy98FebpVypHqrpic+Y3Pnr3Ujj8JQlUu4R/QOILNI0bdjaPX0WX8yN5KSGmbJmk4VPg==", "dev": true, "requires": { - "cli-table": "0.3.1", + "cli-table3": "^0.6.0", + "cross-spawn": "^7.0.3", "is-npm": "^5.0.0", - "mocha": "^8.0.1", + "mocha": "^8.2.1", "yargs": "15.3.1" }, "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "ansi-colors": { + "version": "4.1.1", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } + "ansi-regex": { + "version": "3.0.1", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "dev": true }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } + "camelcase": { + "version": "5.3.1", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true }, "chokidar": { "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", "dev": true, "requires": { @@ -8761,47 +7650,26 @@ "readdirp": "~3.5.0" } }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, "debug": { "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, "requires": { "ms": "2.1.2" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } } }, + "decamelize": { + "version": "1.2.0", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true + }, "escape-string-regexp": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, "glob": { "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "dev": true, "requires": { @@ -8813,33 +7681,45 @@ "path-is-absolute": "^1.0.0" } }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "is-fullwidth-code-point": { + "version": "2.0.0", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", "dev": true }, + "js-yaml": { + "version": "4.0.0", + "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "version": "5.0.0", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "requires": { - "p-locate": "^5.0.0" + "p-locate": "^4.1.0" } }, "log-symbols": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", "dev": true, "requires": { "chalk": "^4.0.0" } }, + "minimatch": { + "version": "3.0.4", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, "mocha": { "version": "8.4.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz", "integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==", "dev": true, "requires": { @@ -8870,19 +7750,13 @@ "yargs-unparser": "2.0.0" }, "dependencies": { - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } + "ms": { + "version": "2.1.3", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true }, "yargs": { "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "requires": { @@ -8897,30 +7771,29 @@ } } }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, "nanoid": { "version": "3.1.20", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", "dev": true }, + "p-limit": { + "version": "2.3.0", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "version": "4.1.0", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "requires": { - "p-limit": "^3.0.2" + "p-limit": "^2.2.0" } }, "readdirp": { "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", "dev": true, "requires": { @@ -8928,43 +7801,64 @@ } }, "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "version": "4.0.0", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", "dev": true, "requires": { - "ansi-regex": "^5.0.1" + "ansi-regex": "^3.0.0" } }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "wide-align": { + "version": "1.1.3", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dev": true, "requires": { - "has-flag": "^4.0.0" + "string-width": "^1.0.2 || 2" + }, + "dependencies": { + "string-width": { + "version": "2.1.1", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + } } }, "workerpool": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz", "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==", "dev": true }, "wrap-ansi": { "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, "requires": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.1", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } } }, "yargs": { "version": "15.3.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz", "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==", "dev": true, "requires": { @@ -8981,9 +7875,13 @@ "yargs-parser": "^18.1.1" }, "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, "cliui": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", "dev": true, "requires": { @@ -8992,15 +7890,30 @@ "wrap-ansi": "^6.2.0" } }, + "find-up": { + "version": "4.1.0", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "strip-ansi": { + "version": "6.0.1", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, "y18n": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", "dev": true }, "yargs-parser": { "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "dev": true, "requires": { @@ -9009,46 +7922,43 @@ } } } - }, - "yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true } } }, "dashdash": { "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", "dev": true, "requires": { "assert-plus": "^1.0.0" } }, "date-fns": { - "version": "2.28.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.28.0.tgz", - "integrity": "sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==", + "version": "2.29.1", + "integrity": "sha512-dlLD5rKaKxpFdnjrs+5azHDFOPEu4ANy/LTh04A1DTzMM7qoajmKCBc8pkKRFT41CNzw+4gQh79X5C+Jq27HAw==", "dev": true }, "dayjs": { - "version": "1.10.7", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.7.tgz", - "integrity": "sha512-P6twpd70BcPK34K26uJ1KT3wlhpuOAPoMwJzpsIWUxHZ7wpmbdZL/hQqBDfz7hGurYSa5PhzdhDHtt319hL3ig==", + "version": "1.11.4", + "integrity": "sha512-Zj/lPM5hOvQ1Bf7uAvewDaUcsJoI6JmNqmHhHl3nyumwe0XHwt8sWdOVAPACJzCebL8gQCi+K49w7iKWnGwX9g==", "dev": true }, + "debug": { + "version": "4.3.4", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "version": "4.0.0", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true }, "decode-named-character-reference": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.1.tgz", - "integrity": "sha512-YV/0HQHreRwKb7uBopyIkLG17jG6Sv2qUchk9qSoVJ2f+flwRsPNBO0hAnjt6mTNYUT+vw9Gy2ihXg4sUWPi2w==", + "version": "1.0.2", + "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", "dev": true, "requires": { "character-entities": "^2.0.0" @@ -9056,37 +7966,30 @@ }, "deep-extend": { "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true }, "delayed-stream": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" }, "dequal": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.2.tgz", - "integrity": "sha512-q9K8BlJVxK7hQYqa6XISGmBZbtQQWVXSrRrWreHC94rMt1QL/Impruc+7p2CYSYuVIUr+YCt6hjrs1kkdJRTug==", + "version": "2.0.3", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", "dev": true }, "diff": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", "dev": true }, "diff-match-patch": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==", "dev": true }, "dir-glob": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, "requires": { @@ -9095,7 +7998,6 @@ }, "dmd": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/dmd/-/dmd-6.1.0.tgz", "integrity": "sha512-0zQIJ873gay1scCTFZvHPWM9mVJBnaylB2NQDI8O9u8O32m00Jb6uxDKexZm8hjTRM7RiWe0FJ32pExHoXdwoQ==", "dev": true, "requires": { @@ -9111,20 +8013,11 @@ "reduce-without": "^1.0.1", "test-value": "^3.0.0", "walk-back": "^5.1.0" - }, - "dependencies": { - "reduce-flatten": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-3.0.1.tgz", - "integrity": "sha512-bYo+97BmUUOzg09XwfkwALt4PQH1M5L0wzKerBt6WLm3Fhdd43mMS89HiT1B9pJIqko/6lWx3OnV4J9f2Kqp5Q==", - "dev": true - } } }, "ecc-jsbn": { "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", "dev": true, "requires": { "jsbn": "~0.1.0", @@ -9132,26 +8025,22 @@ } }, "electron-to-chromium": { - "version": "1.3.768", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.768.tgz", - "integrity": "sha512-I4UMZHhVSK2pwt8jOIxTi3GIuc41NkddtKT/hpuxp9GO5UWJgDKTBa4TACppbVAuKtKbMK6BhQZvT5tFF1bcNA==", + "version": "1.4.213", + "integrity": "sha512-+3DbGHGOCHTVB/Ms63bGqbyC1b8y7Fk86+7ltssB8NQrZtSCvZG6eooSl9U2Q0yw++fL2DpHKOdTU0NVEkFObg==", "dev": true }, "emoji-regex": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, "emojis-list": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", "dev": true }, "end-of-stream": { "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "dev": true, "requires": { @@ -9159,9 +8048,8 @@ } }, "enhanced-resolve": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz", - "integrity": "sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA==", + "version": "5.10.0", + "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", "dev": true, "requires": { "graceful-fs": "^4.2.4", @@ -9170,7 +8058,6 @@ }, "enquirer": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", "dev": true, "requires": { @@ -9179,37 +8066,31 @@ }, "entities": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", "dev": true }, "envinfo": { "version": "7.8.1", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", "dev": true }, "es-module-lexer": { "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", "dev": true }, "escalade": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true }, "escape-string-regexp": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true }, "eslint-scope": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, "requires": { @@ -9219,7 +8100,6 @@ }, "esrecurse": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "requires": { @@ -9227,80 +8107,63 @@ }, "dependencies": { "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "version": "5.3.0", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true } } }, "estraverse": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true }, "eventemitter2": { - "version": "6.4.5", - "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.5.tgz", - "integrity": "sha512-bXE7Dyc1i6oQElDG0jMRZJrRAn9QR2xyyFGmBdZleNmyQX0FqGYmhZIrIrpPfm/w//LTo4tVQGOGQcGCb5q9uw==", + "version": "6.4.7", + "integrity": "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==", "dev": true }, "eventemitter3": { "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", "dev": true }, "events": { "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true }, "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "version": "4.1.0", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", "dev": true, "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", "strip-final-newline": "^2.0.0" } }, "executable": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", "dev": true, "requires": { "pify": "^2.2.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } } }, "extend": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "dev": true }, "extract-zip": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", "dev": true, "requires": { @@ -9308,50 +8171,21 @@ "debug": "^4.1.1", "get-stream": "^5.1.0", "yauzl": "^2.10.0" - }, - "dependencies": { - "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } } }, "extsprintf": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", "dev": true }, "fast-deep-equal": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, "fast-glob": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.6.tgz", - "integrity": "sha512-GnLuqj/pvQ7pX8/L4J84nijv6sAnlwvSDpMkJi9i7nPmPxGtRPkBSStfvDW5l6nMdX9VWe+pkKWFTgD+vF2QSQ==", + "version": "3.2.11", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", @@ -9363,20 +8197,17 @@ }, "fast-json-stable-stringify": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, "fastest-levenshtein": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", - "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", + "version": "1.0.16", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", "dev": true }, "fastq": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.1.tgz", - "integrity": "sha512-HOnr8Mc60eNYl1gzwp6r5RoUyAn5/glBolUzP/Ez6IFVPMPirxn/9phgL6zhOtaTy7ISwPvQ+wT+hfcRZh/bzw==", + "version": "1.13.0", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", "dev": true, "requires": { "reusify": "^1.0.4" @@ -9384,8 +8215,7 @@ }, "fd-slicer": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", "dev": true, "requires": { "pend": "~1.2.0" @@ -9393,7 +8223,6 @@ }, "figures": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", "dev": true, "requires": { @@ -9402,7 +8231,6 @@ }, "file-set": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/file-set/-/file-set-4.0.2.tgz", "integrity": "sha512-fuxEgzk4L8waGXaAkd8cMr73Pm0FxOVkn8hztzUW7BAHhOGH90viQNXbiOsnecCWmfInqU6YmAMwxRMdKETceQ==", "dev": true, "requires": { @@ -9412,7 +8240,6 @@ "dependencies": { "array-back": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-5.0.0.tgz", "integrity": "sha512-kgVWwJReZWmVuWOQKEOohXKJX+nD02JAZ54D1RRWlv8L0NebauKAaFxACKzB74RTclt1+WNz5KHaLRDAPZbDEw==", "dev": true } @@ -9420,7 +8247,6 @@ }, "fill-range": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, "requires": { @@ -9429,7 +8255,6 @@ }, "find-replace": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", "dev": true, "requires": { @@ -9438,58 +8263,50 @@ "dependencies": { "array-back": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", "dev": true } } }, "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "version": "5.0.0", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "requires": { - "locate-path": "^5.0.0", + "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "flat": { "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "dev": true }, "follow-redirects": { - "version": "1.14.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", - "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==", + "version": "1.15.1", + "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", "dev": true }, "forever-agent": { "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", "dev": true }, "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, + "version": "3.0.1", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", "requires": { "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", + "combined-stream": "^1.0.8", "mime-types": "^2.1.12" } }, "fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, "requires": { - "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" @@ -9497,7 +8314,6 @@ }, "fs-minipass": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", "dev": true, "requires": { @@ -9506,77 +8322,63 @@ }, "fs-then-native": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fs-then-native/-/fs-then-native-2.0.0.tgz", - "integrity": "sha1-GaEk2U2QwiyOBF8ujdbr6jbUjGc=", + "integrity": "sha512-X712jAOaWXkemQCAmWeg5rOT2i+KOpWz1Z/txk/cW0qlOu2oQ9H61vc5w3X/iyuUEfq/OyaFJ78/cZAQD1/bgA==", "dev": true }, "fs.realpath": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, "function-bind": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, "get-caller-file": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true + "version": "5.2.0", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } }, "getos": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz", "integrity": "sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==", "dev": true, "requires": { "async": "^3.2.0" - }, - "dependencies": { - "async": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", - "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==", - "dev": true - } } }, "getpass": { "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", "dev": true, "requires": { "assert-plus": "^1.0.0" } }, "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "7.2.3", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "glob-parent": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "requires": { @@ -9585,13 +8387,11 @@ }, "glob-to-regexp": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", "dev": true }, "global-dirs": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz", "integrity": "sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==", "dev": true, "requires": { @@ -9599,34 +8399,30 @@ } }, "globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "version": "11.1.0", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "requires": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", "slash": "^3.0.0" } }, "graceful-fs": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", - "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", + "version": "4.2.10", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, "growl": { "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", "dev": true }, "handlebars": { "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", "dev": true, "requires": { @@ -9639,22 +8435,24 @@ }, "has": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, "requires": { "function-bind": "^1.1.1" } }, + "has-flag": { + "version": "4.0.0", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "he": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, "html-encoding-sniffer": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", "dev": true, "requires": { @@ -9663,7 +8461,6 @@ }, "http-proxy": { "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", "dev": true, "requires": { @@ -9673,9 +8470,8 @@ } }, "http-server": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/http-server/-/http-server-14.1.0.tgz", - "integrity": "sha512-5lYsIcZtf6pdR8tCtzAHTWrAveo4liUlJdWc7YafwK/maPgYHs+VNP6KpCClmUnSorJrARVMXqtT055zBv11Yg==", + "version": "14.1.1", + "integrity": "sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==", "dev": true, "requires": { "basic-auth": "^2.0.1", @@ -9685,68 +8481,16 @@ "html-encoding-sniffer": "^3.0.0", "http-proxy": "^1.18.1", "mime": "^1.6.0", - "minimist": "^1.2.5", + "minimist": "^1.2.6", "opener": "^1.5.1", "portfinder": "^1.0.28", "secure-compare": "3.0.1", "union": "~0.5.0", "url-join": "^4.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } } }, "http-signature": { "version": "1.3.6", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz", "integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==", "dev": true, "requires": { @@ -9756,27 +8500,31 @@ } }, "human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "version": "1.1.1", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", "dev": true }, + "iconv-lite": { + "version": "0.6.3", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, "ieee754": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "dev": true }, "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "version": "5.2.0", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "dev": true }, "import-local": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", - "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", + "version": "3.1.0", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", "dev": true, "requires": { "pkg-dir": "^4.2.0", @@ -9785,14 +8533,12 @@ }, "indent-string": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true }, "inflight": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, "requires": { "once": "^1.3.0", @@ -9801,25 +8547,21 @@ }, "inherits": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, "ini": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", "dev": true }, "interpret": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", "dev": true }, "is-binary-path": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, "requires": { @@ -9828,13 +8570,11 @@ }, "is-buffer": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", "dev": true }, "is-ci": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", "dev": true, "requires": { @@ -9842,9 +8582,8 @@ } }, "is-core-module": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", - "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", + "version": "2.10.0", + "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", "dev": true, "requires": { "has": "^1.0.3" @@ -9852,20 +8591,17 @@ }, "is-extglob": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true }, "is-fullwidth-code-point": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true }, "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "version": "4.0.3", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "requires": { "is-extglob": "^2.1.1" @@ -9873,7 +8609,6 @@ }, "is-installed-globally": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", "dev": true, "requires": { @@ -9883,31 +8618,26 @@ }, "is-npm": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz", "integrity": "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==", "dev": true }, "is-number": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, "is-path-inside": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true }, "is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "version": "4.1.0", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", "dev": true }, "is-plain-object": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, "requires": { @@ -9915,73 +8645,48 @@ } }, "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "version": "2.0.1", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true }, "is-typedarray": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", "dev": true }, "is-unicode-supported": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true }, "isexe": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "isobject": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", "dev": true }, "isstream": { "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", "dev": true }, "jest-worker": { - "version": "27.0.6", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.0.6.tgz", - "integrity": "sha512-qupxcj/dRuA3xHPMUd40gr2EaAurFbkwzOh7wfPaeE9id7hyjURRQoqNfHifHK3XjJU6YJJUQKILGUnwGPEOCA==", + "version": "27.5.1", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "dev": true, "requires": { "@types/node": "*", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } } }, "js-yaml": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", - "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", + "version": "4.1.0", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "requires": { "argparse": "^2.0.1" @@ -9989,7 +8694,6 @@ }, "js2xmlparser": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", "dev": true, "requires": { @@ -9998,14 +8702,12 @@ }, "jsbn": { "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", "dev": true }, "jsdoc": { - "version": "3.6.10", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.10.tgz", - "integrity": "sha512-IdQ8ppSo5LKZ9o3M+LKIIK8i00DIe5msDvG3G81Km+1dhy0XrOWD0Ji8H61ElgyEj/O9KRLokgKbAM9XX9CJAg==", + "version": "3.6.11", + "integrity": "sha512-8UCU0TYeIYD9KeLzEcAu2q8N/mx9O3phAGl32nmHlE0LpaJL71mMkP4d+QE5zWfNt50qheHtOZ0qoxVrsX5TUg==", "dev": true, "requires": { "@babel/parser": "^7.9.4", @@ -10014,7 +8716,7 @@ "catharsis": "^0.9.0", "escape-string-regexp": "^2.0.0", "js2xmlparser": "^4.0.2", - "klaw": "^4.0.1", + "klaw": "^3.0.0", "markdown-it": "^12.3.2", "markdown-it-anchor": "^8.4.1", "marked": "^4.0.10", @@ -10027,7 +8729,6 @@ "dependencies": { "escape-string-regexp": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", "dev": true } @@ -10035,7 +8736,6 @@ }, "jsdoc-api": { "version": "7.1.1", - "resolved": "https://registry.npmjs.org/jsdoc-api/-/jsdoc-api-7.1.1.tgz", "integrity": "sha512-0pkuPCzVXiqsDAsVrNFXCkHzlyNepBIDVtwwehry4RJAnZmXtlAz7rh8F9FRz53u3NeynGbex+bpYWwi8lE66A==", "dev": true, "requires": { @@ -10052,7 +8752,6 @@ }, "jsdoc-parse": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsdoc-parse/-/jsdoc-parse-6.1.0.tgz", "integrity": "sha512-n/hDGQJa69IBun1yZAjqzV4gVR41+flZ3bIlm9fKvNe2Xjsd1/+zCo2+R9ls8LxtePgIWbpA1jU7xkB2lRdLLg==", "dev": true, "requires": { @@ -10066,7 +8765,6 @@ }, "jsdoc-to-markdown": { "version": "7.1.1", - "resolved": "https://registry.npmjs.org/jsdoc-to-markdown/-/jsdoc-to-markdown-7.1.1.tgz", "integrity": "sha512-CI86d63xAVNO+ENumWwmJ034lYe5iGU5GwjtTA11EuphP9tpnoi4hrKgR/J8uME0D+o4KUpVfwX1fjZhc8dEtg==", "dev": true, "requires": { @@ -10079,42 +8777,33 @@ "walk-back": "^5.1.0" } }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "json-parse-even-better-errors": { + "version": "2.3.1", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, "json-schema": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", "dev": true }, "json-schema-traverse": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, "json-stringify-safe": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", "dev": true }, "json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } + "version": "2.2.1", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "dev": true }, "jsonfile": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, "requires": { @@ -10124,7 +8813,6 @@ }, "jsprim": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==", "dev": true, "requires": { @@ -10136,31 +8824,29 @@ }, "kind-of": { "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true }, "klaw": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/klaw/-/klaw-4.0.1.tgz", - "integrity": "sha512-pgsE40/SvC7st04AHiISNewaIMUbY5V/K8b21ekiPiFoYs/EYSdsGa+FJArB1d441uq4Q8zZyIxvAzkGNlBdRw==", - "dev": true + "version": "3.0.0", + "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.9" + } }, "kleur": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.4.tgz", - "integrity": "sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA==", + "version": "4.1.5", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", "dev": true }, "lazy-ass": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", - "integrity": "sha1-eZllXoZGwX8In90YfRUNMyTVRRM=", + "integrity": "sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==", "dev": true }, "linkify-it": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", "dev": true, "requires": { @@ -10169,7 +8855,6 @@ }, "listr2": { "version": "3.14.0", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.14.0.tgz", "integrity": "sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==", "dev": true, "requires": { @@ -10181,26 +8866,16 @@ "rxjs": "^7.5.1", "through": "^2.3.8", "wrap-ansi": "^7.0.0" - }, - "dependencies": { - "colorette": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", - "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", - "dev": true - } } }, "loader-runner": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", - "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", + "version": "4.3.0", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", "dev": true }, "loader-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", - "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "version": "2.0.2", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", "dev": true, "requires": { "big.js": "^5.2.2", @@ -10209,114 +8884,54 @@ } }, "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "version": "6.0.0", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "requires": { - "p-locate": "^4.1.0" + "p-locate": "^5.0.0" } }, "lodash": { "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, "lodash.camelcase": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", "dev": true }, "lodash.omit": { "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", - "integrity": "sha1-brGa5aHuHdnfC5aeZs4Lf6MLXmA=", + "integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==", "dev": true }, "lodash.once": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", "dev": true }, "lodash.padend": { "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz", - "integrity": "sha1-U8y6BH0G4VjTEfRdpiX05J5vFm4=", + "integrity": "sha512-sOQs2aqGpbl27tmCS1QNZA09Uqp01ZzWfDUoD+xzTii0E7dSQfRKcRetFwa+uXaxaqL+TKm7CgD2JdKP7aZBSw==", "dev": true }, "lodash.pick": { "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", - "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=", + "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==", "dev": true }, "log-symbols": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "requires": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } } }, "log-update": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", "dev": true, "requires": { @@ -10328,37 +8943,11 @@ "dependencies": { "ansi-regex": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, "slice-ansi": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", "dev": true, "requires": { @@ -10369,7 +8958,6 @@ }, "strip-ansi": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { @@ -10378,7 +8966,6 @@ }, "wrap-ansi": { "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, "requires": { @@ -10389,15 +8976,21 @@ } } }, + "lru-cache": { + "version": "6.0.0", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, "make-error": { "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, "markdown-it": { "version": "12.3.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", "dev": true, "requires": { @@ -10409,21 +9002,18 @@ } }, "markdown-it-anchor": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.4.1.tgz", - "integrity": "sha512-sLODeRetZ/61KkKLJElaU3NuU2z7MhXf12Ml1WJMSdwpngeofneCRF+JBbat8HiSqhniOMuTemXMrsI7hA6XyA==", + "version": "8.6.4", + "integrity": "sha512-Ul4YVYZNxMJYALpKtu+ZRdrryYt/GlQ5CK+4l1bp/gWXOG2QWElt6AqF3Mih/wfUKdZbNAZVXGR73/n6U/8img==", "dev": true, "requires": {} }, "marked": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.12.tgz", - "integrity": "sha512-hgibXWrEDNBWgGiK18j/4lkS6ihTe9sxtV4Q1OQppb/0zzyPSzoFANBa5MfsG/zgsWklmNnhm0XACZOH/0HBiQ==", + "version": "4.0.18", + "integrity": "sha512-wbLDJ7Zh0sqA0Vdg6aqlbT+yPxqLblpAZh1mK2+AO2twQkPywvvqQNfEPVwSSRjZ7dZcdeVBIAgiO7MMp3Dszw==", "dev": true }, "mdast-util-from-markdown": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.2.0.tgz", "integrity": "sha512-iZJyyvKD1+K7QX1b5jXdE7Sc5dtoTry1vzV28UZZe8Z1xVnB/czKntJ7ZAkG0tANqRnBF6p3p7GpU1y19DTf2Q==", "dev": true, "requires": { @@ -10443,31 +9033,26 @@ }, "mdast-util-to-string": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.1.0.tgz", "integrity": "sha512-n4Vypz/DZgwo0iMHLQL49dJzlp7YtAJP+N07MZHpjPf/5XJuHUWstviF4Mn2jEiR/GNmtnRRqnwsXExk3igfFA==", "dev": true }, "mdurl": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", "dev": true }, "merge-stream": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, "merge2": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true }, "micromark": { "version": "3.0.10", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.0.10.tgz", "integrity": "sha512-ryTDy6UUunOXy2HPjelppgJ2sNfcPz1pLlMdA6Rz9jPzhLikWXv/irpWV/I2jd68Uhmny7hHxAlAhk4+vWggpg==", "dev": true, "requires": { @@ -10488,28 +9073,10 @@ "micromark-util-symbol": "^1.0.0", "micromark-util-types": "^1.0.1", "uvu": "^0.5.0" - }, - "dependencies": { - "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } } }, "micromark-core-commonmark": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.0.6.tgz", "integrity": "sha512-K+PkJTxqjFfSNkfAhp4GB+cZPfQd6dxtTXnf+RjZOV7T4EEXnvgzOcnp+eSTmpGk9d1S9sL6/lqrgSNn/s0HZA==", "dev": true, "requires": { @@ -10533,7 +9100,6 @@ }, "micromark-factory-destination": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.0.0.tgz", "integrity": "sha512-eUBA7Rs1/xtTVun9TmV3gjfPz2wEwgK5R5xcbIM5ZYAtvGF6JkyaDsj0agx8urXnO31tEO6Ug83iVH3tdedLnw==", "dev": true, "requires": { @@ -10544,7 +9110,6 @@ }, "micromark-factory-label": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.0.2.tgz", "integrity": "sha512-CTIwxlOnU7dEshXDQ+dsr2n+yxpP0+fn271pu0bwDIS8uqfFcumXpj5mLn3hSC8iw2MUr6Gx8EcKng1dD7i6hg==", "dev": true, "requires": { @@ -10556,7 +9121,6 @@ }, "micromark-factory-space": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.0.0.tgz", "integrity": "sha512-qUmqs4kj9a5yBnk3JMLyjtWYN6Mzfcx8uJfi5XAveBniDevmZasdGBba5b4QsvRcAkmvGo5ACmSUmyGiKTLZew==", "dev": true, "requires": { @@ -10566,7 +9130,6 @@ }, "micromark-factory-title": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.0.2.tgz", "integrity": "sha512-zily+Nr4yFqgMGRKLpTVsNl5L4PMu485fGFDOQJQBl2NFpjGte1e86zC0da93wf97jrc4+2G2GQudFMHn3IX+A==", "dev": true, "requires": { @@ -10579,7 +9142,6 @@ }, "micromark-factory-whitespace": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.0.0.tgz", "integrity": "sha512-Qx7uEyahU1lt1RnsECBiuEbfr9INjQTGa6Err+gF3g0Tx4YEviPbqqGKNv/NrBaE7dVHdn1bVZKM/n5I/Bak7A==", "dev": true, "requires": { @@ -10591,7 +9153,6 @@ }, "micromark-util-character": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.1.0.tgz", "integrity": "sha512-agJ5B3unGNJ9rJvADMJ5ZiYjBRyDpzKAOk01Kpi1TKhlT1APx3XZk6eN7RtSz1erbWHC2L8T3xLZ81wdtGRZzg==", "dev": true, "requires": { @@ -10601,7 +9162,6 @@ }, "micromark-util-chunked": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.0.0.tgz", "integrity": "sha512-5e8xTis5tEZKgesfbQMKRCyzvffRRUX+lK/y+DvsMFdabAicPkkZV6gO+FEWi9RfuKKoxxPwNL+dFF0SMImc1g==", "dev": true, "requires": { @@ -10610,7 +9170,6 @@ }, "micromark-util-classify-character": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.0.0.tgz", "integrity": "sha512-F8oW2KKrQRb3vS5ud5HIqBVkCqQi224Nm55o5wYLzY/9PwHGXC01tr3d7+TqHHz6zrKQ72Okwtvm/xQm6OVNZA==", "dev": true, "requires": { @@ -10621,7 +9180,6 @@ }, "micromark-util-combine-extensions": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.0.0.tgz", "integrity": "sha512-J8H058vFBdo/6+AsjHp2NF7AJ02SZtWaVUjsayNFeAiydTxUwViQPxN0Hf8dp4FmCQi0UUFovFsEyRSUmFH3MA==", "dev": true, "requires": { @@ -10631,7 +9189,6 @@ }, "micromark-util-decode-numeric-character-reference": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.0.0.tgz", "integrity": "sha512-OzO9AI5VUtrTD7KSdagf4MWgHMtET17Ua1fIpXTpuhclCqD8egFWo85GxSGvxgkGS74bEahvtM0WP0HjvV0e4w==", "dev": true, "requires": { @@ -10640,7 +9197,6 @@ }, "micromark-util-decode-string": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.0.2.tgz", "integrity": "sha512-DLT5Ho02qr6QWVNYbRZ3RYOSSWWFuH3tJexd3dgN1odEuPNxCngTCXJum7+ViRAd9BbdxCvMToPOD/IvVhzG6Q==", "dev": true, "requires": { @@ -10652,19 +9208,16 @@ }, "micromark-util-encode": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.0.1.tgz", "integrity": "sha512-U2s5YdnAYexjKDel31SVMPbfi+eF8y1U4pfiRW/Y8EFVCy/vgxk/2wWTxzcqE71LHtCuCzlBDRU2a5CQ5j+mQA==", "dev": true }, "micromark-util-html-tag-name": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.0.0.tgz", - "integrity": "sha512-NenEKIshW2ZI/ERv9HtFNsrn3llSPZtY337LID/24WeLqMzeZhBEE6BQ0vS2ZBjshm5n40chKtJ3qjAbVV8S0g==", + "version": "1.1.0", + "integrity": "sha512-BKlClMmYROy9UiV03SwNmckkjn8QHVaWkqoAqzivabvdGcwNGMMMH/5szAnywmsTBUzDsU57/mFi0sp4BQO6dA==", "dev": true }, "micromark-util-normalize-identifier": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.0.0.tgz", "integrity": "sha512-yg+zrL14bBTFrQ7n35CmByWUTFsgst5JhA4gJYoty4Dqzj4Z4Fr/DHekSS5aLfH9bdlfnSvKAWsAgJhIbogyBg==", "dev": true, "requires": { @@ -10673,7 +9226,6 @@ }, "micromark-util-resolve-all": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.0.0.tgz", "integrity": "sha512-CB/AGk98u50k42kvgaMM94wzBqozSzDDaonKU7P7jwQIuH2RU0TeBqGYJz2WY1UdihhjweivStrJ2JdkdEmcfw==", "dev": true, "requires": { @@ -10682,7 +9234,6 @@ }, "micromark-util-sanitize-uri": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.0.0.tgz", "integrity": "sha512-cCxvBKlmac4rxCGx6ejlIviRaMKZc0fWm5HdCHEeDWRSkn44l6NdYVRyU+0nT1XC72EQJMZV8IPHF+jTr56lAg==", "dev": true, "requires": { @@ -10693,7 +9244,6 @@ }, "micromark-util-subtokenize": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.0.2.tgz", "integrity": "sha512-d90uqCnXp/cy4G881Ub4psE57Sf8YD0pim9QdjCRNjfas2M1u6Lbt+XZK9gnHL2XFhnozZiEdCa9CNfXSfQ6xA==", "dev": true, "requires": { @@ -10705,57 +9255,47 @@ }, "micromark-util-symbol": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.0.1.tgz", "integrity": "sha512-oKDEMK2u5qqAptasDAwWDXq0tG9AssVwAx3E9bBF3t/shRIGsWIRG+cGafs2p/SnDSOecnt6hZPCE2o6lHfFmQ==", "dev": true }, "micromark-util-types": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.0.2.tgz", "integrity": "sha512-DCfg/T8fcrhrRKTPjRrw/5LLvdGV7BHySf/1LOZx7TzWZdYRjogNtyNq885z3nNallwr3QUKARjqvHqX1/7t+w==", "dev": true }, "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "version": "4.0.5", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" + "braces": "^3.0.2", + "picomatch": "^2.3.1" } }, "mime": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "dev": true }, "mime-db": { - "version": "1.48.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz", - "integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==", - "dev": true + "version": "1.52.0", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" }, "mime-types": { - "version": "2.1.31", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz", - "integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==", - "dev": true, + "version": "2.1.35", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "requires": { - "mime-db": "1.48.0" + "mime-db": "1.52.0" } }, "mimic-fn": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -10763,14 +9303,12 @@ }, "minimist": { "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, "minipass": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", - "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", + "version": "3.3.4", + "integrity": "sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw==", "dev": true, "requires": { "yallist": "^4.0.0" @@ -10778,7 +9316,6 @@ }, "minizlib": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", "dev": true, "requires": { @@ -10788,20 +9325,17 @@ }, "mkdirp": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true }, "mkdirp2": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/mkdirp2/-/mkdirp2-1.0.5.tgz", "integrity": "sha512-xOE9xbICroUDmG1ye2h4bZ8WBie9EGmACaco8K8cx6RlkJJrxGIqjGqztAI+NMhexXBcdGbSEzI6N3EJPevxZw==", "dev": true }, "mocha": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.0.tgz", - "integrity": "sha512-kNn7E8g2SzVcq0a77dkphPsDSN7P+iYkqE0ZsGCYWRsoiKjOt+NvXfaagik8vuDa6W5Zw3qxe8Jfpt5qKf+6/Q==", + "version": "9.2.2", + "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", "dev": true, "requires": { "@ungap/promise-all-settled": "1.1.2", @@ -10817,9 +9351,9 @@ "he": "1.2.0", "js-yaml": "4.1.0", "log-symbols": "4.1.0", - "minimatch": "3.0.4", + "minimatch": "4.2.1", "ms": "2.1.3", - "nanoid": "3.2.0", + "nanoid": "3.3.1", "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", @@ -10830,9 +9364,13 @@ "yargs-unparser": "2.0.0" }, "dependencies": { + "ansi-colors": { + "version": "4.1.1", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, "debug": { "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "dev": true, "requires": { @@ -10841,7 +9379,6 @@ "dependencies": { "ms": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true } @@ -10849,147 +9386,114 @@ }, "escape-string-regexp": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "glob": { + "version": "7.2.0", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "requires": { - "argparse": "^2.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "dependencies": { + "minimatch": { + "version": "3.1.2", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } } }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "minimatch": { + "version": "4.2.1", + "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", "dev": true, "requires": { - "p-locate": "^5.0.0" + "brace-expansion": "^1.1.7" } }, "ms": { "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, "serialize-javascript": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", "dev": true, "requires": { "randombytes": "^2.1.0" } }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "yargs": { + "version": "16.2.0", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "requires": { - "has-flag": "^4.0.0" + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" } - }, - "yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true } } }, "mri": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", "dev": true }, + "ms": { + "version": "2.1.2", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "mylas": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/mylas/-/mylas-2.1.11.tgz", + "integrity": "sha512-krnPUl3n9/k52FGCltWMYcqp9SttxjRJEy0sWLk+g7mIa7wnZrmNSZ40Acx7ghzRSOsxt2rEqMbaq4jWlnTDKg==", + "dev": true + }, "nanoid": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz", - "integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==", + "version": "3.3.1", + "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", "dev": true }, "neo-async": { "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, "node-fetch": { "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", "requires": { "whatwg-url": "^5.0.0" - }, - "dependencies": { - "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" - }, - "webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" - }, - "whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - } } }, "node-releases": { - "version": "1.1.73", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.73.tgz", - "integrity": "sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg==", + "version": "2.0.6", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", "dev": true }, "normalize-path": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, "npm-run-path": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, "requires": { @@ -10998,20 +9502,17 @@ }, "object-get": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/object-get/-/object-get-2.1.1.tgz", "integrity": "sha512-7n4IpLMzGGcLEMiQKsNR7vCe+N5E9LORFrtNUVy4sO3dj9a3HedZCxEL2T7QuLhcHN1NBuBsMOKaOsAYI9IIvg==", "dev": true }, "object-to-spawn-args": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/object-to-spawn-args/-/object-to-spawn-args-2.0.1.tgz", "integrity": "sha512-6FuKFQ39cOID+BMZ3QaphcC8Y4cw6LXBLyIgPU+OhIYwviJamPAn+4mITapnSBQrejB+NNp+FMskhD8Cq+Ys3w==", "dev": true }, "once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "requires": { "wrappy": "1" @@ -11019,7 +9520,6 @@ }, "onetime": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, "requires": { @@ -11028,19 +9528,16 @@ }, "opener": { "version": "1.5.2", - "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", "dev": true }, "ospath": { "version": "1.2.2", - "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz", - "integrity": "sha1-EnZjl3Sj+O8lcvf+QoDg6kVQwHs=", + "integrity": "sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==", "dev": true }, "p-limit": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "requires": { @@ -11048,28 +9545,15 @@ } }, "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "version": "5.0.0", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "requires": { - "p-limit": "^2.2.0" - }, - "dependencies": { - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - } + "p-limit": "^3.0.2" } }, "p-map": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", "dev": true, "requires": { @@ -11078,81 +9562,131 @@ }, "p-try": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, "path-exists": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true }, "path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true }, "path-key": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, "path-parse": { "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, "path-type": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true }, "pend": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", "dev": true }, "performance-now": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "dev": true + }, + "picocolors": { + "version": "1.0.0", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "dev": true }, "picomatch": { + "version": "2.3.1", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "pify": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true }, "pkg-dir": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, "requires": { "find-up": "^4.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + } + } + }, + "plimit-lit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/plimit-lit/-/plimit-lit-1.3.0.tgz", + "integrity": "sha512-63qOoSzggsjBHPVbaKeEtsO8oWTeyJxUfVRLkoc59pRkDiCCLvqn2tCgAkfbejrx+V5zJtlG2wqkk/Db6cwlVQ==", + "dev": true, + "requires": { + "queue-lit": "^1.3.0" } }, "portfinder": { - "version": "1.0.28", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", - "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", + "version": "1.0.29", + "integrity": "sha512-Z5+DarHWCKlufshB9Z1pN95oLtANoY5Wn9X3JGELGyQ6VhEcBfT2t+1fGUBq7MwUant6g/mqowH+4HifByPbiQ==", "dev": true, "requires": { - "async": "^2.6.2", - "debug": "^3.1.1", - "mkdirp": "^0.5.5" + "async": "^2.6.4", + "debug": "^3.2.7", + "mkdirp": "^0.5.6" }, "dependencies": { + "async": { + "version": "2.6.4", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + }, "debug": { "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "requires": { @@ -11160,43 +9694,32 @@ } }, "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "version": "0.5.6", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, "requires": { - "minimist": "^1.2.5" + "minimist": "^1.2.6" } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true } } }, "pretty-bytes": { "version": "5.6.0", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", "dev": true }, "proxy-from-env": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", - "integrity": "sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=", + "integrity": "sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==", "dev": true }, "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "version": "1.9.0", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", "dev": true }, "pump": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, "requires": { @@ -11206,31 +9729,27 @@ }, "punycode": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "version": "6.5.3", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", "dev": true }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "queue-lit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/queue-lit/-/queue-lit-1.3.0.tgz", + "integrity": "sha512-3HpQ7bD2ct56qPithPr5IzH2Oij3WVPcgevCJ+mI9HAfVJKCqQ2yiFYgb4AUkLfsCmmbVDy9luvE97Ztzr5eBg==", "dev": true }, "queue-microtask": { "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, "randombytes": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, "requires": { @@ -11239,7 +9758,6 @@ }, "readdirp": { "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "requires": { @@ -11247,9 +9765,8 @@ } }, "rechoir": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.0.tgz", - "integrity": "sha512-ADsDEH2bvbjltXEP+hTIAmeFekTFK0V2BTxMkok6qILyAJEXV0AFfoWcAq4yfll5VdIMd/RVXq0lR+wQi5ZU3Q==", + "version": "0.7.1", + "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", "dev": true, "requires": { "resolve": "^1.9.0" @@ -11257,8 +9774,7 @@ }, "reduce-extract": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/reduce-extract/-/reduce-extract-1.0.0.tgz", - "integrity": "sha1-Z/I4W+2mUGG19fQxJmLosIDKFSU=", + "integrity": "sha512-QF8vjWx3wnRSL5uFMyCjDeDc5EBMiryoT9tz94VvgjKfzecHAVnqmXAwQDcr7X4JmLc2cjkjFGCVzhMqDjgR9g==", "dev": true, "requires": { "test-value": "^1.0.1" @@ -11266,8 +9782,7 @@ "dependencies": { "array-back": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz", - "integrity": "sha1-ZEun8JX3/898Q7Xw3DnTwfA8Bjs=", + "integrity": "sha512-1WxbZvrmyhkNoeYcizokbmh5oiOCIfyvGtcqbK3Ls1v1fKcquzxnQSceOx6tzq7jmai2kFLWIpGND2cLhH6TPw==", "dev": true, "requires": { "typical": "^2.6.0" @@ -11275,8 +9790,7 @@ }, "test-value": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/test-value/-/test-value-1.1.0.tgz", - "integrity": "sha1-oJE29y7AQ9J8iTcHwrFZv6196T8=", + "integrity": "sha512-wrsbRo7qP+2Je8x8DsK8ovCGyxe3sYfQwOraIY/09A2gFXU9DYKiTF14W4ki/01AEh56kMzAmlj9CaHGDDUBJA==", "dev": true, "requires": { "array-back": "^1.0.2", @@ -11286,21 +9800,18 @@ } }, "reduce-flatten": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-1.0.1.tgz", - "integrity": "sha1-JYx479FT3fk8tWEjf2EYTzaW4yc=", + "version": "3.0.1", + "integrity": "sha512-bYo+97BmUUOzg09XwfkwALt4PQH1M5L0wzKerBt6WLm3Fhdd43mMS89HiT1B9pJIqko/6lWx3OnV4J9f2Kqp5Q==", "dev": true }, "reduce-unique": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/reduce-unique/-/reduce-unique-2.0.1.tgz", "integrity": "sha512-x4jH/8L1eyZGR785WY+ePtyMNhycl1N2XOLxhCbzZFaqF4AXjLzqSxa2UHgJ2ZVR/HHyPOvl1L7xRnW8ye5MdA==", "dev": true }, "reduce-without": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/reduce-without/-/reduce-without-1.0.1.tgz", - "integrity": "sha1-aK0OrRGFXJo31OglbBW7+Hly/Iw=", + "integrity": "sha512-zQv5y/cf85sxvdrKPlfcRzlDn/OqKFThNimYmsS3flmkioKvkUGn2Qg9cJVoQiEvdxFGLE0MQER/9fZ9sUqdxg==", "dev": true, "requires": { "test-value": "^2.0.0" @@ -11308,8 +9819,7 @@ "dependencies": { "array-back": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz", - "integrity": "sha1-ZEun8JX3/898Q7Xw3DnTwfA8Bjs=", + "integrity": "sha512-1WxbZvrmyhkNoeYcizokbmh5oiOCIfyvGtcqbK3Ls1v1fKcquzxnQSceOx6tzq7jmai2kFLWIpGND2cLhH6TPw==", "dev": true, "requires": { "typical": "^2.6.0" @@ -11317,8 +9827,7 @@ }, "test-value": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/test-value/-/test-value-2.1.0.tgz", - "integrity": "sha1-Edpv9nDzRxpztiXKTz/c97t0gpE=", + "integrity": "sha512-+1epbAxtKeXttkGFMTX9H42oqzOTufR1ceCF+GYA5aOmvaPq9wd4PUS8329fn2RRLGNeUkgRLnVpycjx8DsO2w==", "dev": true, "requires": { "array-back": "^1.0.3", @@ -11329,7 +9838,6 @@ }, "remark-parse": { "version": "10.0.1", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-10.0.1.tgz", "integrity": "sha512-1fUyHr2jLsVOkhbvPRBJ5zTKZZyD6yZzYaWCS6BPBdQ8vEMBCH+9zNCDA6tET/zHCi/jLqjCWtlJZUPk+DbnFw==", "dev": true, "requires": { @@ -11340,8 +9848,7 @@ }, "request-progress": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", - "integrity": "sha1-TKdUCBx/7GP1BeT6qCWqBs1mnb4=", + "integrity": "sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==", "dev": true, "requires": { "throttleit": "^1.0.0" @@ -11349,25 +9856,21 @@ }, "require-directory": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true }, "require-main-filename": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, "requires-port": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "dev": true }, "requizzle": { "version": "0.2.3", - "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.3.tgz", "integrity": "sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==", "dev": true, "requires": { @@ -11375,18 +9878,17 @@ } }, "resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "version": "1.22.1", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", "dev": true, "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" } }, "resolve-cwd": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, "requires": { @@ -11395,13 +9897,11 @@ }, "resolve-from": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true }, "restore-cursor": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "dev": true, "requires": { @@ -11411,19 +9911,16 @@ }, "reusify": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true }, "rfdc": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", "dev": true }, "rimraf": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, "requires": { @@ -11432,7 +9929,6 @@ }, "run-parallel": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, "requires": { @@ -11440,9 +9936,8 @@ } }, "rxjs": { - "version": "7.5.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.2.tgz", - "integrity": "sha512-PwDt186XaL3QN5qXj/H9DGyHhP3/RYYgZZwqBv9Tv8rsAaiwFH1IsJJlcgD37J7UW5a6O67qX0KWKS3/pu0m4w==", + "version": "7.5.6", + "integrity": "sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw==", "dev": true, "requires": { "tslib": "^2.1.0" @@ -11450,7 +9945,6 @@ }, "sade": { "version": "1.8.1", - "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", "dev": true, "requires": { @@ -11458,37 +9952,40 @@ } }, "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "version": "5.2.1", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true }, "safer-buffer": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, "schema-utils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.0.tgz", - "integrity": "sha512-tTEaeYkyIhEZ9uWgAjDerWov3T9MgX8dhhy2r0IGeeX4W8ngtGl1++dUve/RUqzuaASSh7shwCDJjEzthxki8w==", + "version": "3.1.1", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", "dev": true, "requires": { - "@types/json-schema": "^7.0.7", + "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", "ajv-keywords": "^3.5.2" } }, "secure-compare": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", - "integrity": "sha1-8aAymzCLIh+uN7mXTz1XjQypmeM=", + "integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==", "dev": true }, + "semver": { + "version": "7.3.7", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, "serialize-javascript": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", "dev": true, "requires": { @@ -11497,13 +9994,11 @@ }, "set-blocking": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, "shallow-clone": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", "dev": true, "requires": { @@ -11512,7 +10007,6 @@ }, "shebang-command": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "requires": { @@ -11521,63 +10015,37 @@ }, "shebang-regex": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, + "shell-quote": { + "version": "1.7.3", + "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==", + "dev": true + }, "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "version": "3.0.7", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, "slash": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, "slice-ansi": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", "dev": true, "requires": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", "is-fullwidth-code-point": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - } } }, "sort-array": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/sort-array/-/sort-array-4.1.4.tgz", - "integrity": "sha512-GVFN6Y1sHKrWaSYOJTk9093ZnrBMc9sP3nuhANU44S4xg3rE6W5Z5WyamuT8VpMBbssnetx5faKCua0LEmUnSw==", + "version": "4.1.5", + "integrity": "sha512-Ya4peoS1fgFN42RN1REk2FgdNOeLIEMKFGJvs7VTP3OklF8+kl2SkpVliZ4tk/PurWsrWRsdNdU+tgyOBkB9sA==", "dev": true, "requires": { "array-back": "^5.0.0", @@ -11586,13 +10054,11 @@ "dependencies": { "array-back": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-5.0.0.tgz", "integrity": "sha512-kgVWwJReZWmVuWOQKEOohXKJX+nD02JAZ54D1RRWlv8L0NebauKAaFxACKzB74RTclt1+WNz5KHaLRDAPZbDEw==", "dev": true }, "typical": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/typical/-/typical-6.0.1.tgz", "integrity": "sha512-+g3NEp7fJLe9DPa1TArHm9QAA7YciZmWnfAqEaFrBihQ7epOv9i99rjtgb6Iz0wh3WuQDjsCTDfgRoGnmHN81A==", "dev": true } @@ -11600,13 +10066,11 @@ }, "source-map": { "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, "source-map-support": { "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "requires": { @@ -11616,13 +10080,11 @@ }, "spawn-command": { "version": "0.0.2-1", - "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz", - "integrity": "sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A=", + "integrity": "sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg==", "dev": true }, "sshpk": { "version": "1.17.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", "dev": true, "requires": { @@ -11639,8 +10101,7 @@ }, "stream-connect": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/stream-connect/-/stream-connect-1.0.2.tgz", - "integrity": "sha1-GLyB8u2zW4tdmoAJIAqYUxRCipc=", + "integrity": "sha512-68Kl+79cE0RGKemKkhxTSg8+6AGrqBt+cbZAXevg2iJ6Y3zX4JhA/sZeGzLpxW9cXhmqAcE7KnJCisUmIUfnFQ==", "dev": true, "requires": { "array-back": "^1.0.2" @@ -11648,8 +10109,7 @@ "dependencies": { "array-back": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz", - "integrity": "sha1-ZEun8JX3/898Q7Xw3DnTwfA8Bjs=", + "integrity": "sha512-1WxbZvrmyhkNoeYcizokbmh5oiOCIfyvGtcqbK3Ls1v1fKcquzxnQSceOx6tzq7jmai2kFLWIpGND2cLhH6TPw==", "dev": true, "requires": { "typical": "^2.6.0" @@ -11659,13 +10119,11 @@ }, "stream-via": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/stream-via/-/stream-via-1.0.4.tgz", "integrity": "sha512-DBp0lSvX5G9KGRDTkR/R+a29H+Wk2xItOF+MpZLLNDWbEV9tGPnqLPxHEYjmiz8xGtJHRIqmI+hCjmNzqoA4nQ==", "dev": true }, "string-width": { "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "requires": { @@ -11676,13 +10134,11 @@ "dependencies": { "ansi-regex": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "strip-ansi": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { @@ -11691,37 +10147,37 @@ } } }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - }, "strip-bom": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, "optional": true }, "strip-final-newline": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true }, "strip-json-comments": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, + "supports-color": { + "version": "8.1.1", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, "table-layout": { "version": "0.4.5", - "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-0.4.5.tgz", "integrity": "sha512-zTvf0mcggrGeTe/2jJ6ECkJHAQPIYEwDoqsiqBjI24mvRmQbInK5jq33fyypaCBxX08hMkfmdOqj6haT33EqWw==", "dev": true, "requires": { @@ -11734,7 +10190,6 @@ "dependencies": { "array-back": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", "dev": true, "requires": { @@ -11745,19 +10200,16 @@ }, "taffydb": { "version": "2.6.2", - "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", - "integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=", + "integrity": "sha512-y3JaeRSplks6NYQuCOj3ZFMO3j60rTwbuKCvZxsAraGYH2epusatvZ0baZYA01WsGqJBq/Dl6vOrMUJqyMj8kA==", "dev": true }, "tapable": { "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "dev": true }, "tar": { "version": "6.1.11", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", "dev": true, "requires": { @@ -11767,17 +10219,22 @@ "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" + }, + "dependencies": { + "chownr": { + "version": "2.0.0", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true + } } }, "temp-path": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/temp-path/-/temp-path-1.0.0.tgz", - "integrity": "sha1-JLFUOXOrRCiW2a02fdnL2/r+kYs=", + "integrity": "sha512-TvmyH7kC6ZVTYkqCODjJIbgvu0FKiwQpZ4D1aknE7xpcDf/qEOB8KZEK5ef2pfbVoiBhNWs3yx4y+ESMtNYmlg==", "dev": true }, "terser": { "version": "5.14.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", "dev": true, "requires": { @@ -11785,25 +10242,29 @@ "acorn": "^8.5.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + } } }, "terser-webpack-plugin": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.1.4.tgz", - "integrity": "sha512-C2WkFwstHDhVEmsmlCxrXUtVklS+Ir1A7twrYzrDrQQOIMOaVAYykaoo/Aq1K0QRkMoY2hhvDQY1cm4jnIMFwA==", + "version": "5.3.3", + "integrity": "sha512-Fx60G5HNYknNTNQnzQ1VePRuu89ZVYWfjRAeT5rITuCY/1b08s49e5kSQwHDirKZWuoKOBRFS98EUUoZ9kLEwQ==", "dev": true, "requires": { - "jest-worker": "^27.0.2", - "p-limit": "^3.1.0", - "schema-utils": "^3.0.0", + "@jridgewell/trace-mapping": "^0.3.7", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", "serialize-javascript": "^6.0.0", - "source-map": "^0.6.1", - "terser": "^5.7.0" + "terser": "^5.7.2" }, "dependencies": { "serialize-javascript": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", "dev": true, "requires": { @@ -11814,7 +10275,6 @@ }, "test-value": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/test-value/-/test-value-3.0.0.tgz", "integrity": "sha512-sVACdAWcZkSU9x7AOmJo5TqE+GyNJknHaHsMrR6ZnhjVlVN9Yx6FjHrsKZ3BjIpPCT68zYesPWkakrNupwfOTQ==", "dev": true, "requires": { @@ -11824,7 +10284,6 @@ "dependencies": { "array-back": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", "dev": true, "requires": { @@ -11833,21 +10292,24 @@ } } }, + "text-encoding": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.7.0.tgz", + "integrity": "sha512-oJQ3f1hrOnbRLOcwKz0Liq2IcrvDeZRHXhd9RgLrsT+DjWY/nty1Hi7v3dtkaEYbPYe0mUoOfzRrMwfXXwgPUA==", + "peer": true + }, "throttleit": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz", - "integrity": "sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw=", + "integrity": "sha512-rkTVqu6IjfQ/6+uNuuc3sZek4CEYxTJom3IktzgdSxcZqdARuebbA/f4QmAxMQIxqq9ZLEUkSYqvuk1I6VKq4g==", "dev": true }, "through": { "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, "tmp": { "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", "dev": true, "requires": { @@ -11856,7 +10318,6 @@ }, "to-regex-range": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "requires": { @@ -11865,7 +10326,6 @@ }, "tough-cookie": { "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", "dev": true, "requires": { @@ -11873,21 +10333,22 @@ "punycode": "^2.1.1" } }, + "tr46": { + "version": "0.0.3", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, "tree-kill": { "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", "dev": true }, "trough": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/trough/-/trough-2.0.2.tgz", - "integrity": "sha512-FnHq5sTMxC0sk957wHDzRnemFnNBvt/gSY99HzK8F7UP5WAbvP70yX5bd7CjEQkN+TjdxwI7g7lJ6podqrG2/w==", + "version": "2.1.0", + "integrity": "sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==", "dev": true }, "ts-mocha": { "version": "9.0.2", - "resolved": "https://registry.npmjs.org/ts-mocha/-/ts-mocha-9.0.2.tgz", "integrity": "sha512-WyQjvnzwrrubl0JT7EC1yWmNpcsU3fOuBFfdps30zbmFBgKniSaSOyZMZx+Wq7kytUs5CY+pEbSYEbGfIKnXTw==", "dev": true, "requires": { @@ -11897,22 +10358,29 @@ "dependencies": { "diff": { "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", "dev": true }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "optional": true, + "requires": { + "minimist": "^1.2.0" + } + }, "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "version": "0.5.6", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, "requires": { - "minimist": "^1.2.5" + "minimist": "^1.2.6" } }, "ts-node": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz", "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==", "dev": true, "requires": { @@ -11926,21 +10394,32 @@ "yn": "^2.0.0" } }, + "tsconfig-paths": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "dev": true, + "optional": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, "yn": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", - "integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=", + "integrity": "sha512-uTv8J/wiWTgUTg+9vLTi//leUl5vDQS6uii/emeTb2ssY7vl6QWf2fFbIIGjnhjvbdKlU0ed7QPgY1htTC86jQ==", "dev": true } } }, "ts-node": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.5.0.tgz", - "integrity": "sha512-6kEJKwVxAJ35W4akuiysfKwKmjkbYxwQMTBaAxo9KKAx/Yd26mPUyhGz3ji+EsJoAgrLqVsYHNuuYwQe22lbtw==", + "version": "10.9.1", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", "dev": true, "requires": { - "@cspotcode/source-map-support": "0.7.0", + "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", "@tsconfig/node12": "^1.0.7", "@tsconfig/node14": "^1.0.0", @@ -11951,53 +10430,47 @@ "create-require": "^1.1.0", "diff": "^4.0.1", "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.0", + "v8-compile-cache-lib": "^3.0.1", "yn": "3.1.1" }, "dependencies": { "diff": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true } } }, - "tsconfig-paths": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz", - "integrity": "sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg==", + "tsc-alias": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/tsc-alias/-/tsc-alias-1.7.0.tgz", + "integrity": "sha512-n/K6g8S7Ec7Y/A2Z77Ikp2Uv1S1ERtT63ni69XV4W1YPT4rnNmz8ItgIiJYvKfFnKfqcZQ81UPjoKpMTxaC/rg==", "dev": true, - "optional": true, "requires": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", - "minimist": "^1.2.0", - "strip-bom": "^3.0.0" + "chokidar": "^3.5.3", + "commander": "^9.0.0", + "globby": "^11.0.4", + "mylas": "^2.1.9", + "normalize-path": "^3.0.0", + "plimit-lit": "^1.2.6" }, "dependencies": { - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "optional": true, - "requires": { - "minimist": "^1.2.0" - } + "commander": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.0.tgz", + "integrity": "sha512-sRPT+umqkz90UA8M1yqYfnHlZA7fF6nSphDtxeywPZ49ysjxDQybzk13CL+mXekDRG92skbcqCLVovuCusNmFw==", + "dev": true } } }, "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "version": "2.4.0", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", "dev": true }, "tunnel-agent": { "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", "dev": true, "requires": { "safe-buffer": "^5.0.1" @@ -12005,14 +10478,12 @@ }, "tweetnacl": { "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", "dev": true }, "txm": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/txm/-/txm-8.0.1.tgz", - "integrity": "sha512-unvGVsgWIP10c3KYrfBMdBWILWsIjnDV4ljO32Juk1qpTzbZCNlpkjGMb+ix6VDH49JKa2V6gSzBnx0gzypp7A==", + "version": "8.0.2", + "integrity": "sha512-X6gUOeUAXzIrUvxp0zvOiJBM0l6Zk8NexaYugac6c6PW7dbrjgz6zC/rfSEjtd29VejI3VKvAnc6pDdOfSNKug==", "dev": true, "requires": { "async": "^3.2.1", @@ -12023,61 +10494,47 @@ "unified": "^10.1.1" }, "dependencies": { - "async": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", - "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==", - "dev": true - }, "supports-color": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.2.1.tgz", - "integrity": "sha512-Obv7ycoCTG51N7y175StI9BlAXrmgZrFhZOb0/PyjHBher/NmsdBgbbQ1Inhq+gIhz6+7Gb+jWF2Vqi7Mf1xnQ==", + "version": "9.2.2", + "integrity": "sha512-XC6g/Kgux+rJXmwokjm9ECpD6k/smUoS5LKlUCcsYr4IY3rW0XyAympon2RmxGrlnZURMpg5T18gWDP9CsHXFA==", "dev": true } } }, "type-fest": { "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true }, "typescript": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.2.tgz", - "integrity": "sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg==", + "version": "4.7.4", + "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", "dev": true }, "typical": { "version": "2.6.1", - "resolved": "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz", - "integrity": "sha1-XAgOXWYcu+OCWdLnCjxyU+hziB0=", + "integrity": "sha512-ofhi8kjIje6npGozTip9Fr8iecmYfEbS06i0JnIg+rh51KakryWF4+jX8lLKZVhy6N+ID45WYSFCxPOdTWCzNg==", "dev": true }, "uc.micro": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", "dev": true }, "uglify-js": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.0.tgz", - "integrity": "sha512-x+xdeDWq7FiORDvyIJ0q/waWd4PhjBNOm5dQUOq2AKC0IEjxOS66Ha9tctiVDGcRQuh69K7fgU5oRuTK4cysSg==", + "version": "3.16.3", + "integrity": "sha512-uVbFqx9vvLhQg0iBaau9Z75AxWJ8tqM9AV890dIZCLApF4rTcyHwmAvLeEdYRs+BzYWu8Iw81F79ah0EfTXbaw==", "dev": true, "optional": true }, "underscore": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.2.tgz", - "integrity": "sha512-ekY1NhRzq0B08g4bGuX4wd2jZx5GnKz6mKSqFL4nqBlfyMGiG10gDFhDTMEfYmDL6Jy0FUIZp7wiRB+0BP7J2g==", + "version": "1.13.4", + "integrity": "sha512-BQFnUDuAQ4Yf/cYY5LNrK9NCJFKriaRbD9uR1fTeXnBeoa97W0i41qkZfGO9pSo8I5KzjAcSY2XYtdf0oKd7KQ==", "dev": true }, "unified": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.1.tgz", - "integrity": "sha512-v4ky1+6BN9X3pQrOdkFIPWAaeDsHPE1svRDxq7YpTc2plkIqFMwukfqM+l0ewpP9EfwARlt9pPFAeWYhHm8X9w==", + "version": "10.1.2", + "integrity": "sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==", "dev": true, "requires": { "@types/unist": "^2.0.0", @@ -12087,19 +10544,10 @@ "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^5.0.0" - }, - "dependencies": { - "is-plain-obj": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.0.0.tgz", - "integrity": "sha512-NXRbBtUdBioI73y/HmOhogw/U5msYPC9DAtGkJXeFcFWSFZw0mCUsPxk/snTuJHzNKA8kLBK4rH97RMB1BfCXw==", - "dev": true - } } }, "union": { "version": "0.5.0", - "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==", "dev": true, "requires": { @@ -12107,9 +10555,8 @@ } }, "unist-util-stringify-position": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.0.tgz", - "integrity": "sha512-SdfAl8fsDclywZpfMDTVDxA2V7LjtRDTOFd44wUJamgl6OlVngsqWjxvermMYf60elWHbxhuRCZml7AnuXCaSA==", + "version": "3.0.2", + "integrity": "sha512-7A6eiDCs9UtjcwZOcCpM4aPII3bAAGv13E96IkawkOAW0OhH+yRxtY0lzo8KiHpzEMfH7Q+FizUmwp8Iqy5EWg==", "dev": true, "requires": { "@types/unist": "^2.0.0" @@ -12117,53 +10564,44 @@ }, "universalify": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", "dev": true }, "untildify": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", "dev": true }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "update-browserslist-db": { + "version": "1.0.5", + "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==", "dev": true, "requires": { - "punycode": "^2.1.0" + "escalade": "^3.1.1", + "picocolors": "^1.0.0" } }, - "url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "uri-js": { + "version": "4.4.1", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - }, - "dependencies": { - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", - "dev": true - } + "punycode": "^2.1.0" } }, "url-join": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", "dev": true }, + "uuid": { + "version": "8.3.2", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true + }, "uvu": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.3.tgz", - "integrity": "sha512-brFwqA3FXzilmtnIyJ+CxdkInkY/i4ErvP7uV0DnUVxQcQ55reuHphorpF+tZoVHK2MniZ/VJzI7zJQoc9T9Yw==", + "version": "0.5.6", + "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", "dev": true, "requires": { "dequal": "^2.0.0", @@ -12174,25 +10612,29 @@ }, "v8-compile-cache-lib": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true }, "verror": { "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", "dev": true, "requires": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", "extsprintf": "^1.2.0" + }, + "dependencies": { + "core-util-is": { + "version": "1.0.2", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true + } } }, "vfile": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.0.tgz", - "integrity": "sha512-Tj44nY/48OQvarrE4FAjUfrv7GZOYzPbl5OD65HxVKwLJKMPU7zmfV8cCgCnzKWnSfYG2f3pxu+ALqs7j22xQQ==", + "version": "5.3.4", + "integrity": "sha512-KI+7cnst03KbEyN1+JE504zF5bJBZa+J+CrevLeyIMq0aPU681I2rQ5p4PlnQ6exFtWiUrg26QUdFMnAKR6PIw==", "dev": true, "requires": { "@types/unist": "^2.0.0", @@ -12202,9 +10644,8 @@ } }, "vfile-message": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.0.tgz", - "integrity": "sha512-4QJbBk+DkPEhBXq3f260xSaWtjE4gPKOfulzfMFF8ZNwaPZieWsg3iVlcmF04+eebzpcpeXOOFMfrYzJHVYg+g==", + "version": "3.1.2", + "integrity": "sha512-QjSNP6Yxzyycd4SVOtmKKyTsSvClqBPJcd00Z0zuPj3hOIjg0rUPG6DbFGPvUKRgYyaIWLPKpuEclcuvb3H8qA==", "dev": true, "requires": { "@types/unist": "^2.0.0", @@ -12213,13 +10654,11 @@ }, "walk-back": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/walk-back/-/walk-back-5.1.0.tgz", "integrity": "sha512-Uhxps5yZcVNbLEAnb+xaEEMdgTXl9qAQDzKYejG2AZ7qPwRQ81lozY9ECDbjLPNWm7YsO1IK5rsP1KoQzXAcGA==", "dev": true }, "wasm-opt": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/wasm-opt/-/wasm-opt-1.3.0.tgz", "integrity": "sha512-24+IOboX4Sav0bI8Krwf0Y6dnpN4KxYtqpl0qWt86qVLsmayUqx1KMBrJTlQWNC+/dsqzQJjK6QvxNJsZYOgJg==", "dev": true, "requires": { @@ -12229,7 +10668,6 @@ }, "wasm-pack": { "version": "0.10.1", - "resolved": "https://registry.npmjs.org/wasm-pack/-/wasm-pack-0.10.1.tgz", "integrity": "sha512-bw480KaaJQhL6UX8wAm6YCO497uaULDrsvUey3EB86dKAfFc6qCGVN1kItcwUklEeufonwo9mwz9/fy9xLOPtQ==", "dev": true, "requires": { @@ -12237,60 +10675,61 @@ } }, "watchpack": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz", - "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==", + "version": "2.4.0", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", "dev": true, "requires": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" } }, + "webidl-conversions": { + "version": "3.0.1", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, "webpack": { - "version": "5.67.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.67.0.tgz", - "integrity": "sha512-LjFbfMh89xBDpUMgA1W9Ur6Rn/gnr2Cq1jjHFPo4v6a79/ypznSYbAyPgGhwsxBtMIaEmDD1oJoA7BEYw/Fbrw==", + "version": "5.74.0", + "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==", "dev": true, "requires": { - "@types/eslint-scope": "^3.7.0", - "@types/estree": "^0.0.50", + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", "@webassemblyjs/ast": "1.11.1", "@webassemblyjs/wasm-edit": "1.11.1", "@webassemblyjs/wasm-parser": "1.11.1", - "acorn": "^8.4.1", + "acorn": "^8.7.1", "acorn-import-assertions": "^1.7.6", "browserslist": "^4.14.5", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.8.3", + "enhanced-resolve": "^5.10.0", "es-module-lexer": "^0.9.0", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.9", - "json-parse-better-errors": "^1.0.2", + "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", "schema-utils": "^3.1.0", "tapable": "^2.1.1", "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.3.1", + "watchpack": "^2.4.0", "webpack-sources": "^3.2.3" } }, "webpack-cli": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.9.2.tgz", - "integrity": "sha512-m3/AACnBBzK/kMTcxWHcZFPrw/eQuY4Df1TxvIWfWM2x7mRqBQCqKEd96oCUa9jkapLBaFfRce33eGDb4Pr7YQ==", + "version": "4.10.0", + "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", "dev": true, "requires": { "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^1.1.1", - "@webpack-cli/info": "^1.4.1", - "@webpack-cli/serve": "^1.6.1", + "@webpack-cli/configtest": "^1.2.0", + "@webpack-cli/info": "^1.5.0", + "@webpack-cli/serve": "^1.7.0", "colorette": "^2.0.14", "commander": "^7.0.0", - "execa": "^5.0.0", + "cross-spawn": "^7.0.3", "fastest-levenshtein": "^1.0.12", "import-local": "^3.0.2", "interpret": "^2.2.0", @@ -12298,15 +10737,8 @@ "webpack-merge": "^5.7.3" }, "dependencies": { - "colorette": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", - "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", - "dev": true - }, "commander": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", "dev": true } @@ -12314,7 +10746,6 @@ }, "webpack-merge": { "version": "5.8.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", "dev": true, "requires": { @@ -12324,33 +10755,27 @@ }, "webpack-sources": { "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", "dev": true }, "whatwg-encoding": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", "dev": true, "requires": { "iconv-lite": "0.6.3" - }, - "dependencies": { - "iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - } + } + }, + "whatwg-url": { + "version": "5.0.0", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" } }, "which": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "requires": { @@ -12359,68 +10784,42 @@ }, "which-module": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", "dev": true }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "requires": { - "string-width": "^1.0.2 || 2" - }, - "dependencies": { - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - } - } - }, "wildcard": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", "dev": true }, "wordwrap": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", "dev": true }, "wordwrapjs": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-3.0.0.tgz", "integrity": "sha512-mO8XtqyPvykVCsrwj5MlOVWvSnCdT+C+QVbm6blradR7JExAhbkZ7hZ9A+9NUtwzSqrlUo9a67ws0EiILrvRpw==", "dev": true, "requires": { "reduce-flatten": "^1.0.1", "typical": "^2.6.1" + }, + "dependencies": { + "reduce-flatten": { + "version": "1.0.1", + "integrity": "sha512-j5WfFJfc9CoXv/WbwVLHq74i/hdTUpy+iNC534LxczMRP67vJeK3V9JOdnL0N1cIRbn9mYhE2yVjvvKXDxvNXQ==", + "dev": true + } } }, "workerpool": { "version": "6.2.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", "dev": true }, "wrap-ansi": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "requires": { @@ -12431,37 +10830,11 @@ "dependencies": { "ansi-regex": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, "strip-ansi": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { @@ -12472,52 +10845,52 @@ }, "wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, "xmlcreate": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", "dev": true }, "y18n": { "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true }, "yallist": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "version": "17.5.1", + "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", "dev": true, "requires": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.0", + "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "yargs-parser": "^21.0.0" + }, + "dependencies": { + "yargs-parser": { + "version": "21.1.1", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true + } } }, "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "version": "20.2.4", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", "dev": true }, "yargs-unparser": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", "dev": true, "requires": { @@ -12527,24 +10900,16 @@ "is-plain-obj": "^2.1.0" }, "dependencies": { - "camelcase": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", - "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", - "dev": true - }, - "decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "is-plain-obj": { + "version": "2.1.0", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true } } }, "yauzl": { "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", "dev": true, "requires": { "buffer-crc32": "~0.2.3", @@ -12553,13 +10918,11 @@ }, "yn": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true }, "yocto-queue": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true } diff --git a/bindings/wasm/package.json b/bindings/wasm/package.json index 88e3ce2432..bf1a8039c9 100644 --- a/bindings/wasm/package.json +++ b/bindings/wasm/package.json @@ -11,8 +11,8 @@ }, "scripts": { "build:src": "cargo build --lib --release --target wasm32-unknown-unknown", - "bundle:nodejs": "wasm-bindgen target/wasm32-unknown-unknown/release/identity_wasm.wasm --typescript --weak-refs --target nodejs --out-dir node && node ./build/node", - "bundle:web": "wasm-bindgen target/wasm32-unknown-unknown/release/identity_wasm.wasm --typescript --weak-refs --target web --out-dir web && node ./build/web", + "bundle:nodejs": "wasm-bindgen target/wasm32-unknown-unknown/release/identity_wasm.wasm --typescript --weak-refs --target nodejs --out-dir node && node ./build/node && tsc --project ./lib/tsconfig.json && tsc-alias -p ./lib/tsconfig.json", + "bundle:web": "wasm-bindgen target/wasm32-unknown-unknown/release/identity_wasm.wasm --typescript --weak-refs --target web --out-dir web && node ./build/web && tsc --project ./lib/tsconfig.web.json && tsc-alias -p ./lib/tsconfig.web.json", "build:nodejs": "npm run build:src && npm run bundle:nodejs && wasm-opt -O node/identity_wasm_bg.wasm -o node/identity_wasm_bg.wasm", "build:web": "npm run build:src && npm run bundle:web && wasm-opt -O web/identity_wasm_bg.wasm -o web/identity_wasm_bg.wasm", "build:docs": "node ./build/docs", @@ -21,15 +21,17 @@ "example:node": "node examples/dist/node.js", "example:browser": "http-server ./examples/dist/ -c-1 -o ", "example:account": "ts-node ./examples-account/src/node.ts", + "example:stardust": "ts-node ./examples-stardust/src/main.ts", "test": "npm run test:unit && npm run test:unit:node && npm run test:examples", - "test:examples": "concurrently -g --timings \"npm:test:node\" \"npm:test:account:node\" \"npm:test:browser:parallel\" \"npm:test:readme\"", + "test:examples": "concurrently -g --timings \"npm:test:node\" \"npm:test:account:node\" \"npm:test:stardust\" \"npm:test:browser:parallel\" \"npm:test:readme\"", "test:node": "mocha ./examples/dist/tests/node/*.js --parallel --jobs 4 --retries 3 --timeout 180000 --exit", - "test:account:node": "ts-mocha ./examples-account/src/tests/*.ts --parallel --jobs 4 --retries 3 --timeout 180000 --exit", + "test:account:node": "ts-mocha -p tsconfig.node.json ./examples-account/src/tests/*.ts --parallel --jobs 4 --retries 3 --timeout 180000 --exit", + "test:stardust": "ts-mocha -p tsconfig.node.json ./examples-stardust/src/tests/*.ts --parallel --jobs 4 --retries 3 --timeout 180000 --exit", "test:browser:parallel": "cypress-parallel -s test:browser -t 4 -d cypress/integration -a '\"--quiet\"'", "test:browser": "cypress run --headless", "test:readme": "mocha ./tests/txm_readme.js --retries 3 --timeout 180000 --exit", "test:unit": "wasm-pack test --release --node", - "test:unit:node": "ts-mocha ./tests/*.ts --parallel --exit", + "test:unit:node": "ts-mocha -p tsconfig.node.json ./tests/*.ts --parallel --exit", "cypress": "cypress open" }, "contributors": [ @@ -52,26 +54,35 @@ "node/*" ], "devDependencies": { + "@iota/crypto.js": "^1.9.0-stardust.6", "@types/mocha": "^9.1.0", "concurrently": "^7.0.0", "copy-webpack-plugin": "^7.0.0", "cypress": "^9.3.1", "cypress-parallel": "^0.1.8", + "fs-extra": "^10.1.0", "http-server": "^14.1.0", "jsdoc-to-markdown": "^7.1.1", "mocha": "^9.2.0", "ts-mocha": "^9.0.2", - "ts-node": "^10.4.0", + "ts-node": "^10.9.1", + "tsc-alias": "^1.7.0", "txm": "^8.0.1", - "typescript": "^4.6.2", + "typescript": "^4.7.2", "wasm-opt": "^1.3.0", "wasm-pack": "0.10.1", "webpack": "^5.67.0", "webpack-cli": "^4.9.2" }, "dependencies": { + "@iota/types": "^1.0.0-beta.11", + "@types/node-fetch": "^2.6.2", "node-fetch": "^2.6.7" }, + "peerDependencies": { + "@cycraig/iota-client-wasm": "^0.5.0-alpha.1", + "@iota/iota.js": "^1.9.0-stardust.25" + }, "engines": { "node": ">=16" } diff --git a/bindings/wasm/rustfmt.toml b/bindings/wasm/rustfmt.toml deleted file mode 120000 index d74601ba2f..0000000000 --- a/bindings/wasm/rustfmt.toml +++ /dev/null @@ -1,8 +0,0 @@ -comment_width = 120 -format_code_in_doc_comments = true -license_template_path = ".license_template" -max_width = 120 -normalize_comments = false -normalize_doc_attributes = false -tab_spaces = 2 -wrap_comments = true diff --git a/bindings/wasm/src/error.rs b/bindings/wasm/src/error.rs index 512f44b942..4e5fad6931 100644 --- a/bindings/wasm/src/error.rs +++ b/bindings/wasm/src/error.rs @@ -178,6 +178,15 @@ impl From for WasmError<'_> { } } +impl From for WasmError<'_> { + fn from(error: identity_stardust::block::Error) -> Self { + Self { + name: Cow::Borrowed("bee_block::Error"), + message: Cow::Owned(error.to_string()), + } + } +} + impl From for WasmError<'_> { fn from(error: identity_iota::credential::CompoundCredentialValidationError) -> Self { Self { @@ -223,6 +232,21 @@ impl JsValueResult { AccountStorageError::JsError(error_string) }) } + + /// Consumes the struct and returns a Result<_, identity_stardust::Error>, leaving an `Ok` value untouched. + pub fn to_stardust_error(self) -> StdResult { + self.0.map_err(|js_value| { + let error_string: String = match wasm_bindgen::JsCast::dyn_into::(js_value) { + Ok(js_err) => ToString::to_string(&js_err.to_string()), + Err(js_val) => { + // Fall back to debug formatting if the error is not a proper JS Error instance. + format!("{js_val:?}") + } + }; + + identity_stardust::Error::JsError(error_string) + }) + } } impl From> for JsValueResult { diff --git a/bindings/wasm/src/stardust/identity_client.rs b/bindings/wasm/src/stardust/identity_client.rs new file mode 100644 index 0000000000..1620c20ed0 --- /dev/null +++ b/bindings/wasm/src/stardust/identity_client.rs @@ -0,0 +1,114 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use core::fmt::Debug; +use core::fmt::Formatter; + +use identity_stardust::block::output::dto::AliasOutputDto; +use identity_stardust::block::output::AliasId; +use identity_stardust::block::output::AliasOutput; +use identity_stardust::block::output::OutputId; +use identity_stardust::block::output::RentStructure; +use identity_stardust::block::output::RentStructureBuilder; +use identity_stardust::StardustIdentityClient; +use js_sys::Promise; +use wasm_bindgen::prelude::*; +use wasm_bindgen_futures::JsFuture; + +use crate::error::JsValueResult; + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(typescript_type = "IStardustIdentityClient")] + pub type WasmStardustIdentityClient; + + #[wasm_bindgen(method, js_name = getNetworkHrp)] + pub fn get_network_hrp(this: &WasmStardustIdentityClient) -> JsValue; + + #[allow(non_snake_case)] + #[wasm_bindgen(method, js_name = getAliasOutput)] + pub fn get_alias_output(this: &WasmStardustIdentityClient, aliasId: String) -> JsValue; + + #[wasm_bindgen(method, js_name = getRentStructure)] + pub fn get_rent_structure(this: &WasmStardustIdentityClient) -> JsValue; +} + +impl Debug for WasmStardustIdentityClient { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + f.write_str("WasmStardustIdentityClient") + } +} + +#[async_trait::async_trait(?Send)] +impl StardustIdentityClient for WasmStardustIdentityClient { + async fn get_network_hrp(&self) -> Result { + let promise: Promise = Promise::resolve(&WasmStardustIdentityClient::get_network_hrp(self)); + let result: JsValueResult = JsFuture::from(promise).await.into(); + let js: JsValue = result.to_stardust_error()?; + let network_hrp = match js.as_string() { + Some(hrp) => hrp, + None => js.into_serde().map_err(|err| { + identity_stardust::Error::JsError(format!("get_network_hrp failed to deserialize String: {}", err)) + })?, + }; + Ok(network_hrp) + } + + async fn get_alias_output(&self, id: AliasId) -> Result<(OutputId, AliasOutput), identity_stardust::Error> { + let promise: Promise = Promise::resolve(&WasmStardustIdentityClient::get_alias_output(self, id.to_string())); + let result: JsValueResult = JsFuture::from(promise).await.into(); + let tuple: js_sys::Array = js_sys::Array::from(&result.to_stardust_error()?); + let mut iter: js_sys::ArrayIter = tuple.iter(); + + let output_id: OutputId = iter + .next() + .ok_or_else(|| identity_stardust::Error::JsError("get_alias_output expected a tuple of size 2".to_owned()))? + .into_serde() + .map_err(|err| { + identity_stardust::Error::JsError(format!("get_alias_output failed to deserialize OutputId: {}", err)) + })?; + let alias_dto: AliasOutputDto = iter + .next() + .ok_or_else(|| identity_stardust::Error::JsError("get_alias_output expected a tuple of size 2".to_owned()))? + .into_serde() + .map_err(|err| { + identity_stardust::Error::JsError(format!( + "get_alias_output failed to deserialize AliasOutputDto: {}", + err + )) + })?; + let alias_output = AliasOutput::try_from(&alias_dto).map_err(|err| { + identity_stardust::Error::JsError(format!("get_alias_output failed to convert AliasOutputDto: {}", err)) + })?; + Ok((output_id, alias_output)) + } + + async fn get_rent_structure(&self) -> Result { + let promise: Promise = Promise::resolve(&WasmStardustIdentityClient::get_rent_structure(self)); + let result: JsValueResult = JsFuture::from(promise).await.into(); + let rent_structure: RentStructureBuilder = result + .to_stardust_error()? + .into_serde() + .map_err(|err| identity_stardust::Error::JsError(format!("get_rent_structure failed to deserialize: {}", err)))?; + Ok(rent_structure.finish()) + } +} + +#[wasm_bindgen(typescript_custom_section)] +const I_STARDUST_IDENTITY_CLIENT: &'static str = r#" +import type { IAliasOutput, IRent } from '@iota/types'; +/** Helper interface necessary for `StardustIdentityClientExt`. */ +interface IStardustIdentityClient { + /** + * Return the Bech32 human-readable part (HRP) of the network. + * + * E.g. "iota", "atoi", "smr", "rms". + */ + getNetworkHrp(): Promise; + + /** Resolve an Alias identifier, returning its latest `OutputId` and `AliasOutput`. */ + getAliasOutput(aliasId: string): Promise<[string, IAliasOutput]>; + + /** Return the rent structure of the network, indicating the byte costs for outputs. */ + getRentStructure(): Promise; +}"#; diff --git a/bindings/wasm/src/stardust/identity_client_ext.rs b/bindings/wasm/src/stardust/identity_client_ext.rs new file mode 100644 index 0000000000..e6549c8175 --- /dev/null +++ b/bindings/wasm/src/stardust/identity_client_ext.rs @@ -0,0 +1,173 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use identity_stardust::block::address::dto::AddressDto; +use identity_stardust::block::address::Address; +use identity_stardust::block::output::dto::AliasOutputDto; +use identity_stardust::block::output::AliasOutput; +use identity_stardust::block::output::RentStructure; +use identity_stardust::StardustDID; +use identity_stardust::StardustDocument; +use identity_stardust::StardustIdentityClientExt; +use js_sys::Promise; +use wasm_bindgen::prelude::*; +use wasm_bindgen::JsCast; +use wasm_bindgen_futures::future_to_promise; + +use crate::error::Result; +use crate::error::WasmResult; +use crate::stardust::identity_client::WasmStardustIdentityClient; +use crate::stardust::WasmStardustDID; +use crate::stardust::WasmStardustDocument; + +// `IAliasOutput`, `AddressTypes`, and `IRent` are external interfaces. +// See the custom TypeScript section in `identity_client.rs` for the first import statement. +#[wasm_bindgen(typescript_custom_section)] +const TYPESCRIPT_IMPORTS: &'static str = r#"import type { AddressTypes } from '@iota/types';"#; +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(typescript_type = "Promise")] + pub type PromiseAliasOutput; + + #[wasm_bindgen(typescript_type = "Promise")] + pub type PromiseStardustDocument; + + #[wasm_bindgen(typescript_type = "AddressTypes")] + pub type AddressTypes; + + #[wasm_bindgen(typescript_type = "IRent")] + pub type IRent; +} + +/// An extension interface that provides helper functions for publication +/// and resolution of DID documents in Alias Outputs. +#[wasm_bindgen(js_name = StardustIdentityClientExt)] +pub struct WasmStardustIdentityClientExt; + +#[wasm_bindgen(js_class = StardustIdentityClientExt)] +impl WasmStardustIdentityClientExt { + /// Create a DID with a new Alias Output containing the given `document`. + /// + /// The `address` will be set as the state controller and governor unlock conditions. + /// The minimum required token deposit amount will be set according to the given + /// `rent_structure`, which will be fetched from the node if not provided. + /// The returned Alias Output can be further customised before publication, if desired. + /// + /// NOTE: this does *not* publish the Alias Output. + #[allow(non_snake_case)] + #[wasm_bindgen(js_name = newDidOutput)] + pub fn new_did_output( + client: WasmStardustIdentityClient, + address: AddressTypes, + document: &WasmStardustDocument, + rentStructure: Option, + ) -> Result { + let address_dto: AddressDto = address.into_serde().wasm_result()?; + let address: Address = Address::try_from(&address_dto) + .map_err(|err| { + identity_stardust::Error::JsError(format!("newDidOutput failed to decode Address: {err}: {address_dto:?}")) + }) + .wasm_result()?; + let doc: StardustDocument = document.0.clone(); + + let promise: Promise = future_to_promise(async move { + let rent_structure: Option = + rentStructure.map(|rent| rent.into_serde()).transpose().wasm_result()?; + + let output: AliasOutput = StardustIdentityClientExt::new_did_output(&client, address, doc, rent_structure) + .await + .wasm_result()?; + // Use DTO for correct serialization. + let dto: AliasOutputDto = AliasOutputDto::from(&output); + JsValue::from_serde(&dto).wasm_result() + }); + + // WARNING: this does not validate the return type. Check carefully. + Ok(promise.unchecked_into::()) + } + + /// Fetches the associated Alias Output and updates it with `document` in its state metadata. + /// The storage deposit on the output is left unchanged. If the size of the document increased, + /// the amount should be increased manually. + /// + /// NOTE: this does *not* publish the updated Alias Output. + #[wasm_bindgen(js_name = updateDidOutput)] + pub fn update_did_output( + client: WasmStardustIdentityClient, + document: &WasmStardustDocument, + ) -> Result { + let document: StardustDocument = document.0.clone(); + let promise: Promise = future_to_promise(async move { + let output: AliasOutput = StardustIdentityClientExt::update_did_output(&client, document) + .await + .wasm_result()?; + // Use DTO for correct serialization. + let dto: AliasOutputDto = AliasOutputDto::from(&output); + JsValue::from_serde(&dto).wasm_result() + }); + + // WARNING: this does not validate the return type. Check carefully. + Ok(promise.unchecked_into::()) + } + + /// Removes the DID document from the state metadata of its Alias Output, + /// effectively deactivating it. The storage deposit on the output is left unchanged, + /// and should be reallocated manually. + /// + /// Deactivating does not destroy the output. Hence, it can be re-activated by publishing + /// an update containing a DID document. + /// + /// NOTE: this does *not* publish the updated Alias Output. + #[wasm_bindgen(js_name = deactivateDidOutput)] + pub fn deactivate_did_output( + client: WasmStardustIdentityClient, + did: &WasmStardustDID, + ) -> Result { + let did: StardustDID = did.0.clone(); + let promise: Promise = future_to_promise(async move { + let output: AliasOutput = StardustIdentityClientExt::deactivate_did_output(&client, &did) + .await + .wasm_result()?; + // Use DTO for correct serialization. + let dto: AliasOutputDto = AliasOutputDto::from(&output); + JsValue::from_serde(&dto).wasm_result() + }); + + // WARNING: this does not validate the return type. Check carefully. + Ok(promise.unchecked_into::()) + } + + /// Resolve a {@link StardustDocument}. Returns an empty, deactivated document if the state metadata + /// of the Alias Output is empty. + #[wasm_bindgen(js_name = resolveDid)] + pub fn resolve_did(client: WasmStardustIdentityClient, did: &WasmStardustDID) -> Result { + let did: StardustDID = did.0.clone(); + let promise: Promise = future_to_promise(async move { + StardustIdentityClientExt::resolve_did(&client, &did) + .await + .map(WasmStardustDocument) + .map(Into::into) + .wasm_result() + }); + + // WARNING: this does not validate the return type. Check carefully. + Ok(promise.unchecked_into::()) + } + + /// Fetches the `IAliasOutput` associated with the given DID. + #[wasm_bindgen(js_name = resolveDidOutput)] + pub fn resolve_did_output(client: WasmStardustIdentityClient, did: &WasmStardustDID) -> Result { + let did: StardustDID = did.0.clone(); + let promise: Promise = future_to_promise(async move { + let output: AliasOutput = StardustIdentityClientExt::resolve_did_output(&client, &did) + .await + .wasm_result()?; + // Use DTO for correct serialization. + let dto: AliasOutputDto = AliasOutputDto::from(&output); + JsValue::from_serde(&dto).wasm_result() + }); + + // WARNING: this does not validate the return type. Check carefully. + Ok(promise.unchecked_into::()) + } +} diff --git a/bindings/wasm/src/stardust/mod.rs b/bindings/wasm/src/stardust/mod.rs index 1f7eb0d5c6..a3c59ed462 100644 --- a/bindings/wasm/src/stardust/mod.rs +++ b/bindings/wasm/src/stardust/mod.rs @@ -9,6 +9,8 @@ pub use stardust_service::WasmStardustService; pub use stardust_verification_method::WasmStardustVerificationMethod; pub use state_metadata_encoding::WasmStateMetadataEncoding; +mod identity_client; +mod identity_client_ext; mod stardust_did; mod stardust_did_url; mod stardust_document; diff --git a/bindings/wasm/src/stardust/stardust_document.rs b/bindings/wasm/src/stardust/stardust_document.rs index 9595ff6713..a342ac9d4d 100644 --- a/bindings/wasm/src/stardust/stardust_document.rs +++ b/bindings/wasm/src/stardust/stardust_document.rs @@ -382,18 +382,47 @@ impl WasmStardustDocument { /// Deserializes the document from the state metadata bytes of an Alias Output. /// + /// If `allowEmpty` is true, this will return an empty DID document marked as `deactivated` + /// if `stateMetadata` is empty. + /// /// NOTE: `did` is required since it is omitted from the serialized DID Document and /// cannot be inferred from the state metadata. It also indicates the network, which is not /// encoded in the `AliasId` alone. #[allow(non_snake_case)] #[wasm_bindgen] - pub fn unpack(did: &WasmStardustDID, stateMetadata: &[u8]) -> Result { - StardustDocument::unpack(&did.0, stateMetadata) + pub fn unpack(did: &WasmStardustDID, stateMetadata: &[u8], allowEmpty: bool) -> Result { + StardustDocument::unpack(&did.0, stateMetadata, allowEmpty) .map(WasmStardustDocument) .wasm_result() } - // TODO: unpack_from_output/unpackFromOutput ? Feature-gated method, do we need an equivalent? + /// Returns all DID documents of the Alias Outputs contained in the block's transaction payload + /// outputs, if any. + /// + /// Errors if any Alias Output does not contain a valid or empty DID Document. + #[wasm_bindgen(js_name = unpackFromBlock)] + pub fn unpack_from_block(network: String, block: &IBlock) -> Result { + let network_name: NetworkName = NetworkName::try_from(network).wasm_result()?; + let block_dto: bee_block::BlockDto = block + .into_serde() + .map_err(|err| { + identity_stardust::Error::JsError(format!("unpackFromBlock failed to deserialize BlockDto: {}", err)) + }) + .wasm_result()?; + let block: bee_block::Block = bee_block::Block::try_from(&block_dto) + .map_err(|err| identity_stardust::Error::JsError(format!("unpackFromBlock failed to convert BlockDto: {}", err))) + .wasm_result()?; + + Ok( + StardustDocument::unpack_from_block(&network_name, &block) + .wasm_result()? + .into_iter() + .map(WasmStardustDocument::from) + .map(JsValue::from) + .collect::() + .unchecked_into::(), + ) + } // =========================================================================== // Metadata @@ -436,6 +465,18 @@ impl WasmStardustDocument { Ok(()) } + /// Returns a copy of the deactivated status of the DID document. + #[wasm_bindgen(js_name = metadataDeactivated)] + pub fn metadata_deactivated(&self) -> Option { + self.0.metadata.deactivated + } + + /// Sets the deactivated status of the DID document. + #[wasm_bindgen(js_name = setMetadataDeactivated)] + pub fn set_metadata_deactivated(&mut self, deactivated: Option) { + self.0.metadata.deactivated = deactivated; + } + /// Sets a custom property in the document metadata. /// If the value is set to `null`, the custom property will be removed. #[wasm_bindgen(js_name = setMetadataPropertyUnchecked)] @@ -502,9 +543,19 @@ extern "C" { #[wasm_bindgen(typescript_type = "StardustDID[]")] pub type ArrayStardustDID; + #[wasm_bindgen(typescript_type = "StardustDocument[]")] + pub type ArrayStardustDocument; + #[wasm_bindgen(typescript_type = "StardustService[]")] pub type ArrayStardustService; #[wasm_bindgen(typescript_type = "StardustVerificationMethod[]")] pub type ArrayStardustVerificationMethods; + + // External interface from `@iota/types`, must be deserialized via BlockDto. + #[wasm_bindgen(typescript_type = "IBlock")] + pub type IBlock; } + +#[wasm_bindgen(typescript_custom_section)] +const TYPESCRIPT_IMPORTS: &'static str = r#"import type { IBlock } from '@iota/types';"#; diff --git a/bindings/wasm/src/stardust/stardust_document_metadata.rs b/bindings/wasm/src/stardust/stardust_document_metadata.rs index e154420509..7e40ce4714 100644 --- a/bindings/wasm/src/stardust/stardust_document_metadata.rs +++ b/bindings/wasm/src/stardust/stardust_document_metadata.rs @@ -28,6 +28,12 @@ impl WasmStardustDocumentMetadata { self.0.updated.map(WasmTimestamp::from) } + /// Returns a copy of the deactivated status of the DID document. + #[wasm_bindgen] + pub fn deactivated(&self) -> Option { + self.0.deactivated + } + /// Returns a copy of the custom metadata properties. #[wasm_bindgen] pub fn properties(&self) -> Result { diff --git a/bindings/wasm/tests/stardust.ts b/bindings/wasm/tests/stardust.ts index a332e97c45..5026fe341b 100644 --- a/bindings/wasm/tests/stardust.ts +++ b/bindings/wasm/tests/stardust.ts @@ -163,6 +163,14 @@ describe('StardustDocument', function () { assert.deepStrictEqual(doc.metadata().updated().toRFC3339(), updated.toRFC3339()); assert.notDeepStrictEqual(doc.metadataUpdated().toRFC3339(), previousUpdated.toRFC3339()); assert.deepStrictEqual(doc.metadataCreated().toRFC3339(), created.toRFC3339()); + // Deactivated. + assert.deepStrictEqual(doc.metadataDeactivated(), undefined); + doc.setMetadataDeactivated(true); + assert.deepStrictEqual(doc.metadataDeactivated(), true); + doc.setMetadataDeactivated(false); + assert.deepStrictEqual(doc.metadataDeactivated(), false); + doc.setMetadataDeactivated(undefined); + assert.deepStrictEqual(doc.metadataDeactivated(), undefined); // Properties. assert.deepStrictEqual(doc.metadata().properties(), new Map()); const properties = new Map() diff --git a/bindings/wasm/tsconfig.json b/bindings/wasm/tsconfig.json index b30db4d61f..da4c3565d8 100644 --- a/bindings/wasm/tsconfig.json +++ b/bindings/wasm/tsconfig.json @@ -1,11 +1,14 @@ { "compilerOptions": { - "declaration": true, - "strict": true, - "esModuleInterop": true + "lib": ["ES2020", "DOM"], + "declaration": true, + "sourceMap": true, + "strict": true, + "moduleResolution": "node", + "importsNotUsedAsValues": "error", + "noImplicitAny": true, + "preserveConstEnums": true, + "forceConsistentCasingInFileNames": true, }, - "exclude": [ - "node_modules", - "dist" - ] -} \ No newline at end of file + "exclude": ["node_modules"] +} diff --git a/bindings/wasm/tsconfig.node.json b/bindings/wasm/tsconfig.node.json new file mode 100644 index 0000000000..6e8349baee --- /dev/null +++ b/bindings/wasm/tsconfig.node.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "target": "ES2020", + "esModuleInterop": true, + "module": "commonjs" + } +} diff --git a/identity_stardust/Cargo.toml b/identity_stardust/Cargo.toml index 2149ea3ec6..371a11f59c 100644 --- a/identity_stardust/Cargo.toml +++ b/identity_stardust/Cargo.toml @@ -13,6 +13,8 @@ description = "An IOTA Ledger integration for the identity.rs library." [workspace] [dependencies] +# Ensure bee-block always matches the version used by iota-client. +bee-block = { version = "1.0.0-beta.5", default-features = false, features = ["std"], optional = true } identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } identity_credential = { version = "=0.6.0", path = "../identity_credential", default-features = false } identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } @@ -46,8 +48,10 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [features] -default = ["iota-client", "revocation-bitmap"] -# Enables the iota-client dependency and associated helper functions. -iota-client = ["dep:iota-client", "dep:async-trait"] +default = ["client", "iota-client", "revocation-bitmap"] +# Exposes the `StardustIdentityClient` and `StardustIdentityClientExt` traits. +client = ["dep:async-trait", "dep:bee-block"] +# Enables the iota-client dependency, the client trait implementations for it, and the `StardustClientExt` trait. +iota-client = ["dep:iota-client", "client"] # Enables revocation with `RevocationBitmap2022`. revocation-bitmap = ["identity_did/revocation-bitmap"] diff --git a/identity_stardust/examples/ex0_create_did.rs b/identity_stardust/examples/ex0_create_did.rs index 2284e2c7f7..5495cc5e90 100644 --- a/identity_stardust/examples/ex0_create_did.rs +++ b/identity_stardust/examples/ex0_create_did.rs @@ -1,61 +1,62 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use anyhow::Context; +use identity_core::convert::ToJson; use identity_core::crypto::KeyPair; use identity_core::crypto::KeyType; use identity_did::verification::MethodScope; -use identity_stardust::NetworkName; -use identity_stardust::StardustClientExt; -use identity_stardust::StardustDocument; -use identity_stardust::StardustVerificationMethod; - use iota_client::block::address::Address; use iota_client::block::output::AliasOutput; use iota_client::block::output::Output; -use iota_client::constants::SHIMMER_TESTNET_BECH32_HRP; use iota_client::crypto::keys::bip39; use iota_client::node_api::indexer::query_parameters::QueryParameter; use iota_client::secret::mnemonic::MnemonicSecretManager; use iota_client::secret::SecretManager; use iota_client::Client; +use identity_stardust::NetworkName; +use identity_stardust::StardustClientExt; +use identity_stardust::StardustDID; +use identity_stardust::StardustDocument; +use identity_stardust::StardustIdentityClient; +use identity_stardust::StardustIdentityClientExt; +use identity_stardust::StardustVerificationMethod; + static ENDPOINT: &str = "https://api.testnet.shimmer.network/"; static FAUCET_URL: &str = "https://faucet.testnet.shimmer.network/api/enqueue"; -/// Demonstrate how to create a DID Document and publish it in a new Alias Output. -pub async fn run() -> anyhow::Result<(Client, Address, SecretManager, StardustDocument)> { - // Create a client and an address with funds from the testnet faucet. +/// Demonstrates how to create a DID Document and publish it in a new Alias Output. +pub async fn run() -> anyhow::Result<(Client, Address, SecretManager, StardustDID)> { + // Create a client and a wallet address with funds from the testnet faucet. let client: Client = Client::builder().with_primary_node(ENDPOINT, None)?.finish()?; - let (address, secret_manager): (Address, SecretManager) = get_address_with_funds(&client).await?; + let (address, secret_manager): (Address, SecretManager) = get_address_with_funds(&client) + .await + .context("failed to get address with funds")?; - // Get the BECH32 HRP identifier of the network. + // Get the Bech32 human-readable part (HRP) of the network. let network_name: NetworkName = client.network_name().await?; - // Create a new document with a placeholder DID and add a verification method. - // The placeholder will be replaced during publication, since the DID is derived from the id of the output - // that creates the Alias Output. + // Create a new DID document with a placeholder DID. + // The DID will be derived from the Alias Id of the Alias Output after publishing. let mut document: StardustDocument = StardustDocument::new(&network_name); - // Create a new key pair that we'll use to create a verification method. + // Insert a new Ed25519 verification method in the DID document. let keypair: KeyPair = KeyPair::new(KeyType::Ed25519)?; - - // Create a new verification method based on the previously created key pair. let method: StardustVerificationMethod = StardustVerificationMethod::new(document.id().clone(), keypair.type_(), keypair.public(), "#key-1")?; - - // Insert the method into the document. document.insert_method(method, MethodScope::VerificationMethod)?; - // Construct an Alias Output containing the DID document, with `address` set as both the state controller and - // governor. + // Construct an Alias Output containing the DID document, with the wallet address + // set as both the state controller and governor. let alias_output: AliasOutput = client.new_did_output(address, document, None).await?; + println!("Alias Output: {}", alias_output.to_json()?); - // Publish the output and get the published document. + // Publish the Alias Output and get the published DID document. let document: StardustDocument = client.publish_did_output(&secret_manager, alias_output).await?; + println!("Published DID document: {:#}", document); - println!("Published DID document: {:#?}", document); - - Ok((client, address, secret_manager, document)) + Ok((client, address, secret_manager, document.id().clone())) } /// Creates a new address and SecretManager with funds from the testnet faucet. @@ -68,35 +69,40 @@ async fn get_address_with_funds(client: &Client) -> anyhow::Result<(Address, Sec let secret_manager = SecretManager::Mnemonic(MnemonicSecretManager::try_from_mnemonic(&mnemonic)?); let address = client.get_addresses(&secret_manager).with_range(0..1).get_raw().await?[0]; - - request_faucet_funds(client, address).await?; + let network_hrp = client.get_network_hrp().await?; + request_faucet_funds(client, address, &network_hrp) + .await + .context("failed to request faucet funds")?; Ok((address, secret_manager)) } /// Requests funds from the testnet faucet for the given `address`. -async fn request_faucet_funds(client: &Client, address: Address) -> anyhow::Result<()> { - let address_bech32 = address.to_bech32(SHIMMER_TESTNET_BECH32_HRP); +async fn request_faucet_funds(client: &Client, address: Address, network_hrp: &str) -> anyhow::Result<()> { + let address_bech32 = address.to_bech32(network_hrp); iota_client::request_funds_from_faucet(FAUCET_URL, &address_bech32).await?; - tokio::time::timeout(std::time::Duration::from_secs(30), async { + tokio::time::timeout(std::time::Duration::from_secs(45), async { loop { tokio::time::sleep(std::time::Duration::from_secs(5)).await; - let balance = get_address_balance(client, &address_bech32).await?; + let balance = get_address_balance(client, &address_bech32) + .await + .context("failed to get address balance")?; if balance > 0 { break; } } Ok::<(), anyhow::Error>(()) }) - .await??; + .await + .context("maximum timeout exceeded")??; Ok(()) } -/// Returns the balance of the given bech32-encoded `address`. +/// Returns the balance of the given Bech32-encoded `address`. async fn get_address_balance(client: &Client, address: &str) -> anyhow::Result { let output_ids = client .basic_output_ids(vec![ @@ -121,5 +127,8 @@ async fn get_address_balance(client: &Client, address: &str) -> anyhow::Result anyhow::Result<()> { - run().await.map(|_| ()) + run().await.map(|_| ()).map_err(|err| { + eprintln!("ex0_create_did error: {:#}", err); + err + }) } diff --git a/identity_stardust/examples/ex1_update_did.rs b/identity_stardust/examples/ex1_update_did.rs index 550f35dca1..77e44c6a05 100644 --- a/identity_stardust/examples/ex1_update_did.rs +++ b/identity_stardust/examples/ex1_update_did.rs @@ -1,31 +1,33 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use identity_core::common::Timestamp; use identity_core::convert::FromJson; use identity_core::json; use identity_did::did::DID; use identity_did::service::Service; use identity_did::verification::MethodRelationship; -use identity_stardust::StardustClientExt; -use identity_stardust::StardustDocument; -use identity_stardust::StardustService; use iota_client::block::output::AliasOutput; use iota_client::block::output::AliasOutputBuilder; use iota_client::secret::SecretManager; use iota_client::Client; +use identity_stardust::StardustClientExt; +use identity_stardust::StardustDID; +use identity_stardust::StardustDocument; +use identity_stardust::StardustIdentityClientExt; +use identity_stardust::StardustService; + mod ex0_create_did; -/// Demonstrate how to modify a DID document in an existing Alias Output. +/// Demonstrates how to update a DID document in an existing Alias Output. #[tokio::main] async fn main() -> anyhow::Result<()> { // Create a new DID in an Alias Output for us to modify. - let (client, _, secret_manager, document): (Client, _, SecretManager, StardustDocument) = - ex0_create_did::run().await?; + let (client, _, secret_manager, did): (Client, _, SecretManager, StardustDID) = ex0_create_did::run().await?; // Resolve the latest state of the document. - // Technically this is equivalent to the document above. - let mut document: StardustDocument = client.resolve_did(document.id()).await?; + let mut document: StardustDocument = client.resolve_did(&did).await?; // Attach a new method relationship to the existing method. document.attach_method_relationship( @@ -40,23 +42,21 @@ async fn main() -> anyhow::Result<()> { "serviceEndpoint": "https://iota.org/" }))?; assert!(document.insert_service(service)); + document.metadata.updated = Some(Timestamp::now_utc()); // Resolve the latest output and update it with the given document. let alias_output: AliasOutput = client.update_did_output(document.clone()).await?; - // Obtain the current byte costs and increase the required storage deposit - // since the amount of stored bytes increased. + // Because the size of the DID document increased, we have to increase the allocated storage deposit. + // This increases the deposit amount to the new minimum. let rent_structure = client.get_rent_structure().await?; let alias_output = AliasOutputBuilder::from(&alias_output) .with_minimum_storage_deposit(rent_structure) .finish()?; - // Publish the output. - let resolved_document: StardustDocument = client.publish_did_output(&secret_manager, alias_output).await?; - - assert_eq!(document, resolved_document); - - println!("Published updated DID Document: {:#?}", resolved_document); + // Publish the updated Alias Output. + let updated: StardustDocument = client.publish_did_output(&secret_manager, alias_output).await?; + println!("Updated DID document: {:#}", updated); Ok(()) } diff --git a/identity_stardust/examples/ex2_resolve_did.rs b/identity_stardust/examples/ex2_resolve_did.rs index 07e88ecb7a..fdfedfdb73 100644 --- a/identity_stardust/examples/ex2_resolve_did.rs +++ b/identity_stardust/examples/ex2_resolve_did.rs @@ -1,28 +1,26 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use identity_stardust::StardustClientExt; -use identity_stardust::StardustDocument; use iota_client::block::output::AliasOutput; use iota_client::Client; +use identity_stardust::StardustDID; +use identity_stardust::StardustDocument; +use identity_stardust::StardustIdentityClientExt; + mod ex0_create_did; -/// Demonstrate how to resolve an existing DID in an Alias Output. +/// Demonstrates how to resolve an existing DID in an Alias Output. #[tokio::main] async fn main() -> anyhow::Result<()> { - let (client, _, _, document): (Client, _, _, StardustDocument) = ex0_create_did::run().await?; + let (client, _, _, did): (Client, _, _, StardustDID) = ex0_create_did::run().await?; - // Obtains the Alias Output and extracts the contained document. - let resolved_doc: StardustDocument = client.resolve_did(document.id()).await?; - - assert_eq!(resolved_doc, document); - - println!("Resolved DID Document: {resolved_doc:#?}"); + // Resolve the associated Alias Output and extract the DID document from it. + let resolved: StardustDocument = client.resolve_did(&did).await?; + println!("Resolved DID Document: {:#}", resolved); // We can also resolve the Alias Output directly. - let alias_output: AliasOutput = client.resolve_did_output(document.id()).await?; - + let alias_output: AliasOutput = client.resolve_did_output(&did).await?; println!("The Alias Output holds {} tokens", alias_output.amount()); Ok(()) diff --git a/identity_stardust/examples/ex3_deactivate_did.rs b/identity_stardust/examples/ex3_deactivate_did.rs index 4d7aa83cf4..c9634804f5 100644 --- a/identity_stardust/examples/ex3_deactivate_did.rs +++ b/identity_stardust/examples/ex3_deactivate_did.rs @@ -1,41 +1,62 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use identity_stardust::StardustClientExt; -use identity_stardust::StardustDocument; use iota_client::block::address::Address; use iota_client::block::output::AliasOutput; +use iota_client::block::output::AliasOutputBuilder; use iota_client::secret::SecretManager; use iota_client::Client; +use identity_stardust::StardustClientExt; +use identity_stardust::StardustDID; +use identity_stardust::StardustDocument; +use identity_stardust::StardustIdentityClientExt; + mod ex0_create_did; -/// Demonstrate how to deactivate a DID in an Alias Output. +/// Demonstrates how to deactivate a DID in an Alias Output. #[tokio::main] async fn main() -> anyhow::Result<()> { // Create a new DID in an Alias Output for us to modify. - let (client, _, secret_manager, document): (Client, Address, SecretManager, StardustDocument) = - ex0_create_did::run().await?; + let (client, _, secret_manager, did): (Client, Address, SecretManager, StardustDID) = ex0_create_did::run().await?; + + // Resolve the latest state of the DID document, so we can reactivate it later. + let document: StardustDocument = client.resolve_did(&did).await?; // Deactivate the DID by publishing an empty document. // This process can be reversed since the Alias Output is not destroyed. - // Deactivation can only be done by the state controller of the Alias Output. - client.deactivate_did_output(&secret_manager, document.id()).await?; - - // Attempting to resolve a deactivated DID results in an document - // where the metadata's `deactivated` field is `true`. - let deactivated_document: StardustDocument = client.resolve_did(document.id()).await?; - - assert!(matches!(deactivated_document.metadata.deactivated, Some(true))); - - // Re-activate the DID by publishing a valid document. - let alias_output: AliasOutput = client.update_did_output(document.clone()).await?; - client.publish_did_output(&secret_manager, alias_output).await?; - - // Resolve the republished document. - let resolved_document: StardustDocument = client.resolve_did(document.id()).await?; - - assert_eq!(document, resolved_document); + // Deactivation may only be performed by the state controller of the Alias Output. + let deactivated_output: AliasOutput = client.deactivate_did_output(&did).await?; + + // Optional: reduce and reclaim the storage deposit, sending the tokens to the state controller. + let rent_structure = client.get_rent_structure().await?; + let deactivated_output = AliasOutputBuilder::from(&deactivated_output) + .with_minimum_storage_deposit(rent_structure) + .finish()?; + + // Publish the deactivated DID document. + let _ = client.publish_did_output(&secret_manager, deactivated_output).await?; + + // Resolving a deactivated DID returns an empty DID document + // with its `deactivated` metadata field set to `true`. + let deactivated: StardustDocument = client.resolve_did(&did).await?; + println!("Deactivated DID document: {:#}", deactivated); + assert_eq!(deactivated.metadata.deactivated, Some(true)); + + // Re-activate the DID by publishing a valid DID document. + let reactivated_output: AliasOutput = client.update_did_output(document.clone()).await?; + + // Increase the storage deposit to the minimum again, if it was reclaimed during deactivation. + let rent_structure = client.get_rent_structure().await?; + let reactivated_output = AliasOutputBuilder::from(&reactivated_output) + .with_minimum_storage_deposit(rent_structure) + .finish()?; + client.publish_did_output(&secret_manager, reactivated_output).await?; + + // Resolve the reactivated DID document. + let reactivated: StardustDocument = client.resolve_did(&did).await?; + assert_eq!(document, reactivated); + assert!(!reactivated.metadata.deactivated.unwrap_or_default()); Ok(()) } diff --git a/identity_stardust/examples/ex4_delete_did.rs b/identity_stardust/examples/ex4_delete_did.rs index baa7fc6209..458de0b995 100644 --- a/identity_stardust/examples/ex4_delete_did.rs +++ b/identity_stardust/examples/ex4_delete_did.rs @@ -1,32 +1,31 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use identity_stardust::Error; -use identity_stardust::StardustClientExt; -use identity_stardust::StardustDocument; use iota_client::block::address::Address; use iota_client::secret::SecretManager; use iota_client::Client; +use identity_stardust::Error; +use identity_stardust::StardustClientExt; +use identity_stardust::StardustDID; +use identity_stardust::StardustIdentityClientExt; + mod ex0_create_did; -/// Demonstrate how to delete an existing DID in an Alias Output, reclaiming the stored deposit. +/// Demonstrates how to delete a DID in an Alias Output, reclaiming the storage deposit. #[tokio::main] async fn main() -> anyhow::Result<()> { // Create a new DID in an Alias Output for us to modify. - let (client, address, secret_manager, document): (Client, Address, SecretManager, StardustDocument) = + let (client, address, secret_manager, did): (Client, Address, SecretManager, StardustDID) = ex0_create_did::run().await?; // Deletes the Alias Output and its contained DID Document, rendering the DID permanently destroyed. // This operation is *not* reversible. // Deletion can only be done by the governor of the Alias Output. - client - .delete_did_output(&secret_manager, address, document.id()) - .await?; + client.delete_did_output(&secret_manager, address, &did).await?; // Attempting to resolve a deleted DID results in a `NotFound` error. - let error: Error = client.resolve_did(document.id()).await.unwrap_err(); - + let error: Error = client.resolve_did(&did).await.unwrap_err(); assert!(matches!( error, identity_stardust::Error::DIDResolutionError(iota_client::Error::NotFound) diff --git a/identity_stardust/src/client/client_ext.rs b/identity_stardust/src/client/client_ext.rs deleted file mode 100644 index 1d65b979ce..0000000000 --- a/identity_stardust/src/client/client_ext.rs +++ /dev/null @@ -1,354 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::borrow::Cow; -use std::ops::Deref; - -use identity_did::did::DIDError; -use iota_client::api_types::responses::OutputResponse; -use iota_client::block::address::Address; -use iota_client::block::output::feature::SenderFeature; -use iota_client::block::output::unlock_condition::AddressUnlockCondition; -use iota_client::block::output::unlock_condition::GovernorAddressUnlockCondition; -use iota_client::block::output::unlock_condition::StateControllerAddressUnlockCondition; -use iota_client::block::output::AliasId; -use iota_client::block::output::AliasOutput; -use iota_client::block::output::AliasOutputBuilder; -use iota_client::block::output::BasicOutputBuilder; -use iota_client::block::output::Feature; -use iota_client::block::output::Output; -use iota_client::block::output::OutputId; -use iota_client::block::output::RentStructure; -use iota_client::block::output::UnlockCondition; -use iota_client::block::payload::transaction::TransactionEssence; -use iota_client::block::payload::Payload; -use iota_client::block::Block; -use iota_client::secret::SecretManager; -use iota_client::Client; - -use crate::error::Result; -use crate::Error; -use crate::NetworkName; -use crate::StardustDID; -use crate::StardustDocument; - -/// An extension trait for a [`Client`] that provides helper functions for publication -/// and resolution of DID documents in Alias Outputs. -/// -/// This trait is only meant to be used rather than implemented. -#[async_trait::async_trait] -pub trait StardustClientExt: Sync { - /// Returns a reference to a [`Client`]. - fn client(&self) -> &Client; - - /// Create a DID with a new Alias Output containing the given `document`. - /// - /// The `address` will be set as the state controller and governor unlock conditions. - /// The minimum required token deposit amount will be set according to the given - /// `rent_structure`, which will be fetched from the node if not provided. - /// The returned Alias Output can be further customized before publication, if desired. - /// - /// NOTE: this does *not* publish the Alias Output. See [`publish_did_output`](StardustClientExt::publish_did_output). - /// - /// # Errors - /// - /// - Returns an [`Error::DIDUpdateError`] when retrieving the `RentStructure` fails. - /// - Returns an [`Error::AliasOutputBuildError`] when building the Alias Output fails. - async fn new_did_output( - &self, - address: Address, - document: StardustDocument, - rent_structure: Option, - ) -> Result { - let rent_structure: RentStructure = if let Some(inner) = rent_structure { - inner - } else { - self - .client() - .get_rent_structure() - .await - .map_err(Error::DIDUpdateError)? - }; - - AliasOutputBuilder::new_with_minimum_storage_deposit(rent_structure, AliasId::null()) - .map_err(Error::AliasOutputBuildError)? - .with_state_index(0) - .with_foundry_counter(0) - .with_state_metadata(document.pack()?) - .add_feature(Feature::Sender(SenderFeature::new(address))) - .add_unlock_condition(UnlockCondition::StateControllerAddress( - StateControllerAddressUnlockCondition::new(address), - )) - .add_unlock_condition(UnlockCondition::GovernorAddress(GovernorAddressUnlockCondition::new( - address, - ))) - .finish() - .map_err(Error::AliasOutputBuildError) - } - - /// Returns the updated Alias Output for further customization and publication. The storage deposit - /// on the output is unchanged. If the size of the document increased, the amount must be increased manually. - /// - /// NOTE: this does *not* publish the updated Alias Output. See - /// [`publish_did_output`](StardustClientExt::publish_did_output). - /// - /// # Errors - /// - /// Returns `Err` when failing to resolve the DID contained in `document`. - async fn update_did_output(&self, document: StardustDocument) -> Result { - let (alias_id, _, alias_output) = resolve_alias_output(self.client(), document.id()).await?; - - let mut alias_output_builder: AliasOutputBuilder = AliasOutputBuilder::from(&alias_output) - .with_state_index(alias_output.state_index() + 1) - .with_state_metadata(document.pack()?); - - if alias_output.alias_id().is_null() { - alias_output_builder = alias_output_builder.with_alias_id(alias_id); - } - - alias_output_builder.finish().map_err(Error::AliasOutputBuildError) - } - - /// Resolves the Alias Output associated to the `did`, removes the DID document, - /// and publishes the output. This effectively deactivates the DID. - /// Deactivating does not destroy the output. Hence, a deactivated DID can be - /// re-activated by updating the contained document. - /// - /// The storage deposit on the output is left unchanged. - /// - /// # Errors - /// - /// Returns `Err` when failing to resolve the `did`. - async fn deactivate_did_output(&self, secret_manager: &SecretManager, did: &StardustDID) -> Result<()> { - let (alias_id, _, alias_output) = resolve_alias_output(self.client(), did).await?; - - let mut alias_output_builder: AliasOutputBuilder = AliasOutputBuilder::from(&alias_output) - .with_state_index(alias_output.state_index() + 1) - .with_state_metadata(Vec::new()); - - if alias_output.alias_id().is_null() { - alias_output_builder = alias_output_builder.with_alias_id(alias_id); - } - - let alias_output: AliasOutput = alias_output_builder.finish().map_err(Error::AliasOutputBuildError)?; - - let _ = publish_output(self.client(), secret_manager, alias_output).await?; - - Ok(()) - } - - /// Publish the given `alias_output` with the provided `secret_manager` - /// and returns the block they were published in. - /// - /// Needs to be called by the state controller of the Alias Output. - /// - /// This method modifies the on-ledger state. - async fn publish_did_output( - &self, - secret_manager: &SecretManager, - alias_output: AliasOutput, - ) -> Result { - let block: Block = publish_output(self.client(), secret_manager, alias_output).await?; - - Ok( - documents_from_block(self.client(), &block) - .await? - .into_iter() - .next() - .expect("there should be exactly one document"), - ) - } - - /// Consume the Alias Output containing the given `did`, sending its tokens to a new Basic Output - /// unlockable by `address`. - /// - /// Note that only the governor of an Alias Output is allowed to destroy it. - /// - /// # WARNING - /// - /// This destroys the DID Document and the Alias Output and renders the DID permanently unrecoverable. - async fn delete_did_output(&self, secret_manager: &SecretManager, address: Address, did: &StardustDID) -> Result<()> { - let client: &Client = self.client(); - - let (_, output_id, alias_output) = resolve_alias_output(client, did).await?; - - let basic_output = BasicOutputBuilder::new_with_amount(alias_output.amount()) - .map_err(Error::BasicOutputBuildError)? - .add_unlock_condition(UnlockCondition::Address(AddressUnlockCondition::new(address))) - .finish_output() - .map_err(Error::BasicOutputBuildError)?; - - let block: Block = client - .block() - .with_secret_manager(secret_manager) - .with_input(output_id.into()) - .map_err(Error::DIDUpdateError)? - .with_outputs(vec![basic_output]) - .map_err(Error::DIDUpdateError)? - .finish() - .await - .map_err(Error::DIDUpdateError)?; - - let _ = client - .retry_until_included(&block.id(), None, None) - .await - .map_err(Error::DIDUpdateError)?; - - Ok(()) - } - - /// Resolve a [`StardustDID`] to a [`StardustDocument`]. - /// - /// # Errors - /// - /// - Returns a [`NetworkMismatch`](Error::NetworkMismatch) error if the DID's and the client's network do not match. - /// - Returns a [`NotFound`](iota_client::Error::NotFound) error if the associated Alias Output wasn't found. - async fn resolve_did(&self, did: &StardustDID) -> Result { - let network_hrp: String = get_network_hrp(self.client()).await?; - - if did.network_str() != network_hrp.as_str() { - return Err(Error::NetworkMismatch { - expected: did.network_str().to_owned(), - actual: network_hrp, - }); - } - - let (_, _, alias_output) = resolve_alias_output(self.client(), did).await?; - - if alias_output.state_metadata().is_empty() { - let mut empty_document: StardustDocument = StardustDocument::new_with_id(did.to_owned()); - empty_document.metadata.deactivated = Some(true); - - Ok(empty_document) - } else { - let document: &[u8] = alias_output.state_metadata(); - StardustDocument::unpack(did, document) - } - } - - /// Resolve a [`StardustDID`] to an [`AliasOutput`]. - /// - /// # Errors - /// - /// - Returns a [`NetworkMismatch`](Error::NetworkMismatch) error if the DID's and the client's network do not match. - /// - Returns a [`NotFound`](iota_client::Error::NotFound) error if the associated Alias Output wasn't found. - async fn resolve_did_output(&self, did: &StardustDID) -> Result { - let network_hrp: String = get_network_hrp(self.client()).await?; - - if did.network_str() != network_hrp.as_str() { - return Err(Error::NetworkMismatch { - expected: did.network_str().to_owned(), - actual: network_hrp, - }); - } - - resolve_alias_output(self.client(), did) - .await - .map(|(_, _, alias_output)| alias_output) - } - - /// Returns the network name of the connected node, which is the - /// BECH32 human-readable part (HRP) of the network. - /// - /// For the IOTA main network this is `iota` and for the Shimmer network it is `smr`. - async fn network_name(&self) -> Result { - get_network_hrp(self.client()).await.and_then(NetworkName::try_from) - } -} - -impl StardustClientExt for Client { - fn client(&self) -> &Client { - self - } -} - -impl StardustClientExt for &Client { - fn client(&self) -> &Client { - self - } -} - -/// Publishes an `alias_output`. -/// Returns the block that the output was included in. -async fn publish_output(client: &Client, secret_manager: &SecretManager, alias_output: AliasOutput) -> Result { - let block: Block = client - .block() - .with_secret_manager(secret_manager) - .with_outputs(vec![alias_output.into()]) - .map_err(Error::DIDUpdateError)? - .finish() - .await - .map_err(Error::DIDUpdateError)?; - - let _ = client - .retry_until_included(&block.id(), None, None) - .await - .map_err(Error::DIDUpdateError)?; - - Ok(block) -} - -/// Get the BECH32 HRP from the client's network. -async fn get_network_hrp(client: &Client) -> Result { - client - .get_network_info() - .await - .map_err(Error::DIDResolutionError)? - .bech32_hrp - .ok_or_else(|| Error::InvalidNetworkName("".to_owned())) -} - -/// Returns all DID documents of the Alias Outputs contained in the payload's transaction, if any. -async fn documents_from_block(client: &Client, block: &Block) -> Result> { - let network_hrp: String = get_network_hrp(client).await?; - let mut documents = Vec::new(); - - if let Some(Payload::Transaction(tx_payload)) = block.payload() { - let TransactionEssence::Regular(regular) = tx_payload.essence(); - - for (index, output) in regular.outputs().iter().enumerate() { - if let Output::Alias(alias_output) = output { - let alias_id = if alias_output.alias_id().is_null() { - AliasId::from( - OutputId::new( - tx_payload.id(), - index - .try_into() - .map_err(|_| Error::OutputIdConversionError(format!("the output index {index} must fit into a u16")))?, - ) - .map_err(|err| Error::OutputIdConversionError(err.to_string()))?, - ) - } else { - alias_output.alias_id().to_owned() - }; - - let did: StardustDID = StardustDID::new( - alias_id.deref(), - &NetworkName::try_from(Cow::from(network_hrp.clone()))?, - ); - documents.push(StardustDocument::unpack(&did, alias_output.state_metadata())?); - } - } - } - - Ok(documents) -} - -/// Resolve a did into an Alias Output and the associated identifiers. -async fn resolve_alias_output(client: &Client, did: &StardustDID) -> Result<(AliasId, OutputId, AliasOutput)> { - let tag_bytes: [u8; StardustDID::TAG_BYTES_LEN] = - prefix_hex::decode(did.tag()).map_err(|_| DIDError::InvalidMethodId)?; - let alias_id: AliasId = AliasId::new(tag_bytes); - let output_id: OutputId = client - .alias_output_id(alias_id) - .await - .map_err(Error::DIDResolutionError)?; - let output_response: OutputResponse = client.get_output(&output_id).await.map_err(Error::DIDResolutionError)?; - let output: Output = Output::try_from(&output_response.output).map_err(Error::OutputConversionError)?; - - if let Output::Alias(alias_output) = output { - Ok((alias_id, output_id, alias_output)) - } else { - Err(Error::NotAnAliasOutput(output_id)) - } -} diff --git a/identity_stardust/src/client/identity_client.rs b/identity_stardust/src/client/identity_client.rs new file mode 100644 index 0000000000..eea778cccb --- /dev/null +++ b/identity_stardust/src/client/identity_client.rs @@ -0,0 +1,200 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use identity_did::did::DIDError; + +use crate::block::address::Address; +use crate::block::output::feature::SenderFeature; +use crate::block::output::unlock_condition::GovernorAddressUnlockCondition; +use crate::block::output::unlock_condition::StateControllerAddressUnlockCondition; +use crate::block::output::AliasId; +use crate::block::output::AliasOutput; +use crate::block::output::AliasOutputBuilder; +use crate::block::output::Feature; +use crate::block::output::OutputId; +use crate::block::output::RentStructure; +use crate::block::output::UnlockCondition; +use crate::Error; +use crate::NetworkName; +use crate::Result; +use crate::StardustDID; +use crate::StardustDocument; + +impl TryFrom<&StardustDID> for AliasId { + type Error = Error; + + fn try_from(did: &StardustDID) -> std::result::Result { + let tag_bytes: [u8; StardustDID::TAG_BYTES_LEN] = + prefix_hex::decode(did.tag()).map_err(|_| DIDError::InvalidMethodId)?; + Ok(AliasId::new(tag_bytes)) + } +} + +/// Helper functions necessary for the [`StardustIdentityClientExt`] trait. +#[async_trait::async_trait(? Send)] +pub trait StardustIdentityClient { + /// Return the Bech32 human-readable part (HRP) of the network. + /// + /// E.g. "iota", "atoi", "smr", "rms". + async fn get_network_hrp(&self) -> Result; + + /// Resolve an Alias identifier, returning its latest [`OutputId`] and [`AliasOutput`]. + async fn get_alias_output(&self, alias_id: AliasId) -> Result<(OutputId, AliasOutput)>; + + /// Return the rent structure of the network, indicating the byte costs for outputs. + async fn get_rent_structure(&self) -> Result; +} + +/// An extension trait that provides helper functions for publication +/// and resolution of DID documents in Alias Outputs. +/// +/// This trait is not intended to be implemented directly, a blanket implementation is +/// provided for [`StardustIdentityClient`] implementers. +#[async_trait::async_trait(? Send)] +pub trait StardustIdentityClientExt: StardustIdentityClient { + /// Create a DID with a new Alias Output containing the given `document`. + /// + /// The `address` will be set as the state controller and governor unlock conditions. + /// The minimum required token deposit amount will be set according to the given + /// `rent_structure`, which will be fetched from the node if not provided. + /// The returned Alias Output can be further customised before publication, if desired. + /// + /// NOTE: this does *not* publish the Alias Output. + /// + /// # Errors + /// + /// - [`Error::DIDUpdateError`] when retrieving the `RentStructure` fails. + /// - [`Error::AliasOutputBuildError`] when building the Alias Output fails. + async fn new_did_output( + &self, + address: Address, + document: StardustDocument, + rent_structure: Option, + ) -> Result { + let rent_structure: RentStructure = if let Some(rent) = rent_structure { + rent + } else { + self.get_rent_structure().await? + }; + + AliasOutputBuilder::new_with_minimum_storage_deposit(rent_structure, AliasId::null()) + .map_err(Error::AliasOutputBuildError)? + .with_state_index(0) + .with_foundry_counter(0) + .with_state_metadata(document.pack()?) + .add_feature(Feature::Sender(SenderFeature::new(address))) + .add_unlock_condition(UnlockCondition::StateControllerAddress( + StateControllerAddressUnlockCondition::new(address), + )) + .add_unlock_condition(UnlockCondition::GovernorAddress(GovernorAddressUnlockCondition::new( + address, + ))) + .finish() + .map_err(Error::AliasOutputBuildError) + } + + /// Fetches the associated Alias Output and updates it with `document` in its state metadata. + /// The storage deposit on the output is left unchanged. If the size of the document increased, + /// the amount should be increased manually. + /// + /// NOTE: this does *not* publish the updated Alias Output. + /// + /// # Errors + /// + /// Returns `Err` when failing to resolve the DID contained in `document`. + async fn update_did_output(&self, document: StardustDocument) -> Result { + let id: AliasId = AliasId::try_from(document.id())?; + let (_, alias_output) = self.get_alias_output(id).await?; + + let mut alias_output_builder: AliasOutputBuilder = AliasOutputBuilder::from(&alias_output) + .with_state_index(alias_output.state_index() + 1) + .with_state_metadata(document.pack()?); + + if alias_output.alias_id().is_null() { + alias_output_builder = alias_output_builder.with_alias_id(id); + } + + alias_output_builder.finish().map_err(Error::AliasOutputBuildError) + } + + /// Removes the DID document from the state metadata of its Alias Output, + /// effectively deactivating it. The storage deposit on the output is left unchanged, + /// and should be reallocated manually. + /// + /// Deactivating does not destroy the output. Hence, it can be re-activated by publishing + /// an update containing a DID document. + /// + /// NOTE: this does *not* publish the updated Alias Output. + /// + /// # Errors + /// + /// Returns `Err` when failing to resolve the `did`. + async fn deactivate_did_output(&self, did: &StardustDID) -> Result { + let alias_id: AliasId = AliasId::try_from(did)?; + let (_, alias_output) = self.get_alias_output(alias_id).await?; + + let mut alias_output_builder: AliasOutputBuilder = AliasOutputBuilder::from(&alias_output) + .with_state_index(alias_output.state_index() + 1) + .with_state_metadata(Vec::new()); + + if alias_output.alias_id().is_null() { + alias_output_builder = alias_output_builder.with_alias_id(alias_id); + } + + alias_output_builder.finish().map_err(Error::AliasOutputBuildError) + } + + /// Resolve a [`StardustDocument`]. Returns an empty, deactivated document if the state metadata + /// of the Alias Output is empty. + /// + /// # Errors + /// + /// - [`NetworkMismatch`](Error::NetworkMismatch) if the network of the DID and client differ. + /// - [`NotFound`](iota_client::Error::NotFound) if the associated Alias Output was not found. + async fn resolve_did(&self, did: &StardustDID) -> Result { + validate_network(self, did).await?; + + let id: AliasId = AliasId::try_from(did)?; + let (_, alias_output) = self.get_alias_output(id).await?; + + let document: &[u8] = alias_output.state_metadata(); + StardustDocument::unpack(did, document, true) + } + + /// Fetches the [`AliasOutput`] associated with the given DID. + /// + /// # Errors + /// + /// - [`NetworkMismatch`](Error::NetworkMismatch) if the network of the DID and client differ. + /// - [`NotFound`](iota_client::Error::NotFound) if the associated Alias Output was not found. + async fn resolve_did_output(&self, did: &StardustDID) -> Result { + validate_network(self, did).await?; + + let id: AliasId = AliasId::try_from(did)?; + self.get_alias_output(id).await.map(|(_, alias_output)| alias_output) + } + + /// Returns the network name of the client, which is the + /// Bech32 human-readable part (HRP) of the network. + /// + /// E.g. "iota", "atoi", "smr", "rms". + async fn network_name(&self) -> Result { + self.get_network_hrp().await.and_then(NetworkName::try_from) + } +} + +impl StardustIdentityClientExt for T where T: StardustIdentityClient {} + +pub(super) async fn validate_network(client: &T, did: &StardustDID) -> Result<()> +where + T: StardustIdentityClient + ?Sized, +{ + let network_hrp: String = client.get_network_hrp().await?; + if did.network_str() != network_hrp.as_str() { + return Err(Error::NetworkMismatch { + expected: did.network_str().to_owned(), + actual: network_hrp, + }); + }; + Ok(()) +} diff --git a/identity_stardust/src/client/iota_client.rs b/identity_stardust/src/client/iota_client.rs new file mode 100644 index 0000000000..fb76512f6b --- /dev/null +++ b/identity_stardust/src/client/iota_client.rs @@ -0,0 +1,153 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use iota_client::api_types::responses::OutputResponse; +use iota_client::secret::SecretManager; +use iota_client::Client; + +use crate::block::address::Address; +use crate::block::output::unlock_condition::AddressUnlockCondition; +use crate::block::output::AliasId; +use crate::block::output::AliasOutput; +use crate::block::output::BasicOutputBuilder; +use crate::block::output::Output; +use crate::block::output::OutputId; +use crate::block::output::RentStructure; +use crate::block::output::UnlockCondition; +use crate::block::Block; +use crate::client::identity_client::validate_network; +use crate::error::Result; +use crate::Error; +use crate::NetworkName; +use crate::StardustDID; +use crate::StardustDocument; +use crate::StardustIdentityClient; +use crate::StardustIdentityClientExt; + +/// An extension trait for [`Client`] that provides helper functions for publication +/// and deletion of DID documents in Alias Outputs. +#[async_trait::async_trait(?Send)] +pub trait StardustClientExt: StardustIdentityClient { + /// Publish the given `alias_output` with the provided `secret_manager`, and returns + /// the DID document extracted from the published block. + /// + /// Note that only the state controller of an Alias Output is allowed to update its state. + /// This will attempt to move tokens to or from the state controller address to match + /// the storage deposit amount specified on `alias_output`. + /// + /// This method modifies the on-ledger state. + async fn publish_did_output( + &self, + secret_manager: &SecretManager, + alias_output: AliasOutput, + ) -> Result; + + /// Destroy the Alias Output containing the given `did`, sending its tokens to a new Basic Output + /// unlockable by `address`. + /// + /// Note that only the governor of an Alias Output is allowed to destroy it. + /// + /// # WARNING + /// + /// This destroys the Alias Output and DID document, rendering them permanently unrecoverable. + async fn delete_did_output(&self, secret_manager: &SecretManager, address: Address, did: &StardustDID) -> Result<()>; +} + +/// An extension trait for [`Client`] that provides helper functions for publication +/// and deletion of DID documents in Alias Outputs. +#[async_trait::async_trait(?Send)] +impl StardustClientExt for Client { + async fn publish_did_output( + &self, + secret_manager: &SecretManager, + alias_output: AliasOutput, + ) -> Result { + let block: Block = publish_output(self, secret_manager, alias_output) + .await + .map_err(|err| Error::DIDUpdateError("publish_did_output: publish failed", Some(err)))?; + let network: NetworkName = self.network_name().await?; + + StardustDocument::unpack_from_block(&network, &block)? + .into_iter() + .next() + .ok_or(Error::DIDUpdateError( + "publish_did_output: no document found in published block", + None, + )) + } + + async fn delete_did_output(&self, secret_manager: &SecretManager, address: Address, did: &StardustDID) -> Result<()> { + validate_network(self, did).await?; + + let alias_id: AliasId = AliasId::try_from(did)?; + let (output_id, alias_output) = self.get_alias_output(alias_id).await?; + + let basic_output = BasicOutputBuilder::new_with_amount(alias_output.amount()) + .map_err(Error::BasicOutputBuildError)? + .with_native_tokens(alias_output.native_tokens().clone()) + .add_unlock_condition(UnlockCondition::Address(AddressUnlockCondition::new(address))) + .finish_output() + .map_err(Error::BasicOutputBuildError)?; + + let block: Block = self + .block() + .with_secret_manager(secret_manager) + .with_input(output_id.into()) + .map_err(|err| Error::DIDUpdateError("delete_did_output: invalid block input", Some(err)))? + .with_outputs(vec![basic_output]) + .map_err(|err| Error::DIDUpdateError("delete_did_output: invalid block output", Some(err)))? + .finish() + .await + .map_err(|err| Error::DIDUpdateError("delete_did_output: publish failed", Some(err)))?; + let _ = self + .retry_until_included(&block.id(), None, None) + .await + .map_err(|err| Error::DIDUpdateError("delete_did_output: publish retry failed or timed-out", Some(err)))?; + + Ok(()) + } +} + +#[async_trait::async_trait(?Send)] +impl StardustIdentityClient for Client { + async fn get_network_hrp(&self) -> Result { + self.get_bech32_hrp().await.map_err(Error::DIDResolutionError) + } + + async fn get_alias_output(&self, id: AliasId) -> Result<(OutputId, AliasOutput)> { + let output_id: OutputId = self.alias_output_id(id).await.map_err(Error::DIDResolutionError)?; + let output_response: OutputResponse = self.get_output(&output_id).await.map_err(Error::DIDResolutionError)?; + let output: Output = Output::try_from(&output_response.output).map_err(Error::OutputConversionError)?; + + if let Output::Alias(alias_output) = output { + Ok((output_id, alias_output)) + } else { + Err(Error::NotAnAliasOutput(output_id)) + } + } + + async fn get_rent_structure(&self) -> Result { + Client::get_rent_structure(self) + .await + .map_err(|err| Error::DIDUpdateError("get_rent_structure failed", Some(err))) + } +} + +/// Publishes an `alias_output`. +/// Returns the block that the output was included in. +async fn publish_output( + client: &Client, + secret_manager: &SecretManager, + alias_output: AliasOutput, +) -> iota_client::Result { + let block: Block = client + .block() + .with_secret_manager(secret_manager) + .with_outputs(vec![alias_output.into()])? + .finish() + .await?; + + let _ = client.retry_until_included(&block.id(), None, None).await?; + + Ok(block) +} diff --git a/identity_stardust/src/client/mod.rs b/identity_stardust/src/client/mod.rs index beb796c365..2565cdfc54 100644 --- a/identity_stardust/src/client/mod.rs +++ b/identity_stardust/src/client/mod.rs @@ -1,6 +1,12 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -pub use client_ext::StardustClientExt; +pub use identity_client::StardustIdentityClient; +pub use identity_client::StardustIdentityClientExt; -mod client_ext; +#[cfg(feature = "iota-client")] +pub use self::iota_client::StardustClientExt; + +mod identity_client; +#[cfg(feature = "iota-client")] +mod iota_client; diff --git a/identity_stardust/src/document/stardust_document.rs b/identity_stardust/src/document/stardust_document.rs index a43f0e5897..ed5f8b1789 100644 --- a/identity_stardust/src/document/stardust_document.rs +++ b/identity_stardust/src/document/stardust_document.rs @@ -285,14 +285,78 @@ impl StardustDocument { /// Deserializes the document from the state metadata bytes of an Alias Output. /// + /// If `allow_empty` is true, this will return an empty DID document marked as `deactivated` + /// if `state_metadata` is empty. + /// /// NOTE: `did` is required since it is omitted from the serialized DID Document and /// cannot be inferred from the state metadata. It also indicates the network, which is not /// encoded in the `AliasId` alone. - pub fn unpack(did: &StardustDID, state_metadata: &[u8]) -> Result { + pub fn unpack(did: &StardustDID, state_metadata: &[u8], allow_empty: bool) -> Result { + if state_metadata.is_empty() && allow_empty { + let mut empty_document = StardustDocument::new_with_id(did.clone()); + empty_document.metadata.created = None; + empty_document.metadata.updated = None; + empty_document.metadata.deactivated = Some(true); + return Ok(empty_document); + } StateMetadataDocument::unpack(state_metadata).and_then(|doc| doc.into_stardust_document(did)) } } +#[cfg(feature = "client")] +mod client_document { + use std::ops::Deref; + + use crate::block::output::AliasId; + use crate::block::output::Output; + use crate::block::output::OutputId; + use crate::block::payload::transaction::TransactionEssence; + use crate::block::payload::Payload; + use crate::block::Block; + use crate::error::Result; + use crate::Error; + use crate::NetworkName; + + use super::*; + + impl StardustDocument { + /// Returns all DID documents of the Alias Outputs contained in the block's transaction payload + /// outputs, if any. + /// + /// Errors if any Alias Output does not contain a valid or empty DID Document. + pub fn unpack_from_block(network: &NetworkName, block: &Block) -> Result> { + let mut documents = Vec::new(); + + if let Some(Payload::Transaction(tx_payload)) = block.payload() { + let TransactionEssence::Regular(regular) = tx_payload.essence(); + + for (index, output) in regular.outputs().iter().enumerate() { + if let Output::Alias(alias_output) = output { + let alias_id = if alias_output.alias_id().is_null() { + AliasId::from( + OutputId::new( + tx_payload.id(), + index + .try_into() + .map_err(|_| Error::OutputIdConversionError(format!("output index {index} must fit into a u16")))?, + ) + .map_err(|err| Error::OutputIdConversionError(err.to_string()))?, + ) + } else { + alias_output.alias_id().to_owned() + }; + + let did: StardustDID = StardustDID::new(alias_id.deref(), network); + documents.push(StardustDocument::unpack(&did, alias_output.state_metadata(), true)?); + } + } + } + + Ok(documents) + } + } +} + impl Document for StardustDocument { type D = StardustDID; type U = Object; @@ -654,6 +718,24 @@ mod tests { assert_eq!(doc1, doc2); } + #[test] + fn test_unpack_empty() { + // VALID: unpack empty, deactivated document. + let did: StardustDID = valid_did(); + let empty: &[u8] = &[]; + let document: StardustDocument = StardustDocument::unpack(&did, empty, true).unwrap(); + assert_eq!(document.id(), &did); + assert_eq!(document.metadata.deactivated, Some(true)); + + // Ensure no other fields are injected. + // TODO: update this when controller/governor fields are added? + let json: String = format!("{{\"doc\":{{\"id\":\"{did}\"}},\"meta\":{{\"deactivated\":true}}}}"); + assert_eq!(document.to_json().unwrap(), json); + + // INVALID: reject empty document. + assert!(StardustDocument::unpack(&did, empty, false).is_err()); + } + #[test] fn test_json_roundtrip() { let document: StardustDocument = generate_document(&valid_did()); @@ -665,6 +747,7 @@ mod tests { #[test] fn test_json_fieldnames() { + // Changing the serialization is a breaking change! let document: StardustDocument = StardustDocument::new_with_id(valid_did()); let serialization: String = document.to_json().unwrap(); assert_eq!( diff --git a/identity_stardust/src/error.rs b/identity_stardust/src/error.rs index fe4eb8d4ed..8e36f070d7 100644 --- a/identity_stardust/src/error.rs +++ b/identity_stardust/src/error.rs @@ -13,8 +13,8 @@ pub enum Error { #[error("{0}")] InvalidDoc(#[from] identity_did::Error), #[cfg(feature = "iota-client")] - #[error("DID update failed")] - DIDUpdateError(#[source] iota_client::error::Error), + #[error("DID update: {0}")] + DIDUpdateError(&'static str, #[source] Option), #[cfg(feature = "iota-client")] #[error("DID resolution failed")] DIDResolutionError(#[source] iota_client::error::Error), @@ -29,9 +29,9 @@ pub enum Error { InvalidStateMetadata(&'static str), #[error("credential revocation error")] RevocationError(#[source] identity_did::Error), - #[cfg(feature = "iota-client")] + #[cfg(feature = "client")] #[error("alias output build error")] - AliasOutputBuildError(#[source] iota_client::block::Error), + AliasOutputBuildError(#[source] bee_block::Error), #[cfg(feature = "iota-client")] #[error("output with id `{0}` is not an alias output")] NotAnAliasOutput(iota_client::block::output::OutputId), @@ -40,4 +40,7 @@ pub enum Error { OutputConversionError(#[source] iota_client::block::DtoError), #[error("conversion to an OutputId failed: {0}")] OutputIdConversionError(String), + #[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] + #[error("JavaScript function threw an exception: {0}")] + JsError(String), } diff --git a/identity_stardust/src/lib.rs b/identity_stardust/src/lib.rs index 7000c3a315..6baf38e7ad 100644 --- a/identity_stardust/src/lib.rs +++ b/identity_stardust/src/lib.rs @@ -4,7 +4,11 @@ #![forbid(unsafe_code)] #![allow(clippy::upper_case_acronyms)] -#[cfg(feature = "iota-client")] +/// Re-export the `bee_block` crate for implementer convenience. +#[cfg(feature = "client")] +pub use bee_block as block; + +#[cfg(feature = "client")] pub use client::*; pub use did::StardustDID; pub use did::StardustDIDUrl; @@ -15,7 +19,7 @@ pub use state_metadata::*; pub use self::error::Error; pub use self::error::Result; -#[cfg(feature = "iota-client")] +#[cfg(feature = "client")] mod client; mod did; mod document; diff --git a/identity_stardust/src/state_metadata/document.rs b/identity_stardust/src/state_metadata/document.rs index 484b93096c..0c23884cd2 100644 --- a/identity_stardust/src/state_metadata/document.rs +++ b/identity_stardust/src/state_metadata/document.rs @@ -30,7 +30,9 @@ const DID_MARKER: &[u8] = b"DID"; /// DID instances in the document are replaced by the `PLACEHOLDER_DID`. #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] pub(crate) struct StateMetadataDocument { + #[serde(rename = "doc")] document: CoreDocument, + #[serde(rename = "meta")] metadata: StardustDocumentMetadata, } @@ -71,7 +73,7 @@ impl StateMetadataDocument { pub fn unpack(data: &[u8]) -> Result { // Check marker. let marker: &[u8] = data.get(0..3).ok_or(identity_did::Error::InvalidDocument( - "expected data to have at least length 3", + "unpack expected data to have at least length 3", None, ))?; if marker != DID_MARKER { @@ -148,6 +150,7 @@ mod tests { use identity_did::did::DID; use identity_did::verification::MethodScope; + use crate::state_metadata::document::DID_MARKER; use crate::state_metadata::PLACEHOLDER_DID; use crate::StardustDID; use crate::StardustDocument; @@ -155,6 +158,7 @@ mod tests { use crate::StardustVerificationMethod; use crate::StateMetadataDocument; use crate::StateMetadataEncoding; + use crate::StateMetadataVersion; struct TestSetup { document: StardustDocument, @@ -298,4 +302,28 @@ mod tests { let unpacked_doc = StateMetadataDocument::unpack(&packed_bytes).unwrap(); assert_eq!(state_metadata_doc, unpacked_doc); } + + #[test] + fn test_pack_format() { + // Changing the serialization is a breaking change! + let TestSetup { document, .. } = test_document(); + let state_metadata_doc: StateMetadataDocument = StateMetadataDocument::from(document); + let packed: Vec = state_metadata_doc.clone().pack(StateMetadataEncoding::Json).unwrap(); + + // DID marker. + assert_eq!(&packed[0..3], DID_MARKER); + // Version. + assert_eq!(packed[3], StateMetadataVersion::V1 as u8); + // Encoding. + assert_eq!(packed[4], StateMetadataEncoding::Json as u8); + // JSON payload. + assert_eq!( + &packed[5..], + format!( + "{{\"doc\":{},\"meta\":{}}}", + state_metadata_doc.document, state_metadata_doc.metadata + ) + .as_bytes() + ); + } } From 9a7e68d6090c53a26eaa4eff004e40970b61459a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Ha=C3=9F?= Date: Tue, 23 Aug 2022 09:32:18 +0200 Subject: [PATCH 39/89] increase verifiy timeout and update to cypress 10 (#980) --- bindings/wasm/cypress.config.ts | 13 + bindings/wasm/cypress.json | 7 - .../createIdentity.cy.js} | 0 .../keyExchange.js => e2e/keyExchange.cy.js} | 0 .../manipulateIdentity.cy.js} | 0 .../privateTangle.cy.js} | 0 .../resolveHistory.cy.js} | 0 .../resolveIdentity.cy.js} | 0 bindings/wasm/package-lock.json | 660 +++++++++++++----- bindings/wasm/package.json | 9 +- 10 files changed, 519 insertions(+), 170 deletions(-) create mode 100644 bindings/wasm/cypress.config.ts delete mode 100644 bindings/wasm/cypress.json rename bindings/wasm/cypress/{integration/createIdentity.js => e2e/createIdentity.cy.js} (100%) rename bindings/wasm/cypress/{integration/keyExchange.js => e2e/keyExchange.cy.js} (100%) rename bindings/wasm/cypress/{integration/manipulateIdentity.js => e2e/manipulateIdentity.cy.js} (100%) rename bindings/wasm/cypress/{integration/privateTangle.js => e2e/privateTangle.cy.js} (100%) rename bindings/wasm/cypress/{integration/resolveHistory.js => e2e/resolveHistory.cy.js} (100%) rename bindings/wasm/cypress/{integration/resolveIdentity.js => e2e/resolveIdentity.cy.js} (100%) diff --git a/bindings/wasm/cypress.config.ts b/bindings/wasm/cypress.config.ts new file mode 100644 index 0000000000..a07a23445c --- /dev/null +++ b/bindings/wasm/cypress.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from 'cypress' + +export default defineConfig({ + screenshotOnRunFailure: false, + video: false, + retries: { + runMode: 3, + }, + e2e: { + setupNodeEvents(on, config) {}, + supportFile: false, + }, +}) diff --git a/bindings/wasm/cypress.json b/bindings/wasm/cypress.json deleted file mode 100644 index 7355427399..0000000000 --- a/bindings/wasm/cypress.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "screenshotOnRunFailure": false, - "video": false, - "retries": { - "runMode": 3 - } -} diff --git a/bindings/wasm/cypress/integration/createIdentity.js b/bindings/wasm/cypress/e2e/createIdentity.cy.js similarity index 100% rename from bindings/wasm/cypress/integration/createIdentity.js rename to bindings/wasm/cypress/e2e/createIdentity.cy.js diff --git a/bindings/wasm/cypress/integration/keyExchange.js b/bindings/wasm/cypress/e2e/keyExchange.cy.js similarity index 100% rename from bindings/wasm/cypress/integration/keyExchange.js rename to bindings/wasm/cypress/e2e/keyExchange.cy.js diff --git a/bindings/wasm/cypress/integration/manipulateIdentity.js b/bindings/wasm/cypress/e2e/manipulateIdentity.cy.js similarity index 100% rename from bindings/wasm/cypress/integration/manipulateIdentity.js rename to bindings/wasm/cypress/e2e/manipulateIdentity.cy.js diff --git a/bindings/wasm/cypress/integration/privateTangle.js b/bindings/wasm/cypress/e2e/privateTangle.cy.js similarity index 100% rename from bindings/wasm/cypress/integration/privateTangle.js rename to bindings/wasm/cypress/e2e/privateTangle.cy.js diff --git a/bindings/wasm/cypress/integration/resolveHistory.js b/bindings/wasm/cypress/e2e/resolveHistory.cy.js similarity index 100% rename from bindings/wasm/cypress/integration/resolveHistory.js rename to bindings/wasm/cypress/e2e/resolveHistory.cy.js diff --git a/bindings/wasm/cypress/integration/resolveIdentity.js b/bindings/wasm/cypress/e2e/resolveIdentity.cy.js similarity index 100% rename from bindings/wasm/cypress/integration/resolveIdentity.js rename to bindings/wasm/cypress/e2e/resolveIdentity.cy.js diff --git a/bindings/wasm/package-lock.json b/bindings/wasm/package-lock.json index 697755f84d..d7dcdd2788 100644 --- a/bindings/wasm/package-lock.json +++ b/bindings/wasm/package-lock.json @@ -18,8 +18,8 @@ "@types/mocha": "^9.1.0", "concurrently": "^7.0.0", "copy-webpack-plugin": "^7.0.0", - "cypress": "^9.3.1", - "cypress-parallel": "^0.1.8", + "cypress": "^10.6.0", + "cypress-parallel": "^0.9.1", "fs-extra": "^10.1.0", "http-server": "^14.1.0", "jsdoc-to-markdown": "^7.1.1", @@ -121,6 +121,7 @@ }, "node_modules/@cypress/request": { "version": "2.88.10", + "resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.10.tgz", "integrity": "sha512-Zp7F+R93N0yZyG34GutyTNr+okam7s/Fzc1+i3kcqOP8vk6OuajuE9qZJ6Rs+10/1JFtXFYMdyarnU1rZuJesg==", "dev": true, "dependencies": { @@ -149,6 +150,7 @@ }, "node_modules/@cypress/request/node_modules/form-data": { "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", "dev": true, "dependencies": { @@ -162,6 +164,7 @@ }, "node_modules/@cypress/xvfb": { "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz", "integrity": "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==", "dev": true, "dependencies": { @@ -171,6 +174,7 @@ }, "node_modules/@cypress/xvfb/node_modules/debug": { "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "dependencies": { @@ -425,11 +429,13 @@ }, "node_modules/@types/sinonjs__fake-timers": { "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz", "integrity": "sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==", "dev": true }, "node_modules/@types/sizzle": { "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz", "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==", "dev": true }, @@ -440,6 +446,7 @@ }, "node_modules/@types/yauzl": { "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz", "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==", "dev": true, "optional": true, @@ -655,6 +662,7 @@ }, "node_modules/aggregate-error": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "dev": true, "dependencies": { @@ -690,6 +698,7 @@ }, "node_modules/ansi-colors": { "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true, "engines": { @@ -717,6 +726,7 @@ }, "node_modules/ansi-escapes": { "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, "dependencies": { @@ -729,6 +739,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/ansi-styles": { "version": "4.3.0", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", @@ -757,6 +776,7 @@ }, "node_modules/arch": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", "dev": true, "funding": [ @@ -810,6 +830,7 @@ }, "node_modules/asn1": { "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", "dev": true, "dependencies": { @@ -818,6 +839,7 @@ }, "node_modules/assert-plus": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", "dev": true, "engines": { @@ -826,6 +848,7 @@ }, "node_modules/astral-regex": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true, "engines": { @@ -852,6 +875,7 @@ }, "node_modules/aws-sign2": { "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", "dev": true, "engines": { @@ -860,6 +884,7 @@ }, "node_modules/aws4": { "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", "dev": true }, @@ -887,6 +912,7 @@ }, "node_modules/base64-js": { "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "dev": true, "funding": [ @@ -922,6 +948,7 @@ }, "node_modules/bcrypt-pbkdf": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", "dev": true, "dependencies": { @@ -967,6 +994,7 @@ }, "node_modules/blob-util": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz", "integrity": "sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==", "dev": true }, @@ -1029,6 +1057,7 @@ }, "node_modules/buffer": { "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "dev": true, "funding": [ @@ -1052,6 +1081,7 @@ }, "node_modules/buffer-crc32": { "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "dev": true, "engines": { @@ -1086,6 +1116,7 @@ }, "node_modules/cachedir": { "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz", "integrity": "sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==", "dev": true, "engines": { @@ -1120,6 +1151,7 @@ }, "node_modules/caseless": { "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", "dev": true }, @@ -1171,6 +1203,7 @@ }, "node_modules/check-more-types": { "version": "2.24.0", + "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", "integrity": "sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==", "dev": true, "engines": { @@ -1213,11 +1246,13 @@ }, "node_modules/ci-info": { "version": "3.3.2", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.2.tgz", "integrity": "sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg==", "dev": true }, "node_modules/clean-stack": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true, "engines": { @@ -1226,6 +1261,7 @@ }, "node_modules/cli-cursor": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "dev": true, "dependencies": { @@ -1251,6 +1287,7 @@ }, "node_modules/cli-truncate": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", "dev": true, "dependencies": { @@ -1339,6 +1376,15 @@ "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", "dev": true }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/combined-stream": { "version": "1.0.8", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", @@ -1432,6 +1478,7 @@ }, "node_modules/commander": { "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", "dev": true, "engines": { @@ -1448,6 +1495,7 @@ }, "node_modules/common-tags": { "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", "dev": true, "engines": { @@ -1522,6 +1570,12 @@ "webpack": "^5.1.0" } }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true + }, "node_modules/corser": { "version": "2.0.1", "integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==", @@ -1549,8 +1603,9 @@ } }, "node_modules/cypress": { - "version": "9.7.0", - "integrity": "sha512-+1EE1nuuuwIt/N1KXRR2iWHU+OiIt7H28jJDyyI4tiUftId/DrXYEwoDa5+kH2pki1zxnA0r6HrUGHV5eLbF5Q==", + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-10.6.0.tgz", + "integrity": "sha512-6sOpHjostp8gcLO34p6r/Ci342lBs8S5z9/eb3ZCQ22w2cIhMWGUoGKkosabPBfKcvRS9BE4UxybBtlIs8gTQA==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -1604,23 +1659,49 @@ "node": ">=12.0.0" } }, + "node_modules/cypress-multi-reporters": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/cypress-multi-reporters/-/cypress-multi-reporters-1.6.1.tgz", + "integrity": "sha512-FPeC0xWF1N6Myrwc2m7KC0xxlrtG8+x4hlsPFBDRWP8u/veR2x90pGaH3BuJfweV7xoQ4Zo85Qjhu3fgZGrBQQ==", + "dev": true, + "peer": true, + "dependencies": { + "debug": "^4.1.1", + "lodash": "^4.17.15" + }, + "engines": { + "node": ">=6.0.0" + }, + "peerDependencies": { + "mocha": ">=3.1.2" + } + }, "node_modules/cypress-parallel": { - "version": "0.1.10", - "integrity": "sha512-TKuJtvM0RU/i/SSkZhAy98FebpVypHqrpic+Y3Pnr3Ujj8JQlUu4R/QOILNI0bdjaPX0WX8yN5KSGmbJmk4VPg==", + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/cypress-parallel/-/cypress-parallel-0.9.1.tgz", + "integrity": "sha512-7VSfFr8HEEN6zkgo6SkG7pPoHK7VakFhEH1jbM4+Ire/I+O2jNzyd1bRUA+O3V2DIMow64ECDJKf13YHBon+BQ==", "dev": true, "dependencies": { "cli-table3": "^0.6.0", + "colors": "^1.4.0", "cross-spawn": "^7.0.3", + "fs-extra": "^10.0.0", + "glob-escape": "^0.0.2", "is-npm": "^5.0.0", + "lodash.camelcase": "^4.3.0", "mocha": "^8.2.1", "yargs": "15.3.1" }, "bin": { "cypress-parallel": "cli.js" + }, + "peerDependencies": { + "cypress-multi-reporters": "^1.5.0" } }, "node_modules/cypress-parallel/node_modules/ansi-colors": { "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true, "engines": { @@ -1628,15 +1709,17 @@ } }, "node_modules/cypress-parallel/node_modules/ansi-regex": { - "version": "3.0.1", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/cypress-parallel/node_modules/camelcase": { "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true, "engines": { @@ -1645,6 +1728,7 @@ }, "node_modules/cypress-parallel/node_modules/chokidar": { "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", "dev": true, "dependencies": { @@ -1665,6 +1749,7 @@ }, "node_modules/cypress-parallel/node_modules/debug": { "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, "dependencies": { @@ -1681,6 +1766,7 @@ }, "node_modules/cypress-parallel/node_modules/decamelize": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", "dev": true, "engines": { @@ -1689,6 +1775,7 @@ }, "node_modules/cypress-parallel/node_modules/escape-string-regexp": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "engines": { @@ -1700,6 +1787,7 @@ }, "node_modules/cypress-parallel/node_modules/glob": { "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "dev": true, "dependencies": { @@ -1717,16 +1805,9 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/cypress-parallel/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/cypress-parallel/node_modules/js-yaml": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", "dev": true, "dependencies": { @@ -1738,6 +1819,7 @@ }, "node_modules/cypress-parallel/node_modules/locate-path": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "dependencies": { @@ -1749,6 +1831,7 @@ }, "node_modules/cypress-parallel/node_modules/log-symbols": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", "dev": true, "dependencies": { @@ -1760,6 +1843,7 @@ }, "node_modules/cypress-parallel/node_modules/minimatch": { "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "dependencies": { @@ -1771,6 +1855,7 @@ }, "node_modules/cypress-parallel/node_modules/mocha": { "version": "8.4.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz", "integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==", "dev": true, "dependencies": { @@ -1814,11 +1899,13 @@ }, "node_modules/cypress-parallel/node_modules/mocha/node_modules/ms": { "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "node_modules/cypress-parallel/node_modules/mocha/node_modules/yargs": { "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "dependencies": { @@ -1836,6 +1923,7 @@ }, "node_modules/cypress-parallel/node_modules/nanoid": { "version": "3.1.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", "dev": true, "bin": { @@ -1847,6 +1935,7 @@ }, "node_modules/cypress-parallel/node_modules/p-limit": { "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "dependencies": { @@ -1861,6 +1950,7 @@ }, "node_modules/cypress-parallel/node_modules/p-locate": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "dependencies": { @@ -1872,6 +1962,7 @@ }, "node_modules/cypress-parallel/node_modules/readdirp": { "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", "dev": true, "dependencies": { @@ -1882,43 +1973,26 @@ } }, "node_modules/cypress-parallel/node_modules/strip-ansi": { - "version": "4.0.0", - "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", - "dev": true, - "dependencies": { - "ansi-regex": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/cypress-parallel/node_modules/wide-align": { - "version": "1.1.3", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "dependencies": { - "string-width": "^1.0.2 || 2" - } - }, - "node_modules/cypress-parallel/node_modules/wide-align/node_modules/string-width": { - "version": "2.1.1", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "dependencies": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/cypress-parallel/node_modules/workerpool": { "version": "6.1.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz", "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==", "dev": true }, "node_modules/cypress-parallel/node_modules/wrap-ansi": { "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, "dependencies": { @@ -1930,27 +2004,9 @@ "node": ">=8" } }, - "node_modules/cypress-parallel/node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "5.0.1", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/cypress-parallel/node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "6.0.1", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/cypress-parallel/node_modules/yargs": { "version": "15.3.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz", "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==", "dev": true, "dependencies": { @@ -1970,16 +2026,9 @@ "node": ">=8" } }, - "node_modules/cypress-parallel/node_modules/yargs/node_modules/ansi-regex": { - "version": "5.0.1", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/cypress-parallel/node_modules/yargs/node_modules/cliui": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", "dev": true, "dependencies": { @@ -1990,6 +2039,7 @@ }, "node_modules/cypress-parallel/node_modules/yargs/node_modules/find-up": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "dependencies": { @@ -2000,24 +2050,15 @@ "node": ">=8" } }, - "node_modules/cypress-parallel/node_modules/yargs/node_modules/strip-ansi": { - "version": "6.0.1", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/cypress-parallel/node_modules/yargs/node_modules/y18n": { "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", "dev": true }, "node_modules/cypress-parallel/node_modules/yargs/node_modules/yargs-parser": { "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "dev": true, "dependencies": { @@ -2029,8 +2070,9 @@ } }, "node_modules/cypress/node_modules/@types/node": { - "version": "14.18.23", - "integrity": "sha512-MhbCWN18R4GhO8ewQWAFK4TGQdBpXWByukz7cWyJmXhvRuCIaM/oWytGPqVmDzgEnnaIc9ss6HbU5mUi+vyZPA==", + "version": "14.18.25", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.25.tgz", + "integrity": "sha512-9pLfceRSrKIsv/MISN6RoFWTIzka36Uk2Uuf5a8cHyDYhEgl5Hm5dXoe621KULeBjt+cFsY18mILsWWtJeG80w==", "dev": true }, "node_modules/cypress/node_modules/fs-extra": { @@ -2050,6 +2092,7 @@ }, "node_modules/dashdash": { "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", "dev": true, "dependencies": { @@ -2072,8 +2115,9 @@ } }, "node_modules/dayjs": { - "version": "1.11.4", - "integrity": "sha512-Zj/lPM5hOvQ1Bf7uAvewDaUcsJoI6JmNqmHhHl3nyumwe0XHwt8sWdOVAPACJzCebL8gQCi+K49w7iKWnGwX9g==", + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.5.tgz", + "integrity": "sha512-CAdX5Q3YW3Gclyo5Vpqkgpj8fSdLQcRuzfX6mC6Phy0nfJ0eGYOeS7m4mt2plDWLAtA4TqTakvbboHvUxfe4iA==", "dev": true }, "node_modules/debug": { @@ -2186,6 +2230,7 @@ }, "node_modules/ecc-jsbn": { "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", "dev": true, "dependencies": { @@ -2213,6 +2258,7 @@ }, "node_modules/end-of-stream": { "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "dev": true, "dependencies": { @@ -2233,6 +2279,7 @@ }, "node_modules/enquirer": { "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", "dev": true, "dependencies": { @@ -2276,6 +2323,7 @@ }, "node_modules/escape-string-regexp": { "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, "engines": { @@ -2323,6 +2371,7 @@ }, "node_modules/eventemitter2": { "version": "6.4.7", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.7.tgz", "integrity": "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==", "dev": true }, @@ -2341,6 +2390,7 @@ }, "node_modules/execa": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", "dev": true, "dependencies": { @@ -2363,6 +2413,7 @@ }, "node_modules/executable": { "version": "4.1.1", + "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", "dev": true, "dependencies": { @@ -2379,6 +2430,7 @@ }, "node_modules/extract-zip": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", "dev": true, "dependencies": { @@ -2398,6 +2450,7 @@ }, "node_modules/extsprintf": { "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", "dev": true, "engines": [ @@ -2447,6 +2500,7 @@ }, "node_modules/fd-slicer": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", "dev": true, "dependencies": { @@ -2455,6 +2509,7 @@ }, "node_modules/figures": { "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", "dev": true, "dependencies": { @@ -2561,6 +2616,7 @@ }, "node_modules/forever-agent": { "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", "dev": true, "engines": { @@ -2617,6 +2673,20 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.1", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", @@ -2632,6 +2702,7 @@ }, "node_modules/get-stream": { "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, "dependencies": { @@ -2646,6 +2717,7 @@ }, "node_modules/getos": { "version": "3.2.1", + "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz", "integrity": "sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==", "dev": true, "dependencies": { @@ -2654,6 +2726,7 @@ }, "node_modules/getpass": { "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", "dev": true, "dependencies": { @@ -2679,6 +2752,15 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/glob-escape": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/glob-escape/-/glob-escape-0.0.2.tgz", + "integrity": "sha512-L/cXYz8x7qer1HAyUQ+mbjcUsJVdpRxpAf7CwqHoNBs9vTpABlGfNN4tzkDxt+u3Z7ZncVyKlCNPtzb0R/7WbA==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/glob-parent": { "version": "5.1.2", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", @@ -2697,6 +2779,7 @@ }, "node_modules/global-dirs": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz", "integrity": "sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==", "dev": true, "dependencies": { @@ -2840,6 +2923,7 @@ }, "node_modules/http-signature": { "version": "1.3.6", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz", "integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==", "dev": true, "dependencies": { @@ -2853,6 +2937,7 @@ }, "node_modules/human-signals": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", "dev": true, "engines": { @@ -2872,6 +2957,7 @@ }, "node_modules/ieee754": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "dev": true, "funding": [ @@ -2917,6 +3003,7 @@ }, "node_modules/indent-string": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true, "engines": { @@ -2939,6 +3026,7 @@ }, "node_modules/ini": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", "dev": true, "engines": { @@ -2988,6 +3076,7 @@ }, "node_modules/is-ci": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", "dev": true, "dependencies": { @@ -3037,6 +3126,7 @@ }, "node_modules/is-installed-globally": { "version": "0.4.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", "dev": true, "dependencies": { @@ -3052,6 +3142,7 @@ }, "node_modules/is-npm": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz", "integrity": "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==", "dev": true, "engines": { @@ -3071,6 +3162,7 @@ }, "node_modules/is-path-inside": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true, "engines": { @@ -3101,6 +3193,7 @@ }, "node_modules/is-stream": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, "engines": { @@ -3112,6 +3205,7 @@ }, "node_modules/is-typedarray": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", "dev": true }, @@ -3141,6 +3235,7 @@ }, "node_modules/isstream": { "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", "dev": true }, @@ -3178,6 +3273,7 @@ }, "node_modules/jsbn": { "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", "dev": true }, @@ -3279,6 +3375,7 @@ }, "node_modules/json-schema": { "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", "dev": true }, @@ -3289,6 +3386,7 @@ }, "node_modules/json-stringify-safe": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", "dev": true }, @@ -3316,6 +3414,7 @@ }, "node_modules/jsprim": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==", "dev": true, "engines": [ @@ -3354,6 +3453,7 @@ }, "node_modules/lazy-ass": { "version": "1.6.0", + "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", "integrity": "sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==", "dev": true, "engines": { @@ -3370,6 +3470,7 @@ }, "node_modules/listr2": { "version": "3.14.0", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.14.0.tgz", "integrity": "sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==", "dev": true, "dependencies": { @@ -3446,6 +3547,7 @@ }, "node_modules/lodash.once": { "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", "dev": true }, @@ -3476,6 +3578,7 @@ }, "node_modules/log-update": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", "dev": true, "dependencies": { @@ -3493,6 +3596,7 @@ }, "node_modules/log-update/node_modules/ansi-regex": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "engines": { @@ -3501,6 +3605,7 @@ }, "node_modules/log-update/node_modules/slice-ansi": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", "dev": true, "dependencies": { @@ -3517,6 +3622,7 @@ }, "node_modules/log-update/node_modules/strip-ansi": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "dependencies": { @@ -3528,6 +3634,7 @@ }, "node_modules/log-update/node_modules/wrap-ansi": { "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, "dependencies": { @@ -3541,6 +3648,7 @@ }, "node_modules/lru-cache": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "dependencies": { @@ -4104,6 +4212,7 @@ }, "node_modules/mimic-fn": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true, "engines": { @@ -4393,6 +4502,7 @@ }, "node_modules/npm-run-path": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, "dependencies": { @@ -4425,6 +4535,7 @@ }, "node_modules/onetime": { "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, "dependencies": { @@ -4447,6 +4558,7 @@ }, "node_modules/ospath": { "version": "1.2.2", + "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz", "integrity": "sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==", "dev": true }, @@ -4480,6 +4592,7 @@ }, "node_modules/p-map": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", "dev": true, "dependencies": { @@ -4539,11 +4652,13 @@ }, "node_modules/pend": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", "dev": true }, "node_modules/performance-now": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", "dev": true }, @@ -4565,6 +4680,7 @@ }, "node_modules/pify": { "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true, "engines": { @@ -4681,6 +4797,7 @@ }, "node_modules/pretty-bytes": { "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", "dev": true, "engines": { @@ -4692,16 +4809,19 @@ }, "node_modules/proxy-from-env": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", "integrity": "sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==", "dev": true }, "node_modules/psl": { "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", "dev": true }, "node_modules/pump": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, "dependencies": { @@ -4880,6 +5000,7 @@ }, "node_modules/request-progress": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", "integrity": "sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==", "dev": true, "dependencies": { @@ -4896,6 +5017,7 @@ }, "node_modules/require-main-filename": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, @@ -4949,6 +5071,7 @@ }, "node_modules/restore-cursor": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "dev": true, "dependencies": { @@ -4970,6 +5093,7 @@ }, "node_modules/rfdc": { "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", "dev": true }, @@ -5076,6 +5200,7 @@ }, "node_modules/semver": { "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, "dependencies": { @@ -5098,6 +5223,7 @@ }, "node_modules/set-blocking": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, @@ -5138,6 +5264,7 @@ }, "node_modules/signal-exit": { "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, @@ -5151,6 +5278,7 @@ }, "node_modules/slice-ansi": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", "dev": true, "dependencies": { @@ -5214,6 +5342,7 @@ }, "node_modules/sshpk": { "version": "1.17.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", "dev": true, "dependencies": { @@ -5298,6 +5427,18 @@ "node": ">=8" } }, + "node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "dev": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/strip-bom": { "version": "3.0.0", "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", @@ -5309,6 +5450,7 @@ }, "node_modules/strip-final-newline": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true, "engines": { @@ -5514,16 +5656,19 @@ }, "node_modules/throttleit": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz", "integrity": "sha512-rkTVqu6IjfQ/6+uNuuc3sZek4CEYxTJom3IktzgdSxcZqdARuebbA/f4QmAxMQIxqq9ZLEUkSYqvuk1I6VKq4g==", "dev": true }, "node_modules/through": { "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, "node_modules/tmp": { "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", "dev": true, "dependencies": { @@ -5546,6 +5691,7 @@ }, "node_modules/tough-cookie": { "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", "dev": true, "dependencies": { @@ -5754,6 +5900,7 @@ }, "node_modules/tunnel-agent": { "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", "dev": true, "dependencies": { @@ -5765,6 +5912,7 @@ }, "node_modules/tweetnacl": { "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", "dev": true }, @@ -5800,6 +5948,7 @@ }, "node_modules/type-fest": { "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true, "engines": { @@ -5899,6 +6048,7 @@ }, "node_modules/untildify": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", "dev": true, "engines": { @@ -5945,6 +6095,7 @@ }, "node_modules/uuid": { "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true, "bin": { @@ -5975,6 +6126,7 @@ }, "node_modules/verror": { "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", "dev": true, "engines": [ @@ -5986,11 +6138,6 @@ "extsprintf": "^1.2.0" } }, - "node_modules/verror/node_modules/core-util-is": { - "version": "1.0.2", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "dev": true - }, "node_modules/vfile": { "version": "5.3.4", "integrity": "sha512-KI+7cnst03KbEyN1+JE504zF5bJBZa+J+CrevLeyIMq0aPU681I2rQ5p4PlnQ6exFtWiUrg26QUdFMnAKR6PIw==", @@ -6223,9 +6370,41 @@ }, "node_modules/which-module": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", "dev": true }, + "node_modules/wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "dependencies": { + "string-width": "^1.0.2 || 2" + } + }, + "node_modules/wide-align/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/wide-align/node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/wildcard": { "version": "2.0.0", "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", @@ -6376,6 +6555,7 @@ }, "node_modules/yauzl": { "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", "dev": true, "dependencies": { @@ -6436,6 +6616,7 @@ }, "@cypress/request": { "version": "2.88.10", + "resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.10.tgz", "integrity": "sha512-Zp7F+R93N0yZyG34GutyTNr+okam7s/Fzc1+i3kcqOP8vk6OuajuE9qZJ6Rs+10/1JFtXFYMdyarnU1rZuJesg==", "dev": true, "requires": { @@ -6461,6 +6642,7 @@ "dependencies": { "form-data": { "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", "dev": true, "requires": { @@ -6473,6 +6655,7 @@ }, "@cypress/xvfb": { "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz", "integrity": "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==", "dev": true, "requires": { @@ -6482,6 +6665,7 @@ "dependencies": { "debug": { "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "requires": { @@ -6708,11 +6892,13 @@ }, "@types/sinonjs__fake-timers": { "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz", "integrity": "sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==", "dev": true }, "@types/sizzle": { "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz", "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==", "dev": true }, @@ -6723,6 +6909,7 @@ }, "@types/yauzl": { "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz", "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==", "dev": true, "optional": true, @@ -6914,6 +7101,7 @@ }, "aggregate-error": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "dev": true, "requires": { @@ -6940,6 +7128,7 @@ }, "ansi-colors": { "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true }, @@ -6960,12 +7149,19 @@ }, "ansi-escapes": { "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, "requires": { "type-fest": "^0.21.3" } }, + "ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "dev": true + }, "ansi-styles": { "version": "4.3.0", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", @@ -6985,6 +7181,7 @@ }, "arch": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", "dev": true }, @@ -7015,6 +7212,7 @@ }, "asn1": { "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", "dev": true, "requires": { @@ -7023,11 +7221,13 @@ }, "assert-plus": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", "dev": true }, "astral-regex": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true }, @@ -7048,11 +7248,13 @@ }, "aws-sign2": { "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", "dev": true }, "aws4": { "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", "dev": true }, @@ -7076,6 +7278,7 @@ }, "base64-js": { "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "dev": true }, @@ -7096,6 +7299,7 @@ }, "bcrypt-pbkdf": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", "dev": true, "requires": { @@ -7129,6 +7333,7 @@ }, "blob-util": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz", "integrity": "sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==", "dev": true }, @@ -7172,6 +7377,7 @@ }, "buffer": { "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "dev": true, "requires": { @@ -7181,6 +7387,7 @@ }, "buffer-crc32": { "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "dev": true }, @@ -7208,6 +7415,7 @@ }, "cachedir": { "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz", "integrity": "sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==", "dev": true }, @@ -7223,6 +7431,7 @@ }, "caseless": { "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", "dev": true }, @@ -7260,6 +7469,7 @@ }, "check-more-types": { "version": "2.24.0", + "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", "integrity": "sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==", "dev": true }, @@ -7285,16 +7495,19 @@ }, "ci-info": { "version": "3.3.2", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.2.tgz", "integrity": "sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg==", "dev": true }, "clean-stack": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true }, "cli-cursor": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "dev": true, "requires": { @@ -7312,6 +7525,7 @@ }, "cli-truncate": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", "dev": true, "requires": { @@ -7381,6 +7595,12 @@ "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", "dev": true }, + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true + }, "combined-stream": { "version": "1.0.8", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", @@ -7456,6 +7676,7 @@ }, "commander": { "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", "dev": true }, @@ -7466,6 +7687,7 @@ }, "common-tags": { "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", "dev": true }, @@ -7520,6 +7742,12 @@ "serialize-javascript": "^5.0.1" } }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true + }, "corser": { "version": "2.0.1", "integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==", @@ -7541,8 +7769,9 @@ } }, "cypress": { - "version": "9.7.0", - "integrity": "sha512-+1EE1nuuuwIt/N1KXRR2iWHU+OiIt7H28jJDyyI4tiUftId/DrXYEwoDa5+kH2pki1zxnA0r6HrUGHV5eLbF5Q==", + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-10.6.0.tgz", + "integrity": "sha512-6sOpHjostp8gcLO34p6r/Ci342lBs8S5z9/eb3ZCQ22w2cIhMWGUoGKkosabPBfKcvRS9BE4UxybBtlIs8gTQA==", "dev": true, "requires": { "@cypress/request": "^2.88.10", @@ -7590,8 +7819,9 @@ }, "dependencies": { "@types/node": { - "version": "14.18.23", - "integrity": "sha512-MhbCWN18R4GhO8ewQWAFK4TGQdBpXWByukz7cWyJmXhvRuCIaM/oWytGPqVmDzgEnnaIc9ss6HbU5mUi+vyZPA==", + "version": "14.18.25", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.25.tgz", + "integrity": "sha512-9pLfceRSrKIsv/MISN6RoFWTIzka36Uk2Uuf5a8cHyDYhEgl5Hm5dXoe621KULeBjt+cFsY18mILsWWtJeG80w==", "dev": true }, "fs-extra": { @@ -7608,35 +7838,55 @@ } } }, + "cypress-multi-reporters": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/cypress-multi-reporters/-/cypress-multi-reporters-1.6.1.tgz", + "integrity": "sha512-FPeC0xWF1N6Myrwc2m7KC0xxlrtG8+x4hlsPFBDRWP8u/veR2x90pGaH3BuJfweV7xoQ4Zo85Qjhu3fgZGrBQQ==", + "dev": true, + "peer": true, + "requires": { + "debug": "^4.1.1", + "lodash": "^4.17.15" + } + }, "cypress-parallel": { - "version": "0.1.10", - "integrity": "sha512-TKuJtvM0RU/i/SSkZhAy98FebpVypHqrpic+Y3Pnr3Ujj8JQlUu4R/QOILNI0bdjaPX0WX8yN5KSGmbJmk4VPg==", + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/cypress-parallel/-/cypress-parallel-0.9.1.tgz", + "integrity": "sha512-7VSfFr8HEEN6zkgo6SkG7pPoHK7VakFhEH1jbM4+Ire/I+O2jNzyd1bRUA+O3V2DIMow64ECDJKf13YHBon+BQ==", "dev": true, "requires": { "cli-table3": "^0.6.0", + "colors": "^1.4.0", "cross-spawn": "^7.0.3", + "fs-extra": "^10.0.0", + "glob-escape": "^0.0.2", "is-npm": "^5.0.0", + "lodash.camelcase": "^4.3.0", "mocha": "^8.2.1", "yargs": "15.3.1" }, "dependencies": { "ansi-colors": { "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true }, "ansi-regex": { - "version": "3.0.1", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "camelcase": { "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, "chokidar": { "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", "dev": true, "requires": { @@ -7652,6 +7902,7 @@ }, "debug": { "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, "requires": { @@ -7660,16 +7911,19 @@ }, "decamelize": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", "dev": true }, "escape-string-regexp": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, "glob": { "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "dev": true, "requires": { @@ -7681,13 +7935,9 @@ "path-is-absolute": "^1.0.0" } }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "dev": true - }, "js-yaml": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", "dev": true, "requires": { @@ -7696,6 +7946,7 @@ }, "locate-path": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "requires": { @@ -7704,6 +7955,7 @@ }, "log-symbols": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", "dev": true, "requires": { @@ -7712,6 +7964,7 @@ }, "minimatch": { "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { @@ -7720,6 +7973,7 @@ }, "mocha": { "version": "8.4.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz", "integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==", "dev": true, "requires": { @@ -7752,11 +8006,13 @@ "dependencies": { "ms": { "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "yargs": { "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "requires": { @@ -7773,11 +8029,13 @@ }, "nanoid": { "version": "3.1.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", "dev": true }, "p-limit": { "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "requires": { @@ -7786,6 +8044,7 @@ }, "p-locate": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "requires": { @@ -7794,6 +8053,7 @@ }, "readdirp": { "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", "dev": true, "requires": { @@ -7801,64 +8061,34 @@ } }, "strip-ansi": { - "version": "4.0.0", - "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - }, - "wide-align": { - "version": "1.1.3", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { - "string-width": "^1.0.2 || 2" - }, - "dependencies": { - "string-width": { - "version": "2.1.1", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - } + "ansi-regex": "^5.0.1" } }, "workerpool": { "version": "6.1.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz", "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==", "dev": true }, "wrap-ansi": { "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, "requires": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "strip-ansi": { - "version": "6.0.1", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - } } }, "yargs": { "version": "15.3.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz", "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==", "dev": true, "requires": { @@ -7875,13 +8105,9 @@ "yargs-parser": "^18.1.1" }, "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, "cliui": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", "dev": true, "requires": { @@ -7892,6 +8118,7 @@ }, "find-up": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "requires": { @@ -7899,21 +8126,15 @@ "path-exists": "^4.0.0" } }, - "strip-ansi": { - "version": "6.0.1", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, "y18n": { "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", "dev": true }, "yargs-parser": { "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "dev": true, "requires": { @@ -7927,6 +8148,7 @@ }, "dashdash": { "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", "dev": true, "requires": { @@ -7939,8 +8161,9 @@ "dev": true }, "dayjs": { - "version": "1.11.4", - "integrity": "sha512-Zj/lPM5hOvQ1Bf7uAvewDaUcsJoI6JmNqmHhHl3nyumwe0XHwt8sWdOVAPACJzCebL8gQCi+K49w7iKWnGwX9g==", + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.5.tgz", + "integrity": "sha512-CAdX5Q3YW3Gclyo5Vpqkgpj8fSdLQcRuzfX6mC6Phy0nfJ0eGYOeS7m4mt2plDWLAtA4TqTakvbboHvUxfe4iA==", "dev": true }, "debug": { @@ -8017,6 +8240,7 @@ }, "ecc-jsbn": { "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", "dev": true, "requires": { @@ -8041,6 +8265,7 @@ }, "end-of-stream": { "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "dev": true, "requires": { @@ -8058,6 +8283,7 @@ }, "enquirer": { "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", "dev": true, "requires": { @@ -8086,6 +8312,7 @@ }, "escape-string-regexp": { "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true }, @@ -8120,6 +8347,7 @@ }, "eventemitter2": { "version": "6.4.7", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.7.tgz", "integrity": "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==", "dev": true }, @@ -8135,6 +8363,7 @@ }, "execa": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", "dev": true, "requires": { @@ -8151,6 +8380,7 @@ }, "executable": { "version": "4.1.1", + "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", "dev": true, "requires": { @@ -8164,6 +8394,7 @@ }, "extract-zip": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", "dev": true, "requires": { @@ -8175,6 +8406,7 @@ }, "extsprintf": { "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", "dev": true }, @@ -8215,6 +8447,7 @@ }, "fd-slicer": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", "dev": true, "requires": { @@ -8223,6 +8456,7 @@ }, "figures": { "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", "dev": true, "requires": { @@ -8289,6 +8523,7 @@ }, "forever-agent": { "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", "dev": true }, @@ -8330,6 +8565,13 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, "function-bind": { "version": "1.1.1", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", @@ -8342,6 +8584,7 @@ }, "get-stream": { "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, "requires": { @@ -8350,6 +8593,7 @@ }, "getos": { "version": "3.2.1", + "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz", "integrity": "sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==", "dev": true, "requires": { @@ -8358,6 +8602,7 @@ }, "getpass": { "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", "dev": true, "requires": { @@ -8377,6 +8622,12 @@ "path-is-absolute": "^1.0.0" } }, + "glob-escape": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/glob-escape/-/glob-escape-0.0.2.tgz", + "integrity": "sha512-L/cXYz8x7qer1HAyUQ+mbjcUsJVdpRxpAf7CwqHoNBs9vTpABlGfNN4tzkDxt+u3Z7ZncVyKlCNPtzb0R/7WbA==", + "dev": true + }, "glob-parent": { "version": "5.1.2", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", @@ -8392,6 +8643,7 @@ }, "global-dirs": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz", "integrity": "sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==", "dev": true, "requires": { @@ -8491,6 +8743,7 @@ }, "http-signature": { "version": "1.3.6", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz", "integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==", "dev": true, "requires": { @@ -8501,6 +8754,7 @@ }, "human-signals": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", "dev": true }, @@ -8514,6 +8768,7 @@ }, "ieee754": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "dev": true }, @@ -8533,6 +8788,7 @@ }, "indent-string": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true }, @@ -8552,6 +8808,7 @@ }, "ini": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", "dev": true }, @@ -8575,6 +8832,7 @@ }, "is-ci": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", "dev": true, "requires": { @@ -8609,6 +8867,7 @@ }, "is-installed-globally": { "version": "0.4.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", "dev": true, "requires": { @@ -8618,6 +8877,7 @@ }, "is-npm": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz", "integrity": "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==", "dev": true }, @@ -8628,6 +8888,7 @@ }, "is-path-inside": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true }, @@ -8646,11 +8907,13 @@ }, "is-stream": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true }, "is-typedarray": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", "dev": true }, @@ -8671,6 +8934,7 @@ }, "isstream": { "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", "dev": true }, @@ -8702,6 +8966,7 @@ }, "jsbn": { "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", "dev": true }, @@ -8784,6 +9049,7 @@ }, "json-schema": { "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", "dev": true }, @@ -8794,6 +9060,7 @@ }, "json-stringify-safe": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", "dev": true }, @@ -8813,6 +9080,7 @@ }, "jsprim": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==", "dev": true, "requires": { @@ -8842,6 +9110,7 @@ }, "lazy-ass": { "version": "1.6.0", + "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", "integrity": "sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==", "dev": true }, @@ -8855,6 +9124,7 @@ }, "listr2": { "version": "3.14.0", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.14.0.tgz", "integrity": "sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==", "dev": true, "requires": { @@ -8908,6 +9178,7 @@ }, "lodash.once": { "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", "dev": true }, @@ -8932,6 +9203,7 @@ }, "log-update": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", "dev": true, "requires": { @@ -8943,11 +9215,13 @@ "dependencies": { "ansi-regex": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "slice-ansi": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", "dev": true, "requires": { @@ -8958,6 +9232,7 @@ }, "strip-ansi": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { @@ -8966,6 +9241,7 @@ }, "wrap-ansi": { "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, "requires": { @@ -8978,6 +9254,7 @@ }, "lru-cache": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "requires": { @@ -9290,6 +9567,7 @@ }, "mimic-fn": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true }, @@ -9494,6 +9772,7 @@ }, "npm-run-path": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, "requires": { @@ -9520,6 +9799,7 @@ }, "onetime": { "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, "requires": { @@ -9533,6 +9813,7 @@ }, "ospath": { "version": "1.2.2", + "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz", "integrity": "sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==", "dev": true }, @@ -9554,6 +9835,7 @@ }, "p-map": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", "dev": true, "requires": { @@ -9592,11 +9874,13 @@ }, "pend": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", "dev": true }, "performance-now": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", "dev": true }, @@ -9612,6 +9896,7 @@ }, "pify": { "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true }, @@ -9705,21 +9990,25 @@ }, "pretty-bytes": { "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", "dev": true }, "proxy-from-env": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", "integrity": "sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==", "dev": true }, "psl": { "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", "dev": true }, "pump": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, "requires": { @@ -9848,6 +10137,7 @@ }, "request-progress": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", "integrity": "sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==", "dev": true, "requires": { @@ -9861,6 +10151,7 @@ }, "require-main-filename": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, @@ -9902,6 +10193,7 @@ }, "restore-cursor": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "dev": true, "requires": { @@ -9916,6 +10208,7 @@ }, "rfdc": { "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", "dev": true }, @@ -9978,6 +10271,7 @@ }, "semver": { "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, "requires": { @@ -9994,6 +10288,7 @@ }, "set-blocking": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, @@ -10025,6 +10320,7 @@ }, "signal-exit": { "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, @@ -10035,6 +10331,7 @@ }, "slice-ansi": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", "dev": true, "requires": { @@ -10085,6 +10382,7 @@ }, "sshpk": { "version": "1.17.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", "dev": true, "requires": { @@ -10147,6 +10445,15 @@ } } }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + }, "strip-bom": { "version": "3.0.0", "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", @@ -10155,6 +10462,7 @@ }, "strip-final-newline": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true }, @@ -10300,16 +10608,19 @@ }, "throttleit": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz", "integrity": "sha512-rkTVqu6IjfQ/6+uNuuc3sZek4CEYxTJom3IktzgdSxcZqdARuebbA/f4QmAxMQIxqq9ZLEUkSYqvuk1I6VKq4g==", "dev": true }, "through": { "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, "tmp": { "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", "dev": true, "requires": { @@ -10326,6 +10637,7 @@ }, "tough-cookie": { "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", "dev": true, "requires": { @@ -10470,6 +10782,7 @@ }, "tunnel-agent": { "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", "dev": true, "requires": { @@ -10478,6 +10791,7 @@ }, "tweetnacl": { "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", "dev": true }, @@ -10503,6 +10817,7 @@ }, "type-fest": { "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true }, @@ -10569,6 +10884,7 @@ }, "untildify": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", "dev": true }, @@ -10596,6 +10912,7 @@ }, "uuid": { "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true }, @@ -10617,19 +10934,13 @@ }, "verror": { "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", "dev": true, "requires": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", "extsprintf": "^1.2.0" - }, - "dependencies": { - "core-util-is": { - "version": "1.0.2", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "dev": true - } } }, "vfile": { @@ -10784,9 +11095,37 @@ }, "which-module": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", "dev": true }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + } + } + }, "wildcard": { "version": "2.0.0", "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", @@ -10909,6 +11248,7 @@ }, "yauzl": { "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", "dev": true, "requires": { diff --git a/bindings/wasm/package.json b/bindings/wasm/package.json index bf1a8039c9..1ee61ed043 100644 --- a/bindings/wasm/package.json +++ b/bindings/wasm/package.json @@ -27,13 +27,16 @@ "test:node": "mocha ./examples/dist/tests/node/*.js --parallel --jobs 4 --retries 3 --timeout 180000 --exit", "test:account:node": "ts-mocha -p tsconfig.node.json ./examples-account/src/tests/*.ts --parallel --jobs 4 --retries 3 --timeout 180000 --exit", "test:stardust": "ts-mocha -p tsconfig.node.json ./examples-stardust/src/tests/*.ts --parallel --jobs 4 --retries 3 --timeout 180000 --exit", - "test:browser:parallel": "cypress-parallel -s test:browser -t 4 -d cypress/integration -a '\"--quiet\"'", + "test:browser:parallel": "cypress-parallel -s test:browser -t 4 -d cypress/e2e -a '\"--quiet\"'", "test:browser": "cypress run --headless", "test:readme": "mocha ./tests/txm_readme.js --retries 3 --timeout 180000 --exit", "test:unit": "wasm-pack test --release --node", "test:unit:node": "ts-mocha -p tsconfig.node.json ./tests/*.ts --parallel --exit", "cypress": "cypress open" }, + "config": { + "CYPRESS_VERIFY_TIMEOUT": 100000 + }, "contributors": [ "Jelle Millenaar ", "Devin Turner ", @@ -58,8 +61,8 @@ "@types/mocha": "^9.1.0", "concurrently": "^7.0.0", "copy-webpack-plugin": "^7.0.0", - "cypress": "^9.3.1", - "cypress-parallel": "^0.1.8", + "cypress": "^10.6.0", + "cypress-parallel": "^0.9.1", "fs-extra": "^10.1.0", "http-server": "^14.1.0", "jsdoc-to-markdown": "^7.1.1", From 67a3bf512d983dfe263073d92663bc2097ac074f Mon Sep 17 00:00:00 2001 From: Philipp Date: Wed, 24 Aug 2022 17:43:07 +0200 Subject: [PATCH 40/89] Extend and refactor Stardust examples (#981) * Commit example for testing/debugging * Working alias-controls-alias example * Impl `StardustDID` -> `AliasId` conversion * Add more (unfinished) examples * Fix native assets example * Remove adding Sender Feature by default * Polish alias-controls-alias example * Use latest draft PR commit * Use `AliasId::from` instead of `AliasId::try_from` * Use `iota-client` crates version * Add token issue example * Add nft owns did example * Rename examples * Refactor examples into separate project * Polish examples * Rename examples stardust directory * Update CI examples instructions * Remove `./` * Remove unknown clippy lint * Polish examples * Polish utils * Remove unused manifest key in examples * Polish examples more * Revert "Remove unknown clippy lint" This reverts commit 7dd77e949ee60195f7eb58593323289e1408b5b0. * Remove dedicated stardust build step in CI * Integrated stardust crates into root workspace * Bump bee-block version to match iota-client * Impl `From` for re-exported `AliasId` * Inline NFT example creation * Use Stronghold secret manager * Use double-digits in example names * Improve examples with suggestions * Remove unnecessary CI steps * Rename package to `examples` * Add examples README * Number sub-folders * Improve variable names in DPP example * Use single-digit within folders after all * Rename old examples to examples_legacy * Add key exchange example * Rename example stronghold file * Use network bech 32 in utils * Use Shimmer Testnet * Use "basic" instead of "crud" folder name * Fix links to examples in Wiki * Use new banner * Fix example paths in CI * Use Shimmer Testnet in Wasm examples * Clarify issuer feature in example * Use explicit digit regex * Remove stronghold file in CI * Revert "Remove stronghold file in CI" This reverts commit e7ee19956d99a02a4e907a5a1cd8e387ae7ad9eb. * Improve examples README * Revert "Revert "Remove stronghold file in CI"" This reverts commit ef80751c774b83b7a03b35512ecd6fcf5dc140a0. * Enable `fs` tokio feature * Remove stronghold removal for CI * Use random stronghold paths --- .github/workflows/build-and-test.yml | 57 +----- .github/workflows/clippy.yml | 7 - .github/workflows/format.yml | 6 - .meta/identity_banner.png | Bin 138128 -> 0 bytes Cargo.toml | 2 + README.md | 2 +- bindings/wasm/examples-account/README.md | 2 +- bindings/wasm/examples-stardust/README.md | 2 +- .../examples-stardust/src/ex0_create_did.ts | 5 +- bindings/wasm/examples/README.md | 2 +- .../decentralized_identifiers/create.mdx | 4 +- .../private_tangle.mdx | 4 +- .../decentralized_identifiers/resolve.mdx | 8 +- .../decentralized_identifiers/update.mdx | 6 +- .../verifiable_credentials/create.mdx | 4 +- .../verifiable_credentials/revocation.mdx | 4 +- .../verifiable_presentations.mdx | 4 +- .../getting_started/create_and_publish.mdx | 4 +- examples/0_basic/0_create_did.rs | 64 ++++++ .../0_basic/1_update_did.rs | 33 ++- .../0_basic/2_resolve_did.rs | 27 ++- .../0_basic/3_deactivate_did.rs | 29 ++- .../0_basic/4_delete_did.rs | 26 ++- examples/1_advanced/0_did_controls_did.rs | 143 +++++++++++++ examples/1_advanced/1_did_issues_nft.rs | 147 ++++++++++++++ examples/1_advanced/2_nft_owns_did.rs | 146 ++++++++++++++ examples/1_advanced/3_did_issues_tokens.rs | 190 ++++++++++++++++++ examples/1_advanced/4_key_exchange.rs | 133 ++++++++++++ examples/Cargo.toml | 88 +++----- examples/README.md | 62 +++--- .../utils/utils.rs | 120 ++++++----- examples_legacy/Cargo.toml | 84 ++++++++ examples_legacy/README.md | 47 +++++ .../account/config.rs | 0 .../account/create_did.rs | 0 .../account/create_vc.rs | 0 .../account/create_vp.rs | 0 .../account/encryption.rs | 0 {examples => examples_legacy}/account/lazy.rs | 0 .../account/manipulate_did.rs | 0 .../account/multiple_identities.rs | 0 .../account/revoke_vc.rs | 0 .../account/signing.rs | 0 .../account/unchecked.rs | 0 .../getting_started.rs | 0 .../low-level-api/common.rs | 0 .../low-level-api/create_did.rs | 0 .../low-level-api/key_exchange.rs | 0 .../low-level-api/manipulate_did.rs | 0 .../low-level-api/private_tangle.rs | 0 .../low-level-api/resolve_did.rs | 0 .../low-level-api/resolve_history.rs | 0 identity_iota/README.md | 2 +- identity_stardust/Cargo.toml | 11 +- .../src/client/identity_client.rs | 20 +- identity_stardust/src/client/iota_client.rs | 2 +- identity_stardust/src/did/stardust_did.rs | 15 ++ 57 files changed, 1218 insertions(+), 294 deletions(-) delete mode 100644 .meta/identity_banner.png create mode 100644 examples/0_basic/0_create_did.rs rename identity_stardust/examples/ex1_update_did.rs => examples/0_basic/1_update_did.rs (69%) rename identity_stardust/examples/ex2_resolve_did.rs => examples/0_basic/2_resolve_did.rs (50%) rename identity_stardust/examples/ex3_deactivate_did.rs => examples/0_basic/3_deactivate_did.rs (78%) rename identity_stardust/examples/ex4_delete_did.rs => examples/0_basic/4_delete_did.rs (62%) create mode 100644 examples/1_advanced/0_did_controls_did.rs create mode 100644 examples/1_advanced/1_did_issues_nft.rs create mode 100644 examples/1_advanced/2_nft_owns_did.rs create mode 100644 examples/1_advanced/3_did_issues_tokens.rs create mode 100644 examples/1_advanced/4_key_exchange.rs rename identity_stardust/examples/ex0_create_did.rs => examples/utils/utils.rs (50%) create mode 100644 examples_legacy/Cargo.toml create mode 100644 examples_legacy/README.md rename {examples => examples_legacy}/account/config.rs (100%) rename {examples => examples_legacy}/account/create_did.rs (100%) rename {examples => examples_legacy}/account/create_vc.rs (100%) rename {examples => examples_legacy}/account/create_vp.rs (100%) rename {examples => examples_legacy}/account/encryption.rs (100%) rename {examples => examples_legacy}/account/lazy.rs (100%) rename {examples => examples_legacy}/account/manipulate_did.rs (100%) rename {examples => examples_legacy}/account/multiple_identities.rs (100%) rename {examples => examples_legacy}/account/revoke_vc.rs (100%) rename {examples => examples_legacy}/account/signing.rs (100%) rename {examples => examples_legacy}/account/unchecked.rs (100%) rename {examples => examples_legacy}/getting_started.rs (100%) rename {examples => examples_legacy}/low-level-api/common.rs (100%) rename {examples => examples_legacy}/low-level-api/create_did.rs (100%) rename {examples => examples_legacy}/low-level-api/key_exchange.rs (100%) rename {examples => examples_legacy}/low-level-api/manipulate_did.rs (100%) rename {examples => examples_legacy}/low-level-api/private_tangle.rs (100%) rename {examples => examples_legacy}/low-level-api/resolve_did.rs (100%) rename {examples => examples_legacy}/low-level-api/resolve_history.rs (100%) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index a9ccc1352a..e884a280ae 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -114,67 +114,18 @@ jobs: # run examples only on ubuntu for now if: matrix.os == 'ubuntu-latest' run: | - cargo read-manifest --manifest-path ./examples/Cargo.toml | \ + cargo read-manifest --manifest-path ./examples_legacy/Cargo.toml | \ jq -r '.targets[].name' | \ parallel -k -j 4 --retries 3 cargo run --example {} --all-features --release - - name: Stop sccache - uses: './.github/actions/rust/sccache/stop-sccache' - with: - os: ${{matrix.os}} - - build-and-test-stardust: - needs: [ check-for-run-condition, check-for-modification ] - if: ${{ needs.check-for-run-condition.outputs.should-run == 'true' && needs.check-for-modification.outputs.core-modified == 'true' }} - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - os: [ ubuntu-latest ] - include: - - os: ubuntu-latest - sccache-path: /home/runner/.cache/sccache - env: - SCCACHE_DIR: ${{ matrix.sccache-path }} - RUSTC_WRAPPER: sccache - - steps: - - uses: actions/checkout@v2 - - - name: Setup Rust and cache - uses: './.github/actions/rust/rust-setup' - with: - os: ${{ runner.os }} - job: ${{ github.job }} - target-cache-enabled: false - sccache-enabled: true - sccache-path: ${{ matrix.sccache-path }} - - - name: Setup sccache - uses: './.github/actions/rust/sccache/setup-sccache' - with: - os: ${{matrix.os}} - - - name: Build Stardust - uses: actions-rs/cargo@v1 - with: - command: build - args: --manifest-path ./identity_stardust/Cargo.toml --workspace --tests --examples --all-features --release - - - name: Run Stardust tests - uses: actions-rs/cargo@v1 - with: - command: test - args: --manifest-path ./identity_stardust/Cargo.toml --workspace --all-features --release - - name: Run Stardust Rust examples # run examples only on ubuntu for now if: matrix.os == 'ubuntu-latest' run: | - cargo read-manifest --manifest-path ./identity_stardust/Cargo.toml | \ + cargo read-manifest --manifest-path ./examples/Cargo.toml | \ jq -r '.targets[].name' | \ - awk '$1 ~ /ex.*/' | \ - parallel -k -j 4 --retries 3 cargo run --manifest-path ./identity_stardust/Cargo.toml --example {} --all-features --release + awk '$1 ~ /[0-9].*/' | \ + parallel -k -j 4 --retries 3 cargo run --manifest-path ./examples/Cargo.toml --example {} --all-features --release - name: Stop sccache uses: './.github/actions/rust/sccache/stop-sccache' diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index ce08221e1a..3005724418 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -43,13 +43,6 @@ jobs: args: --all-targets --all-features -- -D warnings name: core - - name: Stardust clippy check - uses: actions-rs/clippy-check@v1 - with: - token: ${{ secrets.GITHUB_TOKEN }} - args: --manifest-path ./identity_stardust/Cargo.toml --all-targets --all-features -- -D warnings - name: stardust - - name: Wasm clippy check uses: actions-rs/clippy-check@v1 with: diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index ae910b33a3..9e8894b595 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -41,12 +41,6 @@ jobs: command: fmt args: --all -- --check - - name: Stardust fmt check - uses: actions-rs/cargo@v1 - with: - command: fmt - args: --manifest-path ./identity_stardust/Cargo.toml --all -- --check - - name: wasm fmt check uses: actions-rs/cargo@v1 with: diff --git a/.meta/identity_banner.png b/.meta/identity_banner.png deleted file mode 100644 index c78a32862622152d61ecabdb8e77d0388f601d65..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 138128 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 F{{b|D`E~#R diff --git a/Cargo.toml b/Cargo.toml index e564cfa7e4..b4342ed792 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,9 @@ members = [ "identity_iota", "identity_iota_client", "identity_iota_core", + "identity_stardust", + "examples_legacy", "examples", ] diff --git a/README.md b/README.md index d8f7db8ce6..135b9856b6 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![banner](https://github.com/iotaledger/identity.rs/raw/HEAD/.meta/identity_banner.png) +![banner](https://github.com/iotaledger/identity.rs/raw/HEAD/documentation/static/img/Banner/banner_identity.svg)

StackExchange diff --git a/bindings/wasm/examples-account/README.md b/bindings/wasm/examples-account/README.md index 64f137e3b0..d8f4c4e416 100644 --- a/bindings/wasm/examples-account/README.md +++ b/bindings/wasm/examples-account/README.md @@ -1,4 +1,4 @@ -![banner](./../../../.meta/identity_banner.png) +![banner](./../../../documentation/static/img/Banner/banner_identity.svg) ## IOTA Identity Account Examples diff --git a/bindings/wasm/examples-stardust/README.md b/bindings/wasm/examples-stardust/README.md index ecce863248..6594c6597e 100644 --- a/bindings/wasm/examples-stardust/README.md +++ b/bindings/wasm/examples-stardust/README.md @@ -1,4 +1,4 @@ -![banner](./../../../.meta/identity_banner.png) +![banner](./../../../documentation/static/img/Banner/banner_identity.svg) ## IOTA Identity UTXO Examples diff --git a/bindings/wasm/examples-stardust/src/ex0_create_did.ts b/bindings/wasm/examples-stardust/src/ex0_create_did.ts index ca690a9f05..e1fe698b31 100644 --- a/bindings/wasm/examples-stardust/src/ex0_create_did.ts +++ b/bindings/wasm/examples-stardust/src/ex0_create_did.ts @@ -15,9 +15,8 @@ import {Bip39} from "@iota/crypto.js"; import fetch from "node-fetch"; import {Client, MnemonicSecretManager, SecretManager} from "@cycraig/iota-client-wasm/node"; -const EXPLORER = "https://explorer.alphanet.iotaledger.net/alphanet"; -const API_ENDPOINT = "https://api.alphanet.iotaledger.net/"; -const FAUCET = "https://faucet.alphanet.iotaledger.net/api/enqueue"; +const API_ENDPOINT = "https://api.testnet.shimmer.network/"; +const FAUCET = "https://faucet.testnet.shimmer.network/api/enqueue"; /** Demonstrate how to create a DID Document and publish it in a new Alias Output. */ export async function createIdentity(): Promise<{ diff --git a/bindings/wasm/examples/README.md b/bindings/wasm/examples/README.md index bb500f3526..09d6771208 100644 --- a/bindings/wasm/examples/README.md +++ b/bindings/wasm/examples/README.md @@ -1,4 +1,4 @@ -![banner](./../../../.meta/identity_banner.png) +![banner](./../../../documentation/static/img/Banner/banner_identity.svg) ## IOTA Identity Examples diff --git a/documentation/docs/concepts/decentralized_identifiers/create.mdx b/documentation/docs/concepts/decentralized_identifiers/create.mdx index 1039d269aa..a9cd6d66bd 100644 --- a/documentation/docs/concepts/decentralized_identifiers/create.mdx +++ b/documentation/docs/concepts/decentralized_identifiers/create.mdx @@ -11,7 +11,7 @@ keywords: - Publish --- import CodeSnippet from '../../../src/components/CodeSnippetComponent' -import createDidRustExample from '!!raw-loader!../../../../examples/account/create_did.rs'; +import createDidRustExample from '!!raw-loader!../../../../examples_legacy/account/create_did.rs'; When someone or something wants to benefit from Self-Sovereign Identity, they must first create a Decentralized Identity. This identity consists of many parts that have different functions. This page will cover both the basics and the details about identity creation, storage, and publishing to the Tangle. @@ -29,7 +29,7 @@ Select your programming language of choice and press the green play button to ex nodeReplitLink="https://repl.it/@IOTAFoundation/create-did?lite=true" rustContent={createDidRustExample} nodeGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/bindings/wasm/examples-account/src/create_did.ts" - rustGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/examples/account/create_did.rs" + rustGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/examples_legacy/account/create_did.rs" /> The first step in this example is the creation of an account. This acts as a stateful object that manages one or more identities. The account provides an interface to execute high-level operations on identities, such as creating, updating, and storing them. diff --git a/documentation/docs/concepts/decentralized_identifiers/private_tangle.mdx b/documentation/docs/concepts/decentralized_identifiers/private_tangle.mdx index 6b02aa5832..8a26f834d8 100644 --- a/documentation/docs/concepts/decentralized_identifiers/private_tangle.mdx +++ b/documentation/docs/concepts/decentralized_identifiers/private_tangle.mdx @@ -11,8 +11,8 @@ import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import CodeBlock from '@theme/CodeBlock'; import private_tangle_js from '!!raw-loader!../../../../bindings/wasm/examples/src/private_tangle.js'; -import private_tangle_rs from '!!raw-loader!../../../../examples/low-level-api/private_tangle.rs'; -import account_private_tangle_rs from '!!raw-loader!../../../../examples/account/config.rs'; +import private_tangle_rs from '!!raw-loader!../../../../examples_legacy/low-level-api/private_tangle.rs'; +import account_private_tangle_rs from '!!raw-loader!../../../../examples_legacy/account/config.rs'; ## Example diff --git a/documentation/docs/concepts/decentralized_identifiers/resolve.mdx b/documentation/docs/concepts/decentralized_identifiers/resolve.mdx index b636bbb5fb..78405873b6 100644 --- a/documentation/docs/concepts/decentralized_identifiers/resolve.mdx +++ b/documentation/docs/concepts/decentralized_identifiers/resolve.mdx @@ -6,8 +6,8 @@ image: /img/Identity_icon.png keywords: - Resolve --- -import resolve_did_rs from '!!raw-loader!../../../../examples/low-level-api/resolve_did.rs'; -import resolve_history_rs from '!!raw-loader!../../../../examples/low-level-api/resolve_history.rs'; +import resolve_did_rs from '!!raw-loader!../../../../examples_legacy/low-level-api/resolve_did.rs'; +import resolve_history_rs from '!!raw-loader!../../../../examples_legacy/low-level-api/resolve_history.rs'; import resolve_did_js from '!!raw-loader!../../../../bindings/wasm/examples/src/resolve_did.js'; import resolve_history_js from '!!raw-loader!../../../../bindings/wasm/examples/src/resolve_history.js'; import CodeSnippet from '../../../src/components/CodeSnippetComponent' @@ -61,7 +61,7 @@ What happens in this example can be explained on a high level as follows: The Re ## Resolving from a private tangle Resolving a DID from a private tangle is similar to resolving a DID from the main net. The only difference is that -the resolver needs to be configured to have a client capable of operating on said private tangle. Building a `Client` configured for a specified Tangle is explained in [this example in Rust](https://github.com/iotaledger/identity.rs/blob/dev/examples/low-level-api/private_tangle.rs) and [this example in Javascript](https://github.com/iotaledger/identity.rs/blob/dev/bindings/wasm/examples/src/private_tangle.js). +the resolver needs to be configured to have a client capable of operating on said private tangle. Building a `Client` configured for a specified Tangle is explained in [this example in Rust](https://github.com/iotaledger/identity.rs/blob/dev/examples_legacy/low-level-api/private_tangle.rs) and [this example in Javascript](https://github.com/iotaledger/identity.rs/blob/dev/bindings/wasm/examples/src/private_tangle.js). The following example demonstrates how one can setup a `Resolver` with a given `client` and then attempt resolving a specified `did` which may be on any Tangle (public or private). @@ -168,7 +168,7 @@ This section shows complete examples from the Iota Identity Framework code base. nodeReplitLink="https://repl.it/@IOTAFoundation/resolve-did?lite=true" rustContent={resolve_did_rs} nodeGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/bindings/wasm/examples/src/resolve_did.js" - rustGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/examples/low-level-api/resolve_did.rs" + rustGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/examples_legacy/low-level-api/resolve_did.rs" /> diff --git a/documentation/docs/concepts/decentralized_identifiers/update.mdx b/documentation/docs/concepts/decentralized_identifiers/update.mdx index ae95b5e834..91bd8e2d48 100644 --- a/documentation/docs/concepts/decentralized_identifiers/update.mdx +++ b/documentation/docs/concepts/decentralized_identifiers/update.mdx @@ -10,7 +10,7 @@ keywords: - Update - Publish --- -import account_manipulate_did_rs from '!!raw-loader!../../../../examples/account/manipulate_did.rs'; +import account_manipulate_did_rs from '!!raw-loader!../../../../examples_legacy/account/manipulate_did.rs'; import CodeSnippet from '../../../src/components/CodeSnippetComponent' import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; @@ -72,7 +72,7 @@ The following example demonstrates adding verification methods and services to a nodeReplitLink="https://repl.it/@IOTAFoundation/manipulate-did?lite=true" rustContent={account_manipulate_did_rs} nodeGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/bindings/wasm/examples-account/src/manipulate_did.ts" - rustGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/examples/account/manipulate_did.rs" + rustGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/examples_legacy/account/manipulate_did.rs" /> ### Creating Identity @@ -404,5 +404,5 @@ Furthermore and similar to deleting verification methods, services can be delete :::tip In this example, a message is published to the tangle every time the document is updated. These messages can be unnecessary. Instead, one message can be published that contains all the updates to the DID Document. -See the [lazy example for Rust](https://github.com/iotaledger/identity.rs/blob/dev/examples/account/lazy.rs) and [lazy example for JS](https://github.com/iotaledger/identity.rs/blob/dev/bindings/wasm/examples-account/src/lazy.ts) to learn more about lazy publishing. +See the [lazy example for Rust](https://github.com/iotaledger/identity.rs/blob/dev/examples_legacy/account/lazy.rs) and [lazy example for JS](https://github.com/iotaledger/identity.rs/blob/dev/bindings/wasm/examples-account/src/lazy.ts) to learn more about lazy publishing. ::: diff --git a/documentation/docs/concepts/verifiable_credentials/create.mdx b/documentation/docs/concepts/verifiable_credentials/create.mdx index d4577dec46..dba0368c3f 100644 --- a/documentation/docs/concepts/verifiable_credentials/create.mdx +++ b/documentation/docs/concepts/verifiable_credentials/create.mdx @@ -9,7 +9,7 @@ keywords: - Create - sign --- -import create_vc_rs from '!!raw-loader!../../../../examples/account/create_vc.rs'; +import create_vc_rs from '!!raw-loader!../../../../examples_legacy/account/create_vc.rs'; import CodeSnippet from '../../../src/components/CodeSnippetComponent' A [verifiable credential (VC)](./overview.md) can represent all information that a physical credential represents, such as a passport or university degree. However, by allowing other parties to cryptographically verify the authorship and integrity of the claims, verifiable credentials can be seen as more tamper-evident and more trustworthy than their physical counterparts. @@ -82,5 +82,5 @@ This Verifiable Credential can be [verified by anyone](./verifiable_presentation nodeReplitLink="https://repl.it/@IOTAFoundation/create-vc?lite=true" rustContent={create_vc_rs} nodeGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/bindings/wasm/examples-account/src/create_vc.ts" - rustGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/examples/account/create_vc.rs" + rustGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/examples_legacy/account/create_vc.rs" /> \ No newline at end of file diff --git a/documentation/docs/concepts/verifiable_credentials/revocation.mdx b/documentation/docs/concepts/verifiable_credentials/revocation.mdx index 70bab99914..0ec43bb7de 100644 --- a/documentation/docs/concepts/verifiable_credentials/revocation.mdx +++ b/documentation/docs/concepts/verifiable_credentials/revocation.mdx @@ -10,7 +10,7 @@ keywords: - revocation --- -import revoke_vc_rs from "!!raw-loader!../../../../examples/account/revoke_vc.rs"; +import revoke_vc_rs from "!!raw-loader!../../../../examples_legacy/account/revoke_vc.rs"; import CodeSnippet from "../../../src/components/CodeSnippetComponent"; ## Overview @@ -58,5 +58,5 @@ The following code exemplifies how you can revoke a [Verifiable Credential (VC)] nodeReplitLink="https://replit.com/@IOTAFoundation/revoke-vc-06?lite=true" rustContent={revoke_vc_rs} nodeGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/bindings/wasm/examples-account/src/revoke_vc.ts" - rustGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/examples/account/revoke_vc.rs" + rustGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/examples_legacy/account/revoke_vc.rs" /> diff --git a/documentation/docs/concepts/verifiable_credentials/verifiable_presentations.mdx b/documentation/docs/concepts/verifiable_credentials/verifiable_presentations.mdx index 4c1c9e4c5c..d2a1c3fbe7 100644 --- a/documentation/docs/concepts/verifiable_credentials/verifiable_presentations.mdx +++ b/documentation/docs/concepts/verifiable_credentials/verifiable_presentations.mdx @@ -7,7 +7,7 @@ keywords: - verifiable - presentations --- -import create_vp_rs from '!!raw-loader!../../../../examples/account/create_vp.rs'; +import create_vp_rs from '!!raw-loader!../../../../examples_legacy/account/create_vp.rs'; import CodeSnippet from '../../../src/components/CodeSnippetComponent' A verifiable presentation is the recommended data format for sharing one or more [verifiable credentials](./overview.md). @@ -169,5 +169,5 @@ serialize it to JSON for transmission, deserialize it on the receiving side as a nodeReplitLink="https://repl.it/@IOTAFoundation/create-vp?lite=true" rustContent={create_vp_rs} nodeGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/bindings/wasm/examples-account/src/create_vp.ts" - rustGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/examples/account/create_vp.rs" + rustGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/examples_legacy/account/create_vp.rs" /> diff --git a/documentation/docs/getting_started/create_and_publish.mdx b/documentation/docs/getting_started/create_and_publish.mdx index 3c3ec1601b..a8a3b8281b 100644 --- a/documentation/docs/getting_started/create_and_publish.mdx +++ b/documentation/docs/getting_started/create_and_publish.mdx @@ -11,7 +11,7 @@ keywords: - Publish --- import CodeSnippet from '../../src/components/CodeSnippetComponent' -import createDidRustExample from '!!raw-loader!../../../examples/account/create_did.rs'; +import createDidRustExample from '!!raw-loader!../../../examples_legacy/account/create_did.rs'; If you want to benefit from Self-Sovereign Identity, you need to create a [Decentralized Identity](../concepts/decentralized_identifiers/overview). This identity consists of many parts that have different functions. This page will cover the basics about identity creation and publishing to the Tangle. @@ -33,7 +33,7 @@ Select your programming language of choice and press the green play button to ex nodeReplitLink="https://repl.it/@IOTAFoundation/create-did?lite=true" rustContent={createDidRustExample} nodeGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/bindings/wasm/examples-account/src/create_did.ts" - rustGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/examples/account/create_did.rs" + rustGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/examples_legacy/account/create_did.rs" /> The first step in this example is the creation of an account. The account is a stateful object that manages one or more identities. The account provides an interface to execute high-level operations on identities, such as [creating](../concepts/decentralized_identifiers/create) and [updating](../concepts/decentralized_identifiers/update)) them. diff --git a/examples/0_basic/0_create_did.rs b/examples/0_basic/0_create_did.rs new file mode 100644 index 0000000000..7ecb79740a --- /dev/null +++ b/examples/0_basic/0_create_did.rs @@ -0,0 +1,64 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use anyhow::Context; +use examples::get_address_with_funds; +use examples::random_stronghold_path; +use examples::NETWORK_ENDPOINT; +use identity_core::convert::ToJson; +use identity_core::crypto::KeyPair; +use identity_core::crypto::KeyType; +use identity_did::verification::MethodScope; +use identity_stardust::NetworkName; +use identity_stardust::StardustClientExt; +use identity_stardust::StardustDocument; +use identity_stardust::StardustIdentityClientExt; +use identity_stardust::StardustVerificationMethod; +use iota_client::block::address::Address; +use iota_client::block::output::AliasOutput; +use iota_client::secret::stronghold::StrongholdSecretManager; +use iota_client::secret::SecretManager; +use iota_client::Client; + +/// Demonstrates how to create a DID Document and publish it in a new Alias Output. +#[tokio::main] +async fn main() -> anyhow::Result<()> { + // Create a new client to interact with the IOTA ledger. + let client: Client = Client::builder().with_primary_node(NETWORK_ENDPOINT, None)?.finish()?; + + // Create a new secret manager backed by a Stronghold. + let mut secret_manager: SecretManager = SecretManager::Stronghold( + StrongholdSecretManager::builder() + .password("secure_password") + .try_build(random_stronghold_path())?, + ); + + // Get an address and with funds for testing. + let address: Address = get_address_with_funds(&client, &mut secret_manager) + .await + .context("failed to get address with funds")?; + + // Get the Bech32 human-readable part (HRP) of the network. + let network_name: NetworkName = client.network_name().await?; + + // Create a new DID document with a placeholder DID. + // The DID will be derived from the Alias Id of the Alias Output after publishing. + let mut document: StardustDocument = StardustDocument::new(&network_name); + + // Insert a new Ed25519 verification method in the DID document. + let keypair: KeyPair = KeyPair::new(KeyType::Ed25519)?; + let method: StardustVerificationMethod = + StardustVerificationMethod::new(document.id().clone(), keypair.type_(), keypair.public(), "#key-1")?; + document.insert_method(method, MethodScope::VerificationMethod)?; + + // Construct an Alias Output containing the DID document, with the wallet address + // set as both the state controller and governor. + let alias_output: AliasOutput = client.new_did_output(address, document, None).await?; + println!("Alias Output: {}", alias_output.to_json()?); + + // Publish the Alias Output and get the published DID document. + let document: StardustDocument = client.publish_did_output(&secret_manager, alias_output).await?; + println!("Published DID document: {:#}", document); + + Ok(()) +} diff --git a/identity_stardust/examples/ex1_update_did.rs b/examples/0_basic/1_update_did.rs similarity index 69% rename from identity_stardust/examples/ex1_update_did.rs rename to examples/0_basic/1_update_did.rs index 77e44c6a05..d3253107f5 100644 --- a/identity_stardust/examples/ex1_update_did.rs +++ b/examples/0_basic/1_update_did.rs @@ -1,30 +1,43 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use examples::create_did; +use examples::random_stronghold_path; +use examples::NETWORK_ENDPOINT; use identity_core::common::Timestamp; use identity_core::convert::FromJson; use identity_core::json; use identity_did::did::DID; use identity_did::service::Service; use identity_did::verification::MethodRelationship; -use iota_client::block::output::AliasOutput; -use iota_client::block::output::AliasOutputBuilder; -use iota_client::secret::SecretManager; -use iota_client::Client; - +use identity_stardust::block::address::Address; +use identity_stardust::block::output::RentStructure; use identity_stardust::StardustClientExt; use identity_stardust::StardustDID; use identity_stardust::StardustDocument; use identity_stardust::StardustIdentityClientExt; use identity_stardust::StardustService; - -mod ex0_create_did; +use iota_client::block::output::AliasOutput; +use iota_client::block::output::AliasOutputBuilder; +use iota_client::secret::stronghold::StrongholdSecretManager; +use iota_client::secret::SecretManager; +use iota_client::Client; /// Demonstrates how to update a DID document in an existing Alias Output. #[tokio::main] async fn main() -> anyhow::Result<()> { + // Create a new client to interact with the IOTA ledger. + let client: Client = Client::builder().with_primary_node(NETWORK_ENDPOINT, None)?.finish()?; + + // Create a new secret manager backed by a Stronghold. + let mut secret_manager: SecretManager = SecretManager::Stronghold( + StrongholdSecretManager::builder() + .password("secure_password") + .try_build(random_stronghold_path())?, + ); + // Create a new DID in an Alias Output for us to modify. - let (client, _, secret_manager, did): (Client, _, SecretManager, StardustDID) = ex0_create_did::run().await?; + let (_, did): (Address, StardustDID) = create_did(&client, &mut secret_manager).await?; // Resolve the latest state of the document. let mut document: StardustDocument = client.resolve_did(&did).await?; @@ -49,8 +62,8 @@ async fn main() -> anyhow::Result<()> { // Because the size of the DID document increased, we have to increase the allocated storage deposit. // This increases the deposit amount to the new minimum. - let rent_structure = client.get_rent_structure().await?; - let alias_output = AliasOutputBuilder::from(&alias_output) + let rent_structure: RentStructure = client.get_rent_structure().await?; + let alias_output: AliasOutput = AliasOutputBuilder::from(&alias_output) .with_minimum_storage_deposit(rent_structure) .finish()?; diff --git a/identity_stardust/examples/ex2_resolve_did.rs b/examples/0_basic/2_resolve_did.rs similarity index 50% rename from identity_stardust/examples/ex2_resolve_did.rs rename to examples/0_basic/2_resolve_did.rs index fdfedfdb73..e3b9cca41d 100644 --- a/identity_stardust/examples/ex2_resolve_did.rs +++ b/examples/0_basic/2_resolve_did.rs @@ -1,19 +1,33 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use iota_client::block::output::AliasOutput; -use iota_client::Client; - +use examples::create_did; +use examples::random_stronghold_path; +use examples::NETWORK_ENDPOINT; +use identity_stardust::block::address::Address; use identity_stardust::StardustDID; use identity_stardust::StardustDocument; use identity_stardust::StardustIdentityClientExt; - -mod ex0_create_did; +use iota_client::block::output::AliasOutput; +use iota_client::secret::stronghold::StrongholdSecretManager; +use iota_client::secret::SecretManager; +use iota_client::Client; /// Demonstrates how to resolve an existing DID in an Alias Output. #[tokio::main] async fn main() -> anyhow::Result<()> { - let (client, _, _, did): (Client, _, _, StardustDID) = ex0_create_did::run().await?; + // Create a new client to interact with the IOTA ledger. + let client: Client = Client::builder().with_primary_node(NETWORK_ENDPOINT, None)?.finish()?; + + // Create a new secret manager backed by a Stronghold. + let mut secret_manager: SecretManager = SecretManager::Stronghold( + StrongholdSecretManager::builder() + .password("secure_password") + .try_build(random_stronghold_path())?, + ); + + // Create a new DID in an Alias Output for us to modify. + let (_, did): (Address, StardustDID) = create_did(&client, &mut secret_manager).await?; // Resolve the associated Alias Output and extract the DID document from it. let resolved: StardustDocument = client.resolve_did(&did).await?; @@ -21,6 +35,7 @@ async fn main() -> anyhow::Result<()> { // We can also resolve the Alias Output directly. let alias_output: AliasOutput = client.resolve_did_output(&did).await?; + println!("The Alias Output holds {} tokens", alias_output.amount()); Ok(()) diff --git a/identity_stardust/examples/ex3_deactivate_did.rs b/examples/0_basic/3_deactivate_did.rs similarity index 78% rename from identity_stardust/examples/ex3_deactivate_did.rs rename to examples/0_basic/3_deactivate_did.rs index c9634804f5..b3b1ae2a8d 100644 --- a/identity_stardust/examples/ex3_deactivate_did.rs +++ b/examples/0_basic/3_deactivate_did.rs @@ -1,24 +1,35 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use iota_client::block::address::Address; -use iota_client::block::output::AliasOutput; -use iota_client::block::output::AliasOutputBuilder; -use iota_client::secret::SecretManager; -use iota_client::Client; - +use examples::create_did; +use examples::random_stronghold_path; +use examples::NETWORK_ENDPOINT; +use identity_stardust::block::address::Address; use identity_stardust::StardustClientExt; use identity_stardust::StardustDID; use identity_stardust::StardustDocument; use identity_stardust::StardustIdentityClientExt; - -mod ex0_create_did; +use iota_client::block::output::AliasOutput; +use iota_client::block::output::AliasOutputBuilder; +use iota_client::secret::stronghold::StrongholdSecretManager; +use iota_client::secret::SecretManager; +use iota_client::Client; /// Demonstrates how to deactivate a DID in an Alias Output. #[tokio::main] async fn main() -> anyhow::Result<()> { + // Create a new client to interact with the IOTA ledger. + let client: Client = Client::builder().with_primary_node(NETWORK_ENDPOINT, None)?.finish()?; + + // Create a new secret manager backed by a Stronghold. + let mut secret_manager: SecretManager = SecretManager::Stronghold( + StrongholdSecretManager::builder() + .password("secure_password") + .try_build(random_stronghold_path())?, + ); + // Create a new DID in an Alias Output for us to modify. - let (client, _, secret_manager, did): (Client, Address, SecretManager, StardustDID) = ex0_create_did::run().await?; + let (_, did): (Address, StardustDID) = create_did(&client, &mut secret_manager).await?; // Resolve the latest state of the DID document, so we can reactivate it later. let document: StardustDocument = client.resolve_did(&did).await?; diff --git a/identity_stardust/examples/ex4_delete_did.rs b/examples/0_basic/4_delete_did.rs similarity index 62% rename from identity_stardust/examples/ex4_delete_did.rs rename to examples/0_basic/4_delete_did.rs index 458de0b995..4312c4943c 100644 --- a/identity_stardust/examples/ex4_delete_did.rs +++ b/examples/0_basic/4_delete_did.rs @@ -1,23 +1,33 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use iota_client::block::address::Address; -use iota_client::secret::SecretManager; -use iota_client::Client; - +use examples::create_did; +use examples::random_stronghold_path; +use examples::NETWORK_ENDPOINT; use identity_stardust::Error; use identity_stardust::StardustClientExt; use identity_stardust::StardustDID; use identity_stardust::StardustIdentityClientExt; - -mod ex0_create_did; +use iota_client::block::address::Address; +use iota_client::secret::stronghold::StrongholdSecretManager; +use iota_client::secret::SecretManager; +use iota_client::Client; /// Demonstrates how to delete a DID in an Alias Output, reclaiming the storage deposit. #[tokio::main] async fn main() -> anyhow::Result<()> { + // Create a new client to interact with the IOTA ledger. + let client: Client = Client::builder().with_primary_node(NETWORK_ENDPOINT, None)?.finish()?; + + // Create a new secret manager backed by a Stronghold. + let mut secret_manager: SecretManager = SecretManager::Stronghold( + StrongholdSecretManager::builder() + .password("secure_password") + .try_build(random_stronghold_path())?, + ); + // Create a new DID in an Alias Output for us to modify. - let (client, address, secret_manager, did): (Client, Address, SecretManager, StardustDID) = - ex0_create_did::run().await?; + let (address, did): (Address, StardustDID) = create_did(&client, &mut secret_manager).await?; // Deletes the Alias Output and its contained DID Document, rendering the DID permanently destroyed. // This operation is *not* reversible. diff --git a/examples/1_advanced/0_did_controls_did.rs b/examples/1_advanced/0_did_controls_did.rs new file mode 100644 index 0000000000..948341c21d --- /dev/null +++ b/examples/1_advanced/0_did_controls_did.rs @@ -0,0 +1,143 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::ops::Deref; + +use examples::create_did; +use examples::random_stronghold_path; +use examples::NETWORK_ENDPOINT; +use identity_core::crypto::KeyPair; +use identity_core::crypto::KeyType; +use identity_did::verification::MethodScope; +use identity_stardust::block::output::AliasId; +use identity_stardust::block::output::UnlockCondition; +use identity_stardust::NetworkName; +use identity_stardust::StardustClientExt; +use identity_stardust::StardustDID; +use identity_stardust::StardustDocument; +use identity_stardust::StardustIdentityClientExt; +use identity_stardust::StardustVerificationMethod; +use iota_client::block::address::Address; +use iota_client::block::address::AliasAddress; +use iota_client::block::output::feature::IssuerFeature; +use iota_client::block::output::AliasOutput; +use iota_client::block::output::AliasOutputBuilder; +use iota_client::block::output::RentStructure; +use iota_client::secret::stronghold::StrongholdSecretManager; +use iota_client::secret::SecretManager; +use iota_client::Client; + +/// Demonstrates how an identity can control another identity. +/// +/// For this example, we consider the case where a parent company's DID controls the DID of a subsidiary. +#[tokio::main] +async fn main() -> anyhow::Result<()> { + // ======================================================== + // Create the company's and subsidiary's Alias Output DIDs. + // ======================================================== + + // Create a new client to interact with the IOTA ledger. + let client: Client = Client::builder().with_primary_node(NETWORK_ENDPOINT, None)?.finish()?; + + // Create a new secret manager backed by a Stronghold. + let mut secret_manager: SecretManager = SecretManager::Stronghold( + StrongholdSecretManager::builder() + .password("secure_password") + .try_build(random_stronghold_path())?, + ); + + // Create a new DID for the company. + let (_, company_did): (Address, StardustDID) = create_did(&client, &mut secret_manager).await?; + + // Get the current byte costs and network name. + let rent_structure: RentStructure = client.get_rent_structure().await?; + let network_name: NetworkName = client.network_name().await?; + + // Construct a new DID document for the subsidiary. + let subsidiary_document: StardustDocument = StardustDocument::new(&network_name); + + // Create a DID for the subsidiary that is controlled by the parent company's DID. + // This means the subsidiary's Alias Output can only be updated or destroyed by + // the state controller or governor of the company's Alias Output respectively. + let subsidiary_alias: AliasOutput = client + .new_did_output( + Address::Alias(AliasAddress::new(AliasId::from(&company_did))), + subsidiary_document, + Some(rent_structure.clone()), + ) + .await?; + + let subsidiary_alias: AliasOutput = AliasOutputBuilder::from(&subsidiary_alias) + // Optionally, we can mark the company as the issuer of the subsidiary DID. + // This allows to verify trust relationships between DIDs, as a resolver can + // verify that the subsidiary DID was created by the parent company. + .add_immutable_feature(IssuerFeature::new(AliasAddress::new(AliasId::from(&company_did)).into()).into()) + // Adding the issuer feature means we have to recalculate the required storage deposit. + .with_minimum_storage_deposit(rent_structure.clone()) + .finish()?; + + // Publish the subsidiary's DID. + let mut subsidiary_document: StardustDocument = client.publish_did_output(&secret_manager, subsidiary_alias).await?; + + // ===================================== + // Update the subsidiary's Alias Output. + // ===================================== + + // Add a verification method to the subsidiary. + // This only serves as an example for updating the subsidiary DID. + let keypair: KeyPair = KeyPair::new(KeyType::Ed25519)?; + let method: StardustVerificationMethod = StardustVerificationMethod::new( + subsidiary_document.id().clone(), + keypair.type_(), + keypair.public(), + "#key-2", + )?; + subsidiary_document.insert_method(method, MethodScope::VerificationMethod)?; + + // Update the subsidiary's Alias Output with the updated document + // and increase the storage deposit. + let subsidiary_alias: AliasOutput = client.update_did_output(subsidiary_document).await?; + let subsidiary_alias: AliasOutput = AliasOutputBuilder::from(&subsidiary_alias) + .with_minimum_storage_deposit(rent_structure.clone()) + .finish()?; + + // Publish the updated subsidiary's DID. + // + // This works because `secret_manager` can unlock the company's Alias Output, + // which is required in order to update the subsidiary's Alias Output. + let subsidiary_document: StardustDocument = client.publish_did_output(&secret_manager, subsidiary_alias).await?; + + // =================================================================== + // Determine the controlling company's DID given the subsidiary's DID. + // =================================================================== + + // Resolve the subsidiary's Alias Output. + let subsidiary_output: AliasOutput = client.resolve_did_output(subsidiary_document.id()).await?; + + // Extract the company's Alias Id from the state controller unlock condition. + // + // If instead we wanted to determine the original creator of the DID, + // we could inspect the issuer feature. This feature needs to be set when creating the DID. + let company_alias_id: AliasId = if let Some(UnlockCondition::StateControllerAddress(address)) = + subsidiary_output.unlock_conditions().iter().next() + { + if let Address::Alias(alias) = *address.address() { + *alias.alias_id() + } else { + anyhow::bail!("expected an alias address as the state controller"); + } + } else { + anyhow::bail!("expected two unlock conditions"); + }; + + // Reconstruct the company's DID from the Alias Id and the network. + let company_did = StardustDID::new(company_alias_id.deref(), &network_name); + + // Resolve the company's DID document. + let company_document: StardustDocument = client.resolve_did(&company_did).await?; + + println!("Company: {company_document:#}"); + println!("Subsidiary: {subsidiary_document:#}"); + + Ok(()) +} diff --git a/examples/1_advanced/1_did_issues_nft.rs b/examples/1_advanced/1_did_issues_nft.rs new file mode 100644 index 0000000000..b06b6991c7 --- /dev/null +++ b/examples/1_advanced/1_did_issues_nft.rs @@ -0,0 +1,147 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use examples::create_did; +use examples::random_stronghold_path; +use examples::NETWORK_ENDPOINT; +use identity_stardust::block::output::feature::MetadataFeature; +use identity_stardust::NetworkName; +use identity_stardust::StardustDID; +use identity_stardust::StardustDocument; +use identity_stardust::StardustIdentityClientExt; +use iota_client::api_types::responses::OutputResponse; +use iota_client::block::address::Address; +use iota_client::block::address::AliasAddress; +use iota_client::block::output::feature::IssuerFeature; +use iota_client::block::output::unlock_condition::AddressUnlockCondition; +use iota_client::block::output::AliasId; +use iota_client::block::output::Feature; +use iota_client::block::output::NftId; +use iota_client::block::output::NftOutput; +use iota_client::block::output::NftOutputBuilder; +use iota_client::block::output::Output; +use iota_client::block::output::OutputId; +use iota_client::block::output::RentStructure; +use iota_client::block::output::UnlockCondition; +use iota_client::block::payload::transaction::TransactionEssence; +use iota_client::block::payload::Payload; +use iota_client::block::Block; +use iota_client::secret::stronghold::StrongholdSecretManager; +use iota_client::secret::SecretManager; +use iota_client::Client; + +/// Demonstrates how an identity can issue and own NFTs, +/// and how observers can verify the issuer of the NFT. +/// +/// For this example, we consider the case where a manufacturer issues +/// a digital product passport (DPP) as an NFT. +#[tokio::main] +async fn main() -> anyhow::Result<()> { + // ============================================== + // Create the manufacturer's DID and the DPP NFT. + // ============================================== + + // Create a new client to interact with the IOTA ledger. + let client: Client = Client::builder().with_primary_node(NETWORK_ENDPOINT, None)?.finish()?; + + // Create a new secret manager backed by a Stronghold. + let mut secret_manager: SecretManager = SecretManager::Stronghold( + StrongholdSecretManager::builder() + .password("secure_password") + .try_build(random_stronghold_path())?, + ); + + // Create a new DID for the manufacturer. + let (_, manufacturer_did): (Address, StardustDID) = create_did(&client, &mut secret_manager).await?; + + // Get the current byte cost. + let rent_structure: RentStructure = client.get_rent_structure().await?; + + // Create a Digital Product Passport NFT issued by the manufacturer. + let product_passport_nft: NftOutput = + NftOutputBuilder::new_with_minimum_storage_deposit(rent_structure, NftId::null())? + // The NFT will initially be owned by the manufacturer. + .add_unlock_condition(UnlockCondition::Address(AddressUnlockCondition::new(Address::Alias( + AliasAddress::new(AliasId::from(&manufacturer_did)), + )))) + // Set the manufacturer as the immutable issuer. + .add_immutable_feature(Feature::Issuer(IssuerFeature::new(Address::Alias(AliasAddress::new( + AliasId::from(&manufacturer_did), + ))))) + // A proper DPP would hold its metadata here. + .add_immutable_feature(Feature::Metadata(MetadataFeature::new( + b"Digital Product Passport Metadata".to_vec(), + )?)) + .finish()?; + + // Publish the NFT. + let block: Block = client + .block() + .with_secret_manager(&secret_manager) + .with_outputs(vec![product_passport_nft.into()])? + .finish() + .await?; + let _ = client.retry_until_included(&block.id(), None, None).await?; + + // ======================================================== + // Resolve the Digital Product Passport NFT and its issuer. + // ======================================================== + + // Extract the identifier of the NFT from the published block. + let nft_id: NftId = NftId::from(get_nft_output_id( + block + .payload() + .ok_or_else(|| anyhow::anyhow!("expected block to contain a payload"))?, + )?); + + // Fetch the NFT Output. + let nft_output_id: OutputId = client.nft_output_id(nft_id).await?; + let output_response: OutputResponse = client.get_output(&nft_output_id).await?; + let output: Output = Output::try_from(&output_response.output)?; + + // Extract the issuer of the NFT. + let nft_output: NftOutput = if let Output::Nft(nft_output) = output { + nft_output + } else { + anyhow::bail!("expected NFT output") + }; + + let issuer_address: Address = if let Some(Feature::Issuer(issuer)) = nft_output.immutable_features().iter().next() { + *issuer.address() + } else { + anyhow::bail!("expected an issuer feature") + }; + + let manufacturer_alias_id: AliasId = if let Address::Alias(alias_address) = issuer_address { + *alias_address.alias_id() + } else { + anyhow::bail!("expected an Alias Address") + }; + + // Reconstruct the manufacturer's DID from the Alias Id. + let network: NetworkName = client.network_name().await?; + let manufacturer_did: StardustDID = StardustDID::new(&*manufacturer_alias_id, &network); + + // Resolve the issuer of the NFT. + let manufacturer_document: StardustDocument = client.resolve_did(&manufacturer_did).await?; + + println!("The issuer of the Digital Product Passport NFT is: {manufacturer_document:#}"); + + Ok(()) +} + +// Helper function to get the output id for the first NFT output in a Block. +fn get_nft_output_id(payload: &Payload) -> anyhow::Result { + match payload { + Payload::Transaction(tx_payload) => { + let TransactionEssence::Regular(regular) = tx_payload.essence(); + for (index, output) in regular.outputs().iter().enumerate() { + if let Output::Nft(_nft_output) = output { + return Ok(OutputId::new(tx_payload.id(), index.try_into().unwrap())?); + } + } + anyhow::bail!("no NFT output in transaction essence") + } + _ => anyhow::bail!("No transaction payload"), + } +} diff --git a/examples/1_advanced/2_nft_owns_did.rs b/examples/1_advanced/2_nft_owns_did.rs new file mode 100644 index 0000000000..d90f25f6fd --- /dev/null +++ b/examples/1_advanced/2_nft_owns_did.rs @@ -0,0 +1,146 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use examples::create_did_document; +use examples::get_address_with_funds; +use examples::random_stronghold_path; +use examples::NETWORK_ENDPOINT; +use identity_stardust::block::address::NftAddress; +use identity_stardust::block::output::AliasOutput; +use identity_stardust::NetworkName; +use identity_stardust::StardustClientExt; +use identity_stardust::StardustDocument; +use identity_stardust::StardustIdentityClientExt; +use iota_client::api_types::responses::OutputResponse; +use iota_client::block::address::Address; +use iota_client::block::output::unlock_condition::AddressUnlockCondition; +use iota_client::block::output::NftId; +use iota_client::block::output::NftOutput; +use iota_client::block::output::NftOutputBuilder; +use iota_client::block::output::Output; +use iota_client::block::output::OutputId; +use iota_client::block::output::RentStructure; +use iota_client::block::output::UnlockCondition; +use iota_client::block::payload::transaction::TransactionEssence; +use iota_client::block::payload::Payload; +use iota_client::block::Block; +use iota_client::secret::stronghold::StrongholdSecretManager; +use iota_client::secret::SecretManager; +use iota_client::Client; + +/// Demonstrates how an identity can be owned by NFTs, +/// and how observers can verify that relationship. +/// +/// For this example, we consider the case where a car's NFT owns +/// the DID of the car, so that transferring the NFT also transfers DID ownership. +#[tokio::main] +async fn main() -> anyhow::Result<()> { + // ============================= + // Create the car's NFT and DID. + // ============================= + + // Create a new client to interact with the IOTA ledger. + let client: Client = Client::builder().with_primary_node(NETWORK_ENDPOINT, None)?.finish()?; + + // Create a new secret manager backed by a Stronghold. + let mut secret_manager: SecretManager = SecretManager::Stronghold( + StrongholdSecretManager::builder() + .password("secure_password") + .try_build(random_stronghold_path())?, + ); + + // Get an address with funds for testing. + let address: Address = get_address_with_funds(&client, &mut secret_manager).await?; + + // Get the current byte cost. + let rent_structure: RentStructure = client.get_rent_structure().await?; + + // Create the car NFT with an Ed25519 address as the unlock condition. + let car_nft: NftOutput = NftOutputBuilder::new_with_minimum_storage_deposit(rent_structure.clone(), NftId::null())? + .add_unlock_condition(UnlockCondition::Address(AddressUnlockCondition::new(address))) + .finish()?; + + // Publish the NFT output. + let block: Block = client + .block() + .with_secret_manager(&secret_manager) + .with_outputs(vec![car_nft.into()])? + .finish() + .await?; + let _ = client.retry_until_included(&block.id(), None, None).await?; + + let car_nft_id: NftId = NftId::from(get_nft_output_id( + block + .payload() + .ok_or_else(|| anyhow::anyhow!("expected the block to contain a payload"))?, + )?); + + let network: NetworkName = client.network_name().await?; + + // Construct a DID document for the subsidiary. + let document: StardustDocument = create_did_document(&network)?; + + // Create a new DID for the car that is owned by the car NFT. + let car_did_output: AliasOutput = client + .new_did_output(Address::Nft(car_nft_id.into()), document, Some(rent_structure)) + .await?; + + let car_document: StardustDocument = client.publish_did_output(&secret_manager, car_did_output).await?; + + // ============================================ + // Determine the car's NFT given the car's DID. + // ============================================ + + let output: AliasOutput = client.resolve_did_output(car_document.id()).await?; + + // Extract the NFT address from the state controller unlock condition. + let unlock_condition: &UnlockCondition = output + .unlock_conditions() + .iter() + .next() + .ok_or_else(|| anyhow::anyhow!("expected at least one unlock condition"))?; + + let car_nft_address: NftAddress = + if let UnlockCondition::StateControllerAddress(state_controller_unlock_condition) = unlock_condition { + if let Address::Nft(nft_address) = state_controller_unlock_condition.address() { + *nft_address + } else { + anyhow::bail!("expected an NFT address as the unlock condition"); + } + } else { + anyhow::bail!("expected an Address as the unlock condition"); + }; + + // Retrieve the NFT Output of the car. + let car_nft_id: &NftId = car_nft_address.nft_id(); + let output_id: OutputId = client.nft_output_id(*car_nft_id).await?; + let output_response: OutputResponse = client.get_output(&output_id).await?; + let output: Output = Output::try_from(&output_response.output)?; + + let car_nft: NftOutput = if let Output::Nft(nft_output) = output { + nft_output + } else { + anyhow::bail!("expected an NFT output"); + }; + + println!("The car's DID is: {car_document:#}"); + println!("The car's NFT is: {car_nft:#?}"); + + Ok(()) +} + +// Helper function to get the output id for the first NFT output in a Block. +fn get_nft_output_id(payload: &Payload) -> anyhow::Result { + match payload { + Payload::Transaction(tx_payload) => { + let TransactionEssence::Regular(regular) = tx_payload.essence(); + for (index, output) in regular.outputs().iter().enumerate() { + if let Output::Nft(_nft_output) = output { + return Ok(OutputId::new(tx_payload.id(), index.try_into().unwrap())?); + } + } + anyhow::bail!("no NFT output in transaction essence") + } + _ => anyhow::bail!("No transaction payload"), + } +} diff --git a/examples/1_advanced/3_did_issues_tokens.rs b/examples/1_advanced/3_did_issues_tokens.rs new file mode 100644 index 0000000000..7a91ef2427 --- /dev/null +++ b/examples/1_advanced/3_did_issues_tokens.rs @@ -0,0 +1,190 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::ops::Deref; + +use examples::create_did; +use examples::get_address; +use examples::random_stronghold_path; +use examples::NETWORK_ENDPOINT; +use identity_core::common::Duration; +use identity_core::common::Timestamp; +use identity_stardust::block::output::unlock_condition::AddressUnlockCondition; +use identity_stardust::block::output::unlock_condition::ExpirationUnlockCondition; +use identity_stardust::block::output::BasicOutput; +use identity_stardust::block::output::BasicOutputBuilder; +use identity_stardust::block::output::Output; +use identity_stardust::block::output::OutputId; +use identity_stardust::NetworkName; +use identity_stardust::StardustDID; +use identity_stardust::StardustDocument; +use identity_stardust::StardustIdentityClientExt; +use iota_client::api_types::responses::OutputResponse; +use iota_client::block::address::Address; +use iota_client::block::address::AliasAddress; +use iota_client::block::output::unlock_condition::ImmutableAliasAddressUnlockCondition; +use iota_client::block::output::AliasId; +use iota_client::block::output::AliasOutput; +use iota_client::block::output::AliasOutputBuilder; +use iota_client::block::output::FoundryId; +use iota_client::block::output::FoundryOutput; +use iota_client::block::output::FoundryOutputBuilder; +use iota_client::block::output::NativeToken; +use iota_client::block::output::RentStructure; +use iota_client::block::output::SimpleTokenScheme; +use iota_client::block::output::TokenId; +use iota_client::block::output::TokenScheme; +use iota_client::block::output::UnlockCondition; +use iota_client::block::Block; +use iota_client::secret::stronghold::StrongholdSecretManager; +use iota_client::secret::SecretManager; +use iota_client::Client; +use primitive_types::U256; + +/// Demonstrates how an identity can issue and control native assets +/// such as Token Foundries and NFTs. +/// +/// For this example, we consider the case where an authority issues +/// carbon credits that can be used to pay for carbon emissions or traded on a marketplace. +#[tokio::main] +async fn main() -> anyhow::Result<()> { + // =========================================== + // Create the authority's DID and the foundry. + // =========================================== + + // Create a new client to interact with the IOTA ledger. + let client: Client = Client::builder().with_primary_node(NETWORK_ENDPOINT, None)?.finish()?; + + // Create a new secret manager backed by a Stronghold. + let mut secret_manager: SecretManager = SecretManager::Stronghold( + StrongholdSecretManager::builder() + .password("secure_password") + .try_build(random_stronghold_path())?, + ); + + // Create a new DID for the authority. + + let (_, authority_did): (Address, StardustDID) = create_did(&client, &mut secret_manager).await?; + + let rent_structure: RentStructure = client.get_rent_structure().await?; + + // We want to update the foundry counter of the authority's Alias Output, so we create an + // updated version of the output. We pass in the previous document, + // because we don't want to modify it in this update. + let authority_document: StardustDocument = client.resolve_did(&authority_did).await?; + let authority_alias_output: AliasOutput = client.update_did_output(authority_document).await?; + + // We will add one foundry to this Alias Output. + let authority_alias_output = AliasOutputBuilder::from(&authority_alias_output) + .with_foundry_counter(1) + .finish()?; + + // Create a token foundry that represents carbon credits. + let token_scheme = TokenScheme::Simple(SimpleTokenScheme::new( + U256::from(500_000u32), + U256::from(0u8), + U256::from(1_000_000u32), + )?); + + // Create the identifier of the foundry, which is partially derived from the Alias Address. + let foundry_id = FoundryId::build( + &AliasAddress::new(AliasId::from(&authority_did)), + 1, + token_scheme.kind(), + ); + + // Create the Foundry Output. + let carbon_credits_foundry: FoundryOutput = + FoundryOutputBuilder::new_with_minimum_storage_deposit(rent_structure.clone(), 1, token_scheme)? + // Initially, all carbon credits are owned by the foundry. + .add_native_token(NativeToken::new(TokenId::from(foundry_id), U256::from(500_000u32))?) + // The authority is set as the immutable owner. + .add_unlock_condition(UnlockCondition::ImmutableAliasAddress( + ImmutableAliasAddressUnlockCondition::new(AliasAddress::new(AliasId::from(&authority_did))), + )) + .finish()?; + + let carbon_credits_foundry_id: FoundryId = carbon_credits_foundry.id(); + + // Publish all outputs. + let block: Block = client + .block() + .with_secret_manager(&secret_manager) + .with_outputs(vec![authority_alias_output.into(), carbon_credits_foundry.into()])? + .finish() + .await?; + let _ = client.retry_until_included(&block.id(), None, None).await?; + + // =================================== + // Resolve Foundry and its issuer DID. + // =================================== + + // Get the latest output that contains the foundry. + let foundry_output_id: OutputId = client.foundry_output_id(carbon_credits_foundry_id).await?; + let carbon_credits_foundry: OutputResponse = client.get_output(&foundry_output_id).await?; + let carbon_credits_foundry: Output = Output::try_from(&carbon_credits_foundry.output)?; + + let carbon_credits_foundry: FoundryOutput = if let Output::Foundry(foundry_output) = carbon_credits_foundry { + foundry_output + } else { + anyhow::bail!("expected foundry output") + }; + + // Get the Alias Id of the authority that issued the carbon credits foundry. + let authority_alias_id: &AliasId = carbon_credits_foundry.alias_address().alias_id(); + + // Reconstruct the DID of the authority. + let network: NetworkName = client.network_name().await?; + let authority_did: StardustDID = StardustDID::new(authority_alias_id.deref(), &network); + + // Resolve the authority's DID document. + let authority_document: StardustDocument = client.resolve_did(&authority_did).await?; + + println!("The authority's DID is: {authority_document:#}"); + + // ========================================================= + // Transfer 1000 carbon credits to the address of a company. + // ========================================================= + + // Create a new address that represents the company. + let company_address = get_address(&client, &mut secret_manager).await?; + + // Create the timestamp at which the basic output will expire. + let tomorrow: u32 = Timestamp::now_utc() + .checked_add(Duration::seconds(60 * 60 * 24)) + .ok_or_else(|| anyhow::anyhow!("timestamp overflow"))? + .to_unix() + .try_into() + .map_err(|err| anyhow::anyhow!("cannot fit timestamp into u32: {err}"))?; + + // Create a basic output containing our carbon credits that we'll send to the company's address. + let basic_output: BasicOutput = BasicOutputBuilder::new_with_minimum_storage_deposit(rent_structure)? + .add_unlock_condition(UnlockCondition::Address(AddressUnlockCondition::new(company_address))) + .add_native_token(NativeToken::new(carbon_credits_foundry.token_id(), U256::from(1000))?) + .add_unlock_condition(UnlockCondition::Expiration(ExpirationUnlockCondition::new( + Address::Alias(AliasAddress::new(*authority_alias_id)), + tomorrow, + )?)) + .finish()?; + + // Reduce the carbon credits in the foundry by the amount that is sent to the company. + let carbon_credits_foundry = FoundryOutputBuilder::from(&carbon_credits_foundry) + .with_native_tokens(vec![NativeToken::new( + carbon_credits_foundry.token_id(), + U256::from(499_000u32), + )?]) + .finish()?; + + // Publish the output, transferring the carbon credits. + let block: Block = client + .block() + .with_secret_manager(&secret_manager) + .with_outputs(vec![basic_output.into(), carbon_credits_foundry.into()])? + .finish() + .await?; + let _ = client.retry_until_included(&block.id(), None, None).await?; + + println!("Sent carbon credits to {}", company_address.to_bech32(network.as_ref())); + + Ok(()) +} diff --git a/examples/1_advanced/4_key_exchange.rs b/examples/1_advanced/4_key_exchange.rs new file mode 100644 index 0000000000..a3126a6ef5 --- /dev/null +++ b/examples/1_advanced/4_key_exchange.rs @@ -0,0 +1,133 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use anyhow::Context; +use examples::get_address_with_funds; +use examples::random_stronghold_path; +use examples::NETWORK_ENDPOINT; +use identity_core::crypto::KeyPair; +use identity_core::crypto::KeyType; +use identity_core::crypto::X25519; +use identity_did::verification::MethodScope; +use identity_stardust::block::address::Address; +use identity_stardust::block::output::AliasOutput; +use identity_stardust::block::output::RentStructure; +use identity_stardust::NetworkName; +use identity_stardust::StardustClientExt; +use identity_stardust::StardustDID; +use identity_stardust::StardustDocument; +use identity_stardust::StardustIdentityClientExt; +use identity_stardust::StardustVerificationMethod; +use iota_client::secret::stronghold::StrongholdSecretManager; +use iota_client::secret::SecretManager; +use iota_client::Client; + +/// Demonstrates Elliptic-curve Diffie-Hellman (ECDH) cryptographic key exchange with DID Documents. +/// +/// Alice and Bob want to communicate securely by encrypting their messages so only they +/// can read them. They both publish DID Documents with X25519 public keys and use them +/// to derive a shared secret key for encryption. +#[tokio::main] +async fn main() -> anyhow::Result<()> { + // ============================== + // Create DIDs for Alice and Bob. + // ============================== + + // Create a new client to interact with the IOTA ledger. + let client: Client = Client::builder().with_primary_node(NETWORK_ENDPOINT, None)?.finish()?; + + // Create a new secret manager backed by a Stronghold. + let mut secret_manager: SecretManager = SecretManager::Stronghold( + StrongholdSecretManager::builder() + .password("secure_password") + .try_build(random_stronghold_path())?, + ); + + // Get an address and with funds for testing. + let address: Address = get_address_with_funds(&client, &mut secret_manager) + .await + .context("failed to get address with funds")?; + + let network: NetworkName = client.network_name().await?; + let rent_structure: RentStructure = client.get_rent_structure().await?; + + // Alice creates and publishes their DID Document. + let (alice_did, alice_x25519): (StardustDID, KeyPair) = { + // Create a DID Document. + let mut alice_document: StardustDocument = StardustDocument::new(&network); + + // Insert a new X25519 KeyAgreement verification method. + let x25519: KeyPair = KeyPair::new(KeyType::X25519)?; + let method: StardustVerificationMethod = + StardustVerificationMethod::new(alice_document.id().clone(), KeyType::X25519, x25519.public(), "kex-0")?; + alice_document.insert_method(method, MethodScope::key_agreement())?; + + // Publish the DID. + let alice_output: AliasOutput = client + .new_did_output(address, alice_document, Some(rent_structure.clone())) + .await?; + let alice_document: StardustDocument = client.publish_did_output(&secret_manager, alice_output).await?; + + (alice_document.id().clone(), x25519) + }; + + // Bob creates and publishes their DID Document. + let (bob_did, bob_x25519): (StardustDID, KeyPair) = { + // Create a DID Document. + let mut bob_document: StardustDocument = StardustDocument::new(&network); + + // Insert a new X25519 KeyAgreement verification method. + let x25519: KeyPair = KeyPair::new(KeyType::X25519)?; + let method: StardustVerificationMethod = + StardustVerificationMethod::new(bob_document.id().clone(), KeyType::X25519, x25519.public(), "kex-0")?; + bob_document.insert_method(method, MethodScope::key_agreement())?; + + // Publish the DID. + let bob_output: AliasOutput = client + .new_did_output(address, bob_document, Some(rent_structure)) + .await?; + let bob_document: StardustDocument = client.publish_did_output(&secret_manager, bob_output).await?; + + (bob_document.id().clone(), x25519) + }; + + // ====================================================================== + // Alice and Bob tell each other their DIDs. They each resolve the + // DID Document of the other to obtain their X25519 public key. + // Note that in practice, they would run this code completely separately. + // ====================================================================== + + let alice_shared_secret_key: [u8; 32] = { + // Alice: resolves Bob's DID Document and extracts their public key. + let bob_document: StardustDocument = client.resolve_did(&bob_did).await?; + let bob_method: &StardustVerificationMethod = bob_document + .core_document() + .resolve_method("kex-0", Some(MethodScope::key_agreement())) + .unwrap(); + let bob_public_key: Vec = bob_method.data().try_decode()?; + + // Compute the shared secret. + X25519::key_exchange(alice_x25519.private(), &bob_public_key)? + }; + + let bob_shared_secret_key: [u8; 32] = { + // Bob: resolves Alice's DID Document and extracts their public key. + let alice_document: StardustDocument = client.resolve_did(&alice_did).await?; + let alice_method: &StardustVerificationMethod = alice_document + .core_document() + .resolve_method("kex-0", Some(MethodScope::key_agreement())) + .unwrap(); + let alice_public_key: Vec = alice_method.data().try_decode()?; + + // Compute the shared secret. + X25519::key_exchange(bob_x25519.private(), &alice_public_key)? + }; + + // Both shared secret keys computed separately by Alice and Bob will match + // and can then be used to establish encrypted communications. + assert_eq!(alice_shared_secret_key, bob_shared_secret_key); + + println!("Diffie-Hellman key exchange successful!"); + + Ok(()) +} diff --git a/examples/Cargo.toml b/examples/Cargo.toml index a1a2050f0d..26f3642972 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -1,85 +1,59 @@ [package] name = "examples" version = "0.6.0" +authors = ["IOTA Stiftung"] edition = "2021" publish = false -release = false [dependencies] -identity_iota = { path = "../identity_iota" } -pretty_env_logger = { version = "0.4" } -rand = { version = "0.8" } -tokio = { version = "1.17.0", features = ["full"] } +anyhow = "1.0.62" +identity_core = { path = "../identity_core" } +identity_did = { path = "../identity_did" } +identity_stardust = { path = "../identity_stardust" } +iota-client = { version = "2.0.0-beta.2", default-features = false, features = ["tls", "stronghold"] } +primitive-types = "0.11.1" +rand = "0.8.5" +tokio = { version = "1.20.1", default-features = false, features = ["rt"] } -[[example]] -name = "getting_started" -path = "getting_started.rs" - -[[example]] -name = "account_create" -path = "account/create_did.rs" - -[[example]] -name = "account_config" -path = "account/config.rs" - -[[example]] -name = "account_manipulate" -path = "account/manipulate_did.rs" - -[[example]] -name = "account_lazy" -path = "account/lazy.rs" - -[[example]] -name = "account_signing" -path = "account/signing.rs" - -# TODO: Temporarily disabled until iotaledger/stronghold.rs#353 is fixed. -# [[example]] -# name = "account_multiple" -# path = "account/multiple_identities.rs" - -[[example]] -name = "account_unchecked" -path = "account/unchecked.rs" +[lib] +path = "utils/utils.rs" [[example]] -name = "account_encryption" -path = "account/encryption.rs" +path = "0_basic/0_create_did.rs" +name = "0_create_did" [[example]] -name = "create_did" -path = "low-level-api/create_did.rs" +path = "0_basic/1_update_did.rs" +name = "1_update_did" [[example]] -name = "account_create_vc" -path = "account/create_vc.rs" +path = "0_basic/2_resolve_did.rs" +name = "2_resolve_did" [[example]] -name = "account_create_vp" -path = "account/create_vp.rs" +path = "0_basic/3_deactivate_did.rs" +name = "3_deactivate_did" [[example]] -name = "resolve_history" -path = "low-level-api/resolve_history.rs" +path = "0_basic/4_delete_did.rs" +name = "4_delete_did" [[example]] -name = "manipulate_did" -path = "low-level-api/manipulate_did.rs" +path = "1_advanced/0_did_controls_did.rs" +name = "0_did_controls_did" [[example]] -name = "key_exchange" -path = "low-level-api/key_exchange.rs" +path = "1_advanced/1_did_issues_nft.rs" +name = "1_did_issues_nft" [[example]] -name = "resolve_did" -path = "low-level-api/resolve_did.rs" +path = "1_advanced/2_nft_owns_did.rs" +name = "2_nft_owns_did" [[example]] -name = "private_tangle" -path = "low-level-api/private_tangle.rs" +path = "1_advanced/3_did_issues_tokens.rs" +name = "3_did_issues_tokens" [[example]] -name = "account_revoke_vc" -path = "account/revoke_vc.rs" +path = "1_advanced/4_key_exchange.rs" +name = "4_key_exchange" diff --git a/examples/README.md b/examples/README.md index 209c028188..8ed43604d1 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,10 +1,8 @@ -![banner](./../.meta/identity_banner.png) +![banner](./../documentation/static/img/Banner/banner_identity.svg) +# IOTA Identity Examples - -## IOTA Identity Examples - -This folder provides code examples for you to learn how IOTA Identity can be used. +This folder provides code examples to learn how IOTA Identity can be used. You can run each example using @@ -12,36 +10,32 @@ You can run each example using cargo run --example ``` -For Instance, to run the example `getting_started`, use +For instance, to run the example `0_create_did`, use: ```rust -cargo run --example getting_started +cargo run --example 0_create_did ``` -The following examples are available for using the basic account (A high-level API): - -| # | Name | Information | -| :--: | :----------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------- | -| 1 | [getting_started](./getting_started.rs) | Introductory example for you to test whether the library is set up / working properly and compiles. | -| 2 | [account_create](./account/create_did.rs) | A basic example that generates and publishes a DID Document, the fundamental building block for decentralized identity. | -| 3 | [account_config](./account/config.rs) | How to configure the account to work with different networks and other settings. | -| 4 | [account_manipulate](./account/manipulate_did.rs) | How to manipulate a DID Document by adding/removing Verification Methods and Services. | -| 5 | [account_lazy](./account/lazy.rs) | How to take control over publishing DID updates manually, instead of the default automated behavior. | -| 6 | [account_signing](./account/signing.rs) | Using a DID to sign arbitrary statements and validating them. | -| 7 | [account_create_vc](account/create_vc.rs) | Generates and publishes subject and issuer DID Documents, then creates a Verifiable Credential (VC) specifying claims about the subject, and retrieves information through the CredentialValidator API. | -| 8 | [account_create_vp](account/create_vp.rs) | This example explains how to create a Verifiable Presentation from a set of credentials and sign it. | -| 9 | [account_revoke_vc](account/revoke_vc.rs) | Removes a verification method from the Issuers DID Document, making the Verifiable Credential it signed unable to verify, effectively revoking the VC. | -| 10 | [account_multiple](./account/multiple_identities.rs) | How to create multiple identities from a builder and how to load existing identities into an account. | -| 11 | [account_unchecked](./account/unchecked.rs) | How to update the custom properties of a DID document directly by using the account's unchecked methods. | -| 12 | [account_encryption](./account/encryption.rs) | Demonstrates Elliptic-curve Diffie-Hellman (ECDH) cryptographic key exchange by encrypting and decrypting data with a shared key. | - -The following examples are available for using the low-level APIs, which provides more flexibility at the cost of complexity: - -| # | Name | Information | -| :--: | :----------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------- | -| 1 | [create_did](./low-level-api/create_did.rs) | A basic example that generates and publishes a DID Document, the fundamental building block for decentralized identity. | -| 2 | [manipulate_did](low-level-api/manipulate_did.rs) | This example demonstrates how to perform a basic update to the integration chain of a DID Document. | -| 3 | [resolve_history](low-level-api/resolve_history.rs) | Advanced example that performs multiple updates and demonstrates how to resolve the DID Document history to view them. | -| 4 | [resolve_did](./low-level-api/resolve_did.rs) | A basic example that shows how to retrieve information through DID Document resolution/dereferencing. | -| 5 | [key_exchange](./low-level-api/key_exchange.rs) | Demonstrates Elliptic-curve Diffie-Hellman (ECDH) cryptographic key exchange with DID Documents. | -| 6 | [private_tangle](./low-level-api/private_tangle.rs) | Showcases the same procedure as `create_did`, but on a private tangle - a locally running hornet node. | +## Basic Examples + +The following basic CRUD (Create, Read, Update, Delete) examples are available: + +| Name | Information | +| :------------------------------------------------ | :----------------------------------------------------------------------------------- | +| [0_create_did](./0_basic/0_create_did.rs) | Demonstrates how to create a DID Document and publish it in a new Alias Output. | +| [1_update_did](./0_basic/1_update_did.rs) | Demonstrates how to update a DID document in an existing Alias Output. | +| [2_resolve_did](./0_basic/2_resolve_did.rs) | Demonstrates how to resolve an existing DID in an Alias Output. | +| [3_deactivate_did](./0_basic/3_deactivate_did.rs) | Demonstrates how to deactivate a DID in an Alias Output. | +| [4_delete_did](./0_basic/4_delete_did.rs) | Demonstrates how to delete a DID in an Alias Output, reclaiming the storage deposit. | + +## Advanced Examples + +The following advanced examples are available: + +| Name | Information | +| :--------------------------------------------------------- | :------------------------------------------------------------------------------------------------------- | +| [0_did_controls_did](./1_advanced/0_did_controls_did.rs) | Demonstrates how an identity can control another identity. | +| [1_did_issues_nft](./1_advanced/1_did_issues_nft.rs) | Demonstrates how an identity can issue and own NFTs, and how observers can verify the issuer of the NFT. | +| [2_nft_owns_did](./1_advanced/2_nft_owns_did.rs) | Demonstrates how an identity can be owned by NFTs, and how observers can verify that relationship. | +| [3_did_issues_tokens](./1_advanced/3_did_issues_tokens.rs) | Demonstrates how an identity can issue and control native assets such as Token Foundries and NFTs. | +| [4_key_exchange](./1_advanced/4_key_exchange.rs) | Demonstrates Elliptic-curve Diffie-Hellman (ECDH) cryptographic key exchange with DID Documents. | diff --git a/identity_stardust/examples/ex0_create_did.rs b/examples/utils/utils.rs similarity index 50% rename from identity_stardust/examples/ex0_create_did.rs rename to examples/utils/utils.rs index 5495cc5e90..932a8b56bd 100644 --- a/identity_stardust/examples/ex0_create_did.rs +++ b/examples/utils/utils.rs @@ -1,80 +1,98 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 +use std::path::PathBuf; use anyhow::Context; -use identity_core::convert::ToJson; + use identity_core::crypto::KeyPair; use identity_core::crypto::KeyType; use identity_did::verification::MethodScope; +use identity_stardust::NetworkName; +use identity_stardust::StardustClientExt; +use identity_stardust::StardustDID; +use identity_stardust::StardustDocument; +use identity_stardust::StardustIdentityClientExt; +use identity_stardust::StardustVerificationMethod; + use iota_client::block::address::Address; use iota_client::block::output::AliasOutput; use iota_client::block::output::Output; use iota_client::crypto::keys::bip39; use iota_client::node_api::indexer::query_parameters::QueryParameter; -use iota_client::secret::mnemonic::MnemonicSecretManager; use iota_client::secret::SecretManager; use iota_client::Client; +use rand::distributions::DistString; -use identity_stardust::NetworkName; -use identity_stardust::StardustClientExt; -use identity_stardust::StardustDID; -use identity_stardust::StardustDocument; -use identity_stardust::StardustIdentityClient; -use identity_stardust::StardustIdentityClientExt; -use identity_stardust::StardustVerificationMethod; - -static ENDPOINT: &str = "https://api.testnet.shimmer.network/"; -static FAUCET_URL: &str = "https://faucet.testnet.shimmer.network/api/enqueue"; +pub static NETWORK_ENDPOINT: &str = "https://api.testnet.shimmer.network/"; +pub static FAUCET_URL: &str = "https://faucet.testnet.shimmer.network/api/enqueue"; -/// Demonstrates how to create a DID Document and publish it in a new Alias Output. -pub async fn run() -> anyhow::Result<(Client, Address, SecretManager, StardustDID)> { - // Create a client and a wallet address with funds from the testnet faucet. - let client: Client = Client::builder().with_primary_node(ENDPOINT, None)?.finish()?; - let (address, secret_manager): (Address, SecretManager) = get_address_with_funds(&client) +/// Creates a DID Document and publishes it in a new Alias Output. +/// +/// Its functionality is equivalent to the "create DID" example +/// and exists for convenient calling from the other examples. +pub async fn create_did(client: &Client, secret_manager: &mut SecretManager) -> anyhow::Result<(Address, StardustDID)> { + let address: Address = get_address_with_funds(client, secret_manager) .await .context("failed to get address with funds")?; - // Get the Bech32 human-readable part (HRP) of the network. let network_name: NetworkName = client.network_name().await?; - // Create a new DID document with a placeholder DID. - // The DID will be derived from the Alias Id of the Alias Output after publishing. - let mut document: StardustDocument = StardustDocument::new(&network_name); + let document: StardustDocument = create_did_document(&network_name)?; + + let alias_output: AliasOutput = client.new_did_output(address, document, None).await?; + + let document: StardustDocument = client.publish_did_output(secret_manager, alias_output).await?; + + Ok((address, document.id().clone())) +} + +/// Creates an example DID document with the given `network_name`. +/// +/// Its functionality is equivalent to the "create DID" example +/// and exists for convenient calling from the other examples. +pub fn create_did_document(network_name: &NetworkName) -> anyhow::Result { + let mut document: StardustDocument = StardustDocument::new(network_name); - // Insert a new Ed25519 verification method in the DID document. let keypair: KeyPair = KeyPair::new(KeyType::Ed25519)?; + let method: StardustVerificationMethod = StardustVerificationMethod::new(document.id().clone(), keypair.type_(), keypair.public(), "#key-1")?; + document.insert_method(method, MethodScope::VerificationMethod)?; - // Construct an Alias Output containing the DID document, with the wallet address - // set as both the state controller and governor. - let alias_output: AliasOutput = client.new_did_output(address, document, None).await?; - println!("Alias Output: {}", alias_output.to_json()?); + Ok(document) +} - // Publish the Alias Output and get the published DID document. - let document: StardustDocument = client.publish_did_output(&secret_manager, alias_output).await?; - println!("Published DID document: {:#}", document); +/// Generates an address from the given [`SecretManager`] and adds funds from the testnet faucet. +pub async fn get_address_with_funds(client: &Client, stronghold: &mut SecretManager) -> anyhow::Result

{ + let address: Address = get_address(client, stronghold).await?; - Ok((client, address, secret_manager, document.id().clone())) + request_faucet_funds(client, address, client.get_bech32_hrp().await?.as_str()) + .await + .context("failed to request faucet funds")?; + + Ok(address) } -/// Creates a new address and SecretManager with funds from the testnet faucet. -async fn get_address_with_funds(client: &Client) -> anyhow::Result<(Address, SecretManager)> { +/// Initializes the [`SecretManager`] with a new mnemonic, if necessary, +/// and generates an address from the given [`SecretManager`]. +pub async fn get_address(client: &Client, secret_manager: &mut SecretManager) -> anyhow::Result
{ let keypair = identity_core::crypto::KeyPair::new(KeyType::Ed25519)?; let mnemonic = iota_client::crypto::keys::bip39::wordlist::encode(keypair.private().as_ref(), &bip39::wordlist::ENGLISH) .map_err(|err| anyhow::anyhow!(format!("{err:?}")))?; - let secret_manager = SecretManager::Mnemonic(MnemonicSecretManager::try_from_mnemonic(&mnemonic)?); + if let SecretManager::Stronghold(ref mut stronghold) = secret_manager { + match stronghold.store_mnemonic(mnemonic).await { + Ok(()) => (), + Err(iota_client::Error::StrongholdMnemonicAlreadyStored) => (), + Err(err) => anyhow::bail!(err), + } + } else { + anyhow::bail!("expected a `StrongholdSecretManager`"); + } - let address = client.get_addresses(&secret_manager).with_range(0..1).get_raw().await?[0]; - let network_hrp = client.get_network_hrp().await?; - request_faucet_funds(client, address, &network_hrp) - .await - .context("failed to request faucet funds")?; + let address = client.get_addresses(secret_manager).with_range(0..1).get_raw().await?[0]; - Ok((address, secret_manager)) + Ok(address) } /// Requests funds from the testnet faucet for the given `address`. @@ -107,9 +125,9 @@ async fn get_address_balance(client: &Client, address: &str) -> anyhow::Result anyhow::Result anyhow::Result<()> { - run().await.map(|_| ()).map_err(|err| { - eprintln!("ex0_create_did error: {:#}", err); - err - }) +/// Creates a random stronghold path in the temporary directory, whose exact location is OS-dependent. +pub fn random_stronghold_path() -> PathBuf { + let mut file = std::env::temp_dir(); + file.push("test_strongholds"); + file.push(rand::distributions::Alphanumeric.sample_string(&mut rand::thread_rng(), 32)); + file.set_extension("stronghold"); + file.to_owned() } diff --git a/examples_legacy/Cargo.toml b/examples_legacy/Cargo.toml new file mode 100644 index 0000000000..07e7ab1acd --- /dev/null +++ b/examples_legacy/Cargo.toml @@ -0,0 +1,84 @@ +[package] +name = "examples_legacy" +version = "0.6.0" +edition = "2021" +publish = false + +[dependencies] +identity_iota = { path = "../identity_iota" } +pretty_env_logger = { version = "0.4" } +rand = { version = "0.8" } +tokio = { version = "1.17.0", features = ["full"] } + +[[example]] +name = "getting_started" +path = "getting_started.rs" + +[[example]] +name = "account_create" +path = "account/create_did.rs" + +[[example]] +name = "account_config" +path = "account/config.rs" + +[[example]] +name = "account_manipulate" +path = "account/manipulate_did.rs" + +[[example]] +name = "account_lazy" +path = "account/lazy.rs" + +[[example]] +name = "account_signing" +path = "account/signing.rs" + +# TODO: Temporarily disabled until iotaledger/stronghold.rs#353 is fixed. +# [[example]] +# name = "account_multiple" +# path = "account/multiple_identities.rs" + +[[example]] +name = "account_unchecked" +path = "account/unchecked.rs" + +[[example]] +name = "account_encryption" +path = "account/encryption.rs" + +[[example]] +name = "create_did" +path = "low-level-api/create_did.rs" + +[[example]] +name = "account_create_vc" +path = "account/create_vc.rs" + +[[example]] +name = "account_create_vp" +path = "account/create_vp.rs" + +[[example]] +name = "resolve_history" +path = "low-level-api/resolve_history.rs" + +[[example]] +name = "manipulate_did" +path = "low-level-api/manipulate_did.rs" + +[[example]] +name = "key_exchange" +path = "low-level-api/key_exchange.rs" + +[[example]] +name = "resolve_did" +path = "low-level-api/resolve_did.rs" + +[[example]] +name = "private_tangle" +path = "low-level-api/private_tangle.rs" + +[[example]] +name = "account_revoke_vc" +path = "account/revoke_vc.rs" diff --git a/examples_legacy/README.md b/examples_legacy/README.md new file mode 100644 index 0000000000..c0bd008e9c --- /dev/null +++ b/examples_legacy/README.md @@ -0,0 +1,47 @@ +![banner](./../documentation/static/img/Banner/banner_identity.svg) + + + +## IOTA Identity Examples + +This folder provides code examples for you to learn how IOTA Identity can be used. + +You can run each example using + +```rust +cargo run --example +``` + +For Instance, to run the example `getting_started`, use + +```rust +cargo run --example getting_started +``` + +The following examples are available for using the basic account (A high-level API): + +| # | Name | Information | +| :--: | :----------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------- | +| 1 | [getting_started](./getting_started.rs) | Introductory example for you to test whether the library is set up / working properly and compiles. | +| 2 | [account_create](./account/create_did.rs) | A basic example that generates and publishes a DID Document, the fundamental building block for decentralized identity. | +| 3 | [account_config](./account/config.rs) | How to configure the account to work with different networks and other settings. | +| 4 | [account_manipulate](./account/manipulate_did.rs) | How to manipulate a DID Document by adding/removing Verification Methods and Services. | +| 5 | [account_lazy](./account/lazy.rs) | How to take control over publishing DID updates manually, instead of the default automated behavior. | +| 6 | [account_signing](./account/signing.rs) | Using a DID to sign arbitrary statements and validating them. | +| 7 | [account_create_vc](account/create_vc.rs) | Generates and publishes subject and issuer DID Documents, then creates a Verifiable Credential (VC) specifying claims about the subject, and retrieves information through the CredentialValidator API. | +| 8 | [account_create_vp](account/create_vp.rs) | This example explains how to create a Verifiable Presentation from a set of credentials and sign it. | +| 9 | [account_revoke_vc](account/revoke_vc.rs) | Removes a verification method from the Issuers DID Document, making the Verifiable Credential it signed unable to verify, effectively revoking the VC. | +| 10 | [account_multiple](./account/multiple_identities.rs) | How to create multiple identities from a builder and how to load existing identities into an account. | +| 11 | [account_unchecked](./account/unchecked.rs) | How to update the custom properties of a DID document directly by using the account's unchecked methods. | +| 12 | [account_encryption](./account/encryption.rs) | Demonstrates Elliptic-curve Diffie-Hellman (ECDH) cryptographic key exchange by encrypting and decrypting data with a shared key. | + +The following examples are available for using the low-level APIs, which provides more flexibility at the cost of complexity: + +| # | Name | Information | +| :--: | :----------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------- | +| 1 | [create_did](./low-level-api/create_did.rs) | A basic example that generates and publishes a DID Document, the fundamental building block for decentralized identity. | +| 2 | [manipulate_did](low-level-api/manipulate_did.rs) | This example demonstrates how to perform a basic update to the integration chain of a DID Document. | +| 3 | [resolve_history](low-level-api/resolve_history.rs) | Advanced example that performs multiple updates and demonstrates how to resolve the DID Document history to view them. | +| 4 | [resolve_did](./low-level-api/resolve_did.rs) | A basic example that shows how to retrieve information through DID Document resolution/dereferencing. | +| 5 | [key_exchange](./low-level-api/key_exchange.rs) | Demonstrates Elliptic-curve Diffie-Hellman (ECDH) cryptographic key exchange with DID Documents. | +| 6 | [private_tangle](./low-level-api/private_tangle.rs) | Showcases the same procedure as `create_did`, but on a private tangle - a locally running hornet node. | diff --git a/examples/account/config.rs b/examples_legacy/account/config.rs similarity index 100% rename from examples/account/config.rs rename to examples_legacy/account/config.rs diff --git a/examples/account/create_did.rs b/examples_legacy/account/create_did.rs similarity index 100% rename from examples/account/create_did.rs rename to examples_legacy/account/create_did.rs diff --git a/examples/account/create_vc.rs b/examples_legacy/account/create_vc.rs similarity index 100% rename from examples/account/create_vc.rs rename to examples_legacy/account/create_vc.rs diff --git a/examples/account/create_vp.rs b/examples_legacy/account/create_vp.rs similarity index 100% rename from examples/account/create_vp.rs rename to examples_legacy/account/create_vp.rs diff --git a/examples/account/encryption.rs b/examples_legacy/account/encryption.rs similarity index 100% rename from examples/account/encryption.rs rename to examples_legacy/account/encryption.rs diff --git a/examples/account/lazy.rs b/examples_legacy/account/lazy.rs similarity index 100% rename from examples/account/lazy.rs rename to examples_legacy/account/lazy.rs diff --git a/examples/account/manipulate_did.rs b/examples_legacy/account/manipulate_did.rs similarity index 100% rename from examples/account/manipulate_did.rs rename to examples_legacy/account/manipulate_did.rs diff --git a/examples/account/multiple_identities.rs b/examples_legacy/account/multiple_identities.rs similarity index 100% rename from examples/account/multiple_identities.rs rename to examples_legacy/account/multiple_identities.rs diff --git a/examples/account/revoke_vc.rs b/examples_legacy/account/revoke_vc.rs similarity index 100% rename from examples/account/revoke_vc.rs rename to examples_legacy/account/revoke_vc.rs diff --git a/examples/account/signing.rs b/examples_legacy/account/signing.rs similarity index 100% rename from examples/account/signing.rs rename to examples_legacy/account/signing.rs diff --git a/examples/account/unchecked.rs b/examples_legacy/account/unchecked.rs similarity index 100% rename from examples/account/unchecked.rs rename to examples_legacy/account/unchecked.rs diff --git a/examples/getting_started.rs b/examples_legacy/getting_started.rs similarity index 100% rename from examples/getting_started.rs rename to examples_legacy/getting_started.rs diff --git a/examples/low-level-api/common.rs b/examples_legacy/low-level-api/common.rs similarity index 100% rename from examples/low-level-api/common.rs rename to examples_legacy/low-level-api/common.rs diff --git a/examples/low-level-api/create_did.rs b/examples_legacy/low-level-api/create_did.rs similarity index 100% rename from examples/low-level-api/create_did.rs rename to examples_legacy/low-level-api/create_did.rs diff --git a/examples/low-level-api/key_exchange.rs b/examples_legacy/low-level-api/key_exchange.rs similarity index 100% rename from examples/low-level-api/key_exchange.rs rename to examples_legacy/low-level-api/key_exchange.rs diff --git a/examples/low-level-api/manipulate_did.rs b/examples_legacy/low-level-api/manipulate_did.rs similarity index 100% rename from examples/low-level-api/manipulate_did.rs rename to examples_legacy/low-level-api/manipulate_did.rs diff --git a/examples/low-level-api/private_tangle.rs b/examples_legacy/low-level-api/private_tangle.rs similarity index 100% rename from examples/low-level-api/private_tangle.rs rename to examples_legacy/low-level-api/private_tangle.rs diff --git a/examples/low-level-api/resolve_did.rs b/examples_legacy/low-level-api/resolve_did.rs similarity index 100% rename from examples/low-level-api/resolve_did.rs rename to examples_legacy/low-level-api/resolve_did.rs diff --git a/examples/low-level-api/resolve_history.rs b/examples_legacy/low-level-api/resolve_history.rs similarity index 100% rename from examples/low-level-api/resolve_history.rs rename to examples_legacy/low-level-api/resolve_history.rs diff --git a/identity_iota/README.md b/identity_iota/README.md index 4fd9e6422d..00ad3729f3 100644 --- a/identity_iota/README.md +++ b/identity_iota/README.md @@ -1,4 +1,4 @@ -![banner](https://github.com/iotaledger/identity.rs/raw/HEAD/.meta/identity_banner.png) +![banner](https://github.com/iotaledger/identity.rs/raw/HEAD/documentation/static/img/Banner/banner_identity.svg)

StackExchange diff --git a/identity_stardust/Cargo.toml b/identity_stardust/Cargo.toml index 371a11f59c..b94ff8faf9 100644 --- a/identity_stardust/Cargo.toml +++ b/identity_stardust/Cargo.toml @@ -10,16 +10,16 @@ readme = "../README.md" repository = "https://github.com/iotaledger/identity.rs" rust-version = "1.62" description = "An IOTA Ledger integration for the identity.rs library." -[workspace] [dependencies] # Ensure bee-block always matches the version used by iota-client. -bee-block = { version = "1.0.0-beta.5", default-features = false, features = ["std"], optional = true } +bee-block = { version = "1.0.0-beta.6", default-features = false, features = ["std"], optional = true } identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } identity_credential = { version = "=0.6.0", path = "../identity_credential", default-features = false } identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } async-trait = { version = "0.1.56", default-features = false, optional = true } +iota-client = { version = "2.0.0-beta.2", default-features = false, features = ["tls"], optional = true } num-derive = { version = "0.3", default-features = false } num-traits = { version = "0.2", default-features = false, features = ["std"] } once_cell = { version = "1", default-features = false, features = ["std"] } @@ -28,13 +28,6 @@ serde = { version = "1.0", default-features = false, features = ["std", "derive" strum = { version = "0.21", features = ["derive"] } thiserror = { version = "1.0", default-features = false } -[dependencies.iota-client] -git = "https://github.com/iotaledger/iota.rs" -rev = "a582bfa882793fe21db2055c4f7878ebc531877a" # develop branch, 2022-07-27 -features = ["tls"] -default-features = false -optional = true - [dev-dependencies] anyhow = { version = "1.0.57" } iota-crypto = { version = "0.12.1", default-features = false, features = ["bip39", "bip39-en"] } diff --git a/identity_stardust/src/client/identity_client.rs b/identity_stardust/src/client/identity_client.rs index eea778cccb..cbed527097 100644 --- a/identity_stardust/src/client/identity_client.rs +++ b/identity_stardust/src/client/identity_client.rs @@ -1,8 +1,6 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use identity_did::did::DIDError; - use crate::block::address::Address; use crate::block::output::feature::SenderFeature; use crate::block::output::unlock_condition::GovernorAddressUnlockCondition; @@ -20,16 +18,6 @@ use crate::Result; use crate::StardustDID; use crate::StardustDocument; -impl TryFrom<&StardustDID> for AliasId { - type Error = Error; - - fn try_from(did: &StardustDID) -> std::result::Result { - let tag_bytes: [u8; StardustDID::TAG_BYTES_LEN] = - prefix_hex::decode(did.tag()).map_err(|_| DIDError::InvalidMethodId)?; - Ok(AliasId::new(tag_bytes)) - } -} - /// Helper functions necessary for the [`StardustIdentityClientExt`] trait. #[async_trait::async_trait(? Send)] pub trait StardustIdentityClient { @@ -103,7 +91,7 @@ pub trait StardustIdentityClientExt: StardustIdentityClient { /// /// Returns `Err` when failing to resolve the DID contained in `document`. async fn update_did_output(&self, document: StardustDocument) -> Result { - let id: AliasId = AliasId::try_from(document.id())?; + let id: AliasId = AliasId::from(document.id()); let (_, alias_output) = self.get_alias_output(id).await?; let mut alias_output_builder: AliasOutputBuilder = AliasOutputBuilder::from(&alias_output) @@ -130,7 +118,7 @@ pub trait StardustIdentityClientExt: StardustIdentityClient { /// /// Returns `Err` when failing to resolve the `did`. async fn deactivate_did_output(&self, did: &StardustDID) -> Result { - let alias_id: AliasId = AliasId::try_from(did)?; + let alias_id: AliasId = AliasId::from(did); let (_, alias_output) = self.get_alias_output(alias_id).await?; let mut alias_output_builder: AliasOutputBuilder = AliasOutputBuilder::from(&alias_output) @@ -154,7 +142,7 @@ pub trait StardustIdentityClientExt: StardustIdentityClient { async fn resolve_did(&self, did: &StardustDID) -> Result { validate_network(self, did).await?; - let id: AliasId = AliasId::try_from(did)?; + let id: AliasId = AliasId::from(did); let (_, alias_output) = self.get_alias_output(id).await?; let document: &[u8] = alias_output.state_metadata(); @@ -170,7 +158,7 @@ pub trait StardustIdentityClientExt: StardustIdentityClient { async fn resolve_did_output(&self, did: &StardustDID) -> Result { validate_network(self, did).await?; - let id: AliasId = AliasId::try_from(did)?; + let id: AliasId = AliasId::from(did); self.get_alias_output(id).await.map(|(_, alias_output)| alias_output) } diff --git a/identity_stardust/src/client/iota_client.rs b/identity_stardust/src/client/iota_client.rs index fb76512f6b..d3c6dcd80d 100644 --- a/identity_stardust/src/client/iota_client.rs +++ b/identity_stardust/src/client/iota_client.rs @@ -79,7 +79,7 @@ impl StardustClientExt for Client { async fn delete_did_output(&self, secret_manager: &SecretManager, address: Address, did: &StardustDID) -> Result<()> { validate_network(self, did).await?; - let alias_id: AliasId = AliasId::try_from(did)?; + let alias_id: AliasId = AliasId::from(did); let (output_id, alias_output) = self.get_alias_output(alias_id).await?; let basic_output = BasicOutputBuilder::new_with_amount(alias_output.amount()) diff --git a/identity_stardust/src/did/stardust_did.rs b/identity_stardust/src/did/stardust_did.rs index b18429ead5..61bc819ac3 100644 --- a/identity_stardust/src/did/stardust_did.rs +++ b/identity_stardust/src/did/stardust_did.rs @@ -343,6 +343,21 @@ impl KeyComparable for StardustDID { } } +#[cfg(feature = "client")] +mod __stardust_did_iota_client { + use crate::block::output::AliasId; + use crate::StardustDID; + + impl From<&StardustDID> for AliasId { + /// Creates an [`AliasId`] from the DID tag. + fn from(did: &StardustDID) -> Self { + let tag_bytes: [u8; StardustDID::TAG_BYTES_LEN] = prefix_hex::decode(did.tag()) + .expect("being able to successfully decode the tag should be checked during DID creation"); + AliasId::new(tag_bytes) + } + } +} + #[cfg(test)] mod tests { use once_cell::sync::Lazy; From 0e8993aeab2e238e06ab92841fe9b224c7d53144 Mon Sep 17 00:00:00 2001 From: cycraig Date: Thu, 25 Aug 2022 09:34:33 +0200 Subject: [PATCH 41/89] Change Stardust DID method to IOTA (#982) --- bindings/wasm/package-lock.json | 12 ++++++------ bindings/wasm/src/stardust/stardust_did.rs | 5 ++--- identity_stardust/src/did/stardust_did.rs | 18 ++++++++---------- .../src/document/stardust_document.rs | 2 +- .../src/state_metadata/document.rs | 4 ++-- 5 files changed, 19 insertions(+), 22 deletions(-) diff --git a/bindings/wasm/package-lock.json b/bindings/wasm/package-lock.json index d7dcdd2788..67cca14774 100644 --- a/bindings/wasm/package-lock.json +++ b/bindings/wasm/package-lock.json @@ -106,9 +106,9 @@ } }, "node_modules/@cycraig/iota-client-wasm": { - "version": "0.5.0-alpha.1", - "resolved": "https://registry.npmjs.org/@cycraig/iota-client-wasm/-/iota-client-wasm-0.5.0-alpha.1.tgz", - "integrity": "sha512-5D1l6QkOupmvaI19mROp/QjcqFNOHvjjGo2zJUbGbF7tI/v6J/CyiYpziREqWMUkCzKW3a/gEE9/YRbTRkJ7pg==", + "version": "0.5.0-alpha.2", + "resolved": "https://registry.npmjs.org/@cycraig/iota-client-wasm/-/iota-client-wasm-0.5.0-alpha.2.tgz", + "integrity": "sha512-KbA7YUzEf+A1KoucQaPkKHTm8SFAB9ZX9WKAvRjgI+qh40xVubykbaj1cZ+x1UNekMK3K8Oc7uOmRtzBnsZZkA==", "peer": true, "dependencies": { "@iota/types": "^1.0.0-beta.11", @@ -6604,9 +6604,9 @@ } }, "@cycraig/iota-client-wasm": { - "version": "0.5.0-alpha.1", - "resolved": "https://registry.npmjs.org/@cycraig/iota-client-wasm/-/iota-client-wasm-0.5.0-alpha.1.tgz", - "integrity": "sha512-5D1l6QkOupmvaI19mROp/QjcqFNOHvjjGo2zJUbGbF7tI/v6J/CyiYpziREqWMUkCzKW3a/gEE9/YRbTRkJ7pg==", + "version": "0.5.0-alpha.2", + "resolved": "https://registry.npmjs.org/@cycraig/iota-client-wasm/-/iota-client-wasm-0.5.0-alpha.2.tgz", + "integrity": "sha512-KbA7YUzEf+A1KoucQaPkKHTm8SFAB9ZX9WKAvRjgI+qh40xVubykbaj1cZ+x1UNekMK3K8Oc7uOmRtzBnsZZkA==", "peer": true, "requires": { "@iota/types": "^1.0.0-beta.11", diff --git a/bindings/wasm/src/stardust/stardust_did.rs b/bindings/wasm/src/stardust/stardust_did.rs index 559d96a831..cab7839570 100644 --- a/bindings/wasm/src/stardust/stardust_did.rs +++ b/bindings/wasm/src/stardust/stardust_did.rs @@ -19,8 +19,7 @@ pub struct WasmStardustDID(pub(crate) StardustDID); #[wasm_bindgen(js_class = StardustDID)] impl WasmStardustDID { - /// The IOTA UTXO DID method name (`"stardust"`). - // TODO: This will be changed to `iota` in the future. + /// The IOTA UTXO DID method name (`"iota"`). #[wasm_bindgen(getter = METHOD)] pub fn static_method() -> String { StardustDID::METHOD.to_owned() @@ -52,7 +51,7 @@ impl WasmStardustDID { /// Creates a new placeholder [`StardustDID`] with the given network name. /// - /// E.g. `did:stardust:smr:0x0000000000000000000000000000000000000000000000000000000000000000`. + /// E.g. `did:iota:smr:0x0000000000000000000000000000000000000000000000000000000000000000`. #[wasm_bindgen] pub fn placeholder(network: String) -> Result { let network_name: NetworkName = NetworkName::try_from(network).wasm_result()?; diff --git a/identity_stardust/src/did/stardust_did.rs b/identity_stardust/src/did/stardust_did.rs index 61bc819ac3..edad3ffe1b 100644 --- a/identity_stardust/src/did/stardust_did.rs +++ b/identity_stardust/src/did/stardust_did.rs @@ -20,7 +20,7 @@ use crate::NetworkName; pub type Result = std::result::Result; -/// A DID URL conforming to the IOTA Stardust UTXO DID method specification. +/// A DID URL conforming to the IOTA UTXO DID method specification. /// /// See [`DIDUrl`]. pub type StardustDIDUrl = DIDUrl; @@ -38,9 +38,8 @@ impl StardustDID { /// The URL scheme for Decentralized Identifiers. pub const SCHEME: &'static str = CoreDID::SCHEME; - /// The IOTA UTXO DID method name (`"stardust"`). - // TODO: This will be changed to `iota` in the future. - pub const METHOD: &'static str = "stardust"; + /// The IOTA UTXO DID method name (`"iota"`). + pub const METHOD: &'static str = "iota"; /// The default Tangle network (`"main"`). pub const DEFAULT_NETWORK: &'static str = "main"; @@ -65,7 +64,7 @@ impl StardustDID { /// # use identity_stardust::StardustDID; /// # /// let did = StardustDID::new(&[1;32], &NetworkName::try_from("smr").unwrap()); - /// assert_eq!(did.as_str(), "did:stardust:smr:0x0101010101010101010101010101010101010101010101010101010101010101"); + /// assert_eq!(did.as_str(), "did:iota:smr:0x0101010101010101010101010101010101010101010101010101010101010101"); pub fn new(bytes: &[u8; 32], network_name: &NetworkName) -> Self { let tag = prefix_hex::encode(bytes); let did: String = format!("did:{}:{}:{}", Self::METHOD, network_name, tag); @@ -83,7 +82,7 @@ impl StardustDID { /// # use identity_stardust::StardustDID; /// # /// let placeholder = StardustDID::placeholder(&NetworkName::try_from("smr").unwrap()); - /// assert_eq!(placeholder.as_str(), "did:stardust:smr:0x0000000000000000000000000000000000000000000000000000000000000000"); + /// assert_eq!(placeholder.as_str(), "did:iota:smr:0x0000000000000000000000000000000000000000000000000000000000000000"); pub fn placeholder(network_name: &NetworkName) -> Self { Self::new(&[0; 32], network_name) } @@ -149,12 +148,11 @@ impl StardustDID { // Helpers // =========================================================================== - /// Checks if the given `DID` has a valid [`StardustDID`] `method` (i.e. `"stardust"`). + /// Checks if the given `DID` has a valid [`StardustDID`] `method` (i.e. `"iota"`). /// /// # Errors /// /// Returns `Err` if the input represents another method. - // TODO: Change the naming in the docs once we remove the code for the current IOTA method. fn check_method(did: &D) -> Result<()> { (did.method() == Self::METHOD) .then_some(()) @@ -188,8 +186,8 @@ impl StardustDID { /// Normalizes the DID `method_id` by removing the default network segment if present. /// /// E.g. - /// - `"did:stardust:main:123" -> "did:stardust:123"` is normalized - /// - `"did:stardust:dev:123" -> "did:stardust:dev:123"` is unchanged + /// - `"did:iota:main:123" -> "did:iota:123"` is normalized + /// - `"did:iota:dev:123" -> "did:iota:dev:123"` is unchanged // TODO: Remove the lint once this bug in clippy has been fixed. Without to_owned a mutable reference will be aliased. #[allow(clippy::unnecessary_to_owned)] fn normalize(mut did: CoreDID) -> CoreDID { diff --git a/identity_stardust/src/document/stardust_document.rs b/identity_stardust/src/document/stardust_document.rs index ed5f8b1789..020cae3b7b 100644 --- a/identity_stardust/src/document/stardust_document.rs +++ b/identity_stardust/src/document/stardust_document.rs @@ -465,7 +465,7 @@ mod tests { use super::*; fn valid_did() -> StardustDID { - "did:stardust:0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "did:iota:0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" .parse() .unwrap() } diff --git a/identity_stardust/src/state_metadata/document.rs b/identity_stardust/src/state_metadata/document.rs index 0c23884cd2..27cd1d0ee2 100644 --- a/identity_stardust/src/state_metadata/document.rs +++ b/identity_stardust/src/state_metadata/document.rs @@ -168,9 +168,9 @@ mod tests { fn test_document() -> TestSetup { let did_self = - StardustDID::parse("did:stardust:0x8036235b6b5939435a45d68bcea7890eef399209a669c8c263fac7f5089b2ec6").unwrap(); + StardustDID::parse("did:iota:0x8036235b6b5939435a45d68bcea7890eef399209a669c8c263fac7f5089b2ec6").unwrap(); let did_foreign = - StardustDID::parse("did:stardust:0x71b709dff439f1ac9dd2b9c2e28db0807156b378e13bfa3605ce665aa0d0fdca").unwrap(); + StardustDID::parse("did:iota:0x71b709dff439f1ac9dd2b9c2e28db0807156b378e13bfa3605ce665aa0d0fdca").unwrap(); let mut document: StardustDocument = StardustDocument::new_with_id(did_self.clone()); let keypair: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); From 13b87750aacd5b4ae8b7ba04713bad150e3e89a1 Mon Sep 17 00:00:00 2001 From: Philipp Date: Wed, 31 Aug 2022 13:58:11 +0200 Subject: [PATCH 42/89] Reexport `bee-block` from `iota-client` (#988) * Reexport `bee-block` from `iota-client` * Re-export either bee-block or `iota-client::block * Bump bee-block in Wasm --- bindings/wasm/Cargo.toml | 3 ++- examples/0_basic/0_create_did.rs | 2 +- examples/0_basic/1_update_did.rs | 2 +- examples/0_basic/2_resolve_did.rs | 2 +- examples/0_basic/3_deactivate_did.rs | 2 +- examples/0_basic/4_delete_did.rs | 2 +- examples/1_advanced/0_did_controls_did.rs | 2 +- examples/1_advanced/1_did_issues_nft.rs | 2 +- examples/1_advanced/2_nft_owns_did.rs | 2 +- examples/1_advanced/3_did_issues_tokens.rs | 2 +- examples/1_advanced/4_key_exchange.rs | 2 +- examples/Cargo.toml | 2 +- identity_stardust/Cargo.toml | 4 ++-- identity_stardust/src/error.rs | 2 +- identity_stardust/src/lib.rs | 6 ++++-- 15 files changed, 20 insertions(+), 17 deletions(-) diff --git a/bindings/wasm/Cargo.toml b/bindings/wasm/Cargo.toml index 75b4f3831b..3914cc1ab5 100644 --- a/bindings/wasm/Cargo.toml +++ b/bindings/wasm/Cargo.toml @@ -17,7 +17,8 @@ crate-type = ["cdylib", "rlib"] [dependencies] async-trait = { version = "0.1", default-features = false } -bee-block = { version = "1.0.0-beta.5", default-features = false, features = ["dto"] } +# Ensure bee-block always matches the version used by iota-client. +bee-block = { version = "1.0.0-beta.7", default-features = false, features = ["dto"] } console_error_panic_hook = { version = "0.1" } futures = { version = "0.3" } js-sys = { version = "0.3" } diff --git a/examples/0_basic/0_create_did.rs b/examples/0_basic/0_create_did.rs index 7ecb79740a..b07b8fcbca 100644 --- a/examples/0_basic/0_create_did.rs +++ b/examples/0_basic/0_create_did.rs @@ -30,7 +30,7 @@ async fn main() -> anyhow::Result<()> { let mut secret_manager: SecretManager = SecretManager::Stronghold( StrongholdSecretManager::builder() .password("secure_password") - .try_build(random_stronghold_path())?, + .build(random_stronghold_path())?, ); // Get an address and with funds for testing. diff --git a/examples/0_basic/1_update_did.rs b/examples/0_basic/1_update_did.rs index d3253107f5..59ca2ad742 100644 --- a/examples/0_basic/1_update_did.rs +++ b/examples/0_basic/1_update_did.rs @@ -33,7 +33,7 @@ async fn main() -> anyhow::Result<()> { let mut secret_manager: SecretManager = SecretManager::Stronghold( StrongholdSecretManager::builder() .password("secure_password") - .try_build(random_stronghold_path())?, + .build(random_stronghold_path())?, ); // Create a new DID in an Alias Output for us to modify. diff --git a/examples/0_basic/2_resolve_did.rs b/examples/0_basic/2_resolve_did.rs index e3b9cca41d..aa034abc43 100644 --- a/examples/0_basic/2_resolve_did.rs +++ b/examples/0_basic/2_resolve_did.rs @@ -23,7 +23,7 @@ async fn main() -> anyhow::Result<()> { let mut secret_manager: SecretManager = SecretManager::Stronghold( StrongholdSecretManager::builder() .password("secure_password") - .try_build(random_stronghold_path())?, + .build(random_stronghold_path())?, ); // Create a new DID in an Alias Output for us to modify. diff --git a/examples/0_basic/3_deactivate_did.rs b/examples/0_basic/3_deactivate_did.rs index b3b1ae2a8d..f62109d921 100644 --- a/examples/0_basic/3_deactivate_did.rs +++ b/examples/0_basic/3_deactivate_did.rs @@ -25,7 +25,7 @@ async fn main() -> anyhow::Result<()> { let mut secret_manager: SecretManager = SecretManager::Stronghold( StrongholdSecretManager::builder() .password("secure_password") - .try_build(random_stronghold_path())?, + .build(random_stronghold_path())?, ); // Create a new DID in an Alias Output for us to modify. diff --git a/examples/0_basic/4_delete_did.rs b/examples/0_basic/4_delete_did.rs index 4312c4943c..9353ff9c8e 100644 --- a/examples/0_basic/4_delete_did.rs +++ b/examples/0_basic/4_delete_did.rs @@ -23,7 +23,7 @@ async fn main() -> anyhow::Result<()> { let mut secret_manager: SecretManager = SecretManager::Stronghold( StrongholdSecretManager::builder() .password("secure_password") - .try_build(random_stronghold_path())?, + .build(random_stronghold_path())?, ); // Create a new DID in an Alias Output for us to modify. diff --git a/examples/1_advanced/0_did_controls_did.rs b/examples/1_advanced/0_did_controls_did.rs index 948341c21d..6dff01e714 100644 --- a/examples/1_advanced/0_did_controls_did.rs +++ b/examples/1_advanced/0_did_controls_did.rs @@ -43,7 +43,7 @@ async fn main() -> anyhow::Result<()> { let mut secret_manager: SecretManager = SecretManager::Stronghold( StrongholdSecretManager::builder() .password("secure_password") - .try_build(random_stronghold_path())?, + .build(random_stronghold_path())?, ); // Create a new DID for the company. diff --git a/examples/1_advanced/1_did_issues_nft.rs b/examples/1_advanced/1_did_issues_nft.rs index b06b6991c7..796d70b4cb 100644 --- a/examples/1_advanced/1_did_issues_nft.rs +++ b/examples/1_advanced/1_did_issues_nft.rs @@ -48,7 +48,7 @@ async fn main() -> anyhow::Result<()> { let mut secret_manager: SecretManager = SecretManager::Stronghold( StrongholdSecretManager::builder() .password("secure_password") - .try_build(random_stronghold_path())?, + .build(random_stronghold_path())?, ); // Create a new DID for the manufacturer. diff --git a/examples/1_advanced/2_nft_owns_did.rs b/examples/1_advanced/2_nft_owns_did.rs index d90f25f6fd..bb6f663f81 100644 --- a/examples/1_advanced/2_nft_owns_did.rs +++ b/examples/1_advanced/2_nft_owns_did.rs @@ -46,7 +46,7 @@ async fn main() -> anyhow::Result<()> { let mut secret_manager: SecretManager = SecretManager::Stronghold( StrongholdSecretManager::builder() .password("secure_password") - .try_build(random_stronghold_path())?, + .build(random_stronghold_path())?, ); // Get an address with funds for testing. diff --git a/examples/1_advanced/3_did_issues_tokens.rs b/examples/1_advanced/3_did_issues_tokens.rs index 7a91ef2427..b6ebaa8952 100644 --- a/examples/1_advanced/3_did_issues_tokens.rs +++ b/examples/1_advanced/3_did_issues_tokens.rs @@ -59,7 +59,7 @@ async fn main() -> anyhow::Result<()> { let mut secret_manager: SecretManager = SecretManager::Stronghold( StrongholdSecretManager::builder() .password("secure_password") - .try_build(random_stronghold_path())?, + .build(random_stronghold_path())?, ); // Create a new DID for the authority. diff --git a/examples/1_advanced/4_key_exchange.rs b/examples/1_advanced/4_key_exchange.rs index a3126a6ef5..a14877d391 100644 --- a/examples/1_advanced/4_key_exchange.rs +++ b/examples/1_advanced/4_key_exchange.rs @@ -40,7 +40,7 @@ async fn main() -> anyhow::Result<()> { let mut secret_manager: SecretManager = SecretManager::Stronghold( StrongholdSecretManager::builder() .password("secure_password") - .try_build(random_stronghold_path())?, + .build(random_stronghold_path())?, ); // Get an address and with funds for testing. diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 26f3642972..863a1e157a 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -10,7 +10,7 @@ anyhow = "1.0.62" identity_core = { path = "../identity_core" } identity_did = { path = "../identity_did" } identity_stardust = { path = "../identity_stardust" } -iota-client = { version = "2.0.0-beta.2", default-features = false, features = ["tls", "stronghold"] } +iota-client = { version = "2.0.0-beta.3", default-features = false, features = ["tls", "stronghold"] } primitive-types = "0.11.1" rand = "0.8.5" tokio = { version = "1.20.1", default-features = false, features = ["rt"] } diff --git a/identity_stardust/Cargo.toml b/identity_stardust/Cargo.toml index b94ff8faf9..20dee67a33 100644 --- a/identity_stardust/Cargo.toml +++ b/identity_stardust/Cargo.toml @@ -13,13 +13,13 @@ description = "An IOTA Ledger integration for the identity.rs library." [dependencies] # Ensure bee-block always matches the version used by iota-client. -bee-block = { version = "1.0.0-beta.6", default-features = false, features = ["std"], optional = true } +bee-block = { version = "1.0.0-beta.7", default-features = false, features = ["std"], optional = true } identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } identity_credential = { version = "=0.6.0", path = "../identity_credential", default-features = false } identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } async-trait = { version = "0.1.56", default-features = false, optional = true } -iota-client = { version = "2.0.0-beta.2", default-features = false, features = ["tls"], optional = true } +iota-client = { version = "2.0.0-beta.3", default-features = false, features = ["tls"], optional = true } num-derive = { version = "0.3", default-features = false } num-traits = { version = "0.2", default-features = false, features = ["std"] } once_cell = { version = "1", default-features = false, features = ["std"] } diff --git a/identity_stardust/src/error.rs b/identity_stardust/src/error.rs index 8e36f070d7..08f5542732 100644 --- a/identity_stardust/src/error.rs +++ b/identity_stardust/src/error.rs @@ -31,7 +31,7 @@ pub enum Error { RevocationError(#[source] identity_did::Error), #[cfg(feature = "client")] #[error("alias output build error")] - AliasOutputBuildError(#[source] bee_block::Error), + AliasOutputBuildError(#[source] crate::block::Error), #[cfg(feature = "iota-client")] #[error("output with id `{0}` is not an alias output")] NotAnAliasOutput(iota_client::block::output::OutputId), diff --git a/identity_stardust/src/lib.rs b/identity_stardust/src/lib.rs index 6baf38e7ad..2c38e7dbbc 100644 --- a/identity_stardust/src/lib.rs +++ b/identity_stardust/src/lib.rs @@ -4,9 +4,11 @@ #![forbid(unsafe_code)] #![allow(clippy::upper_case_acronyms)] -/// Re-export the `bee_block` crate for implementer convenience. -#[cfg(feature = "client")] +// Re-export the `bee_block` crate for implementer convenience. +#[cfg(all(feature = "client", not(feature = "iota-client")))] pub use bee_block as block; +#[cfg(feature = "iota-client")] +pub use iota_client::block; #[cfg(feature = "client")] pub use client::*; From c8651c66895aa72880166ceb351b9beea42688e9 Mon Sep 17 00:00:00 2001 From: Philipp Date: Thu, 1 Sep 2022 13:38:30 +0200 Subject: [PATCH 43/89] Use recommended cargo tools; improve performance (#987) --- .github/workflows/build-and-test.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index e884a280ae..3bf0075662 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -114,18 +114,18 @@ jobs: # run examples only on ubuntu for now if: matrix.os == 'ubuntu-latest' run: | - cargo read-manifest --manifest-path ./examples_legacy/Cargo.toml | \ - jq -r '.targets[].name' | \ - parallel -k -j 4 --retries 3 cargo run --example {} --all-features --release + cargo metadata --format-version 1 --manifest-path ./examples_legacy/Cargo.toml | \ + jq -r '.packages[] | select(.name == "examples_legacy") | .targets[].name' + parallel -k -j 4 --retries 3 ./target/release/examples/{} - name: Run Stardust Rust examples # run examples only on ubuntu for now if: matrix.os == 'ubuntu-latest' run: | - cargo read-manifest --manifest-path ./examples/Cargo.toml | \ - jq -r '.targets[].name' | \ + cargo metadata --format-version 1 --manifest-path ./examples/Cargo.toml | \ + jq -r '.packages[] | select(.name == "examples") | .targets[].name' awk '$1 ~ /[0-9].*/' | \ - parallel -k -j 4 --retries 3 cargo run --manifest-path ./examples/Cargo.toml --example {} --all-features --release + parallel -k -j 4 --retries 3 ./target/release/examples/{} - name: Stop sccache uses: './.github/actions/rust/sccache/stop-sccache' From 0603e6b2eaa32c4956d660a0a8d4f4e0d869d7fa Mon Sep 17 00:00:00 2001 From: "Oliver E. Anderson" Date: Mon, 5 Sep 2022 15:26:39 +0200 Subject: [PATCH 44/89] fix broken bindings compilation (#995) --- identity_iota_client/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/identity_iota_client/Cargo.toml b/identity_iota_client/Cargo.toml index 5ad3e1292e..a06276ed90 100644 --- a/identity_iota_client/Cargo.toml +++ b/identity_iota_client/Cargo.toml @@ -29,7 +29,7 @@ serde = { version = "1.0", default-features = false, features = ["std", "derive" strum = { version = "0.24.0", default-features = false, features = ["std", "derive"] } thiserror = { version = "1.0", default-features = false } -[dependencies.iota-client] +[target.'cfg(not(target_family = "wasm"))'.dependencies.iota-client] version = "1.2.0" features = ["async", "tls"] default-features = false From cfeb07f293c86eaf78a6e704ec7e95f536ebcd1a Mon Sep 17 00:00:00 2001 From: Abdulrahim Al Methiab <31316147+abdulmth@users.noreply.github.com> Date: Tue, 6 Sep 2022 10:44:39 +0200 Subject: [PATCH 45/89] Update `create DID` wiki page (#986) --- .../decentralized_identifiers/alias.md | 38 +++++++++++++++++++ .../decentralized_identifiers/create.mdx | 29 +++++++++----- .../decentralized_identifiers/overview.md | 29 ++++++++------ documentation/sidebars.js | 1 + 4 files changed, 76 insertions(+), 21 deletions(-) create mode 100644 documentation/docs/concepts/decentralized_identifiers/alias.md diff --git a/documentation/docs/concepts/decentralized_identifiers/alias.md b/documentation/docs/concepts/decentralized_identifiers/alias.md new file mode 100644 index 0000000000..536dd1184a --- /dev/null +++ b/documentation/docs/concepts/decentralized_identifiers/alias.md @@ -0,0 +1,38 @@ +--- +title: Alias Output +sidebar_label: Alias Output +description: UTXO Alias Ouput +image: /img/Identity_icon.png +keywords: +- public keys +- utxo +- Method Specification +- Decentralized Identifiers +- overview +- DLT +--- + +# Alias Output + +The IOTA method uses the IOTA ledger which uses the [unspent transaction output (UTXO) model](https://wiki.iota.org/goshimmer/protocol_specification/components/ledgerstate). Also, the features of the [Stardust](https://wiki.iota.org/introduction/develop/explanations/what_is_stardust) upgrade are fundamental to the IOTA DID method. + +The Alias Output is used for storing the DID Document on the ledger. It is a specific implementation of the UTXO state machine that can hold arbitrary data in its `State Metadata`. The Alias Output has two kinds of controllers, a state controller and a governor. A state controller can execute a state transition which allows updating the data in the `State Metadata`. The governor, on the contrary, can't update the `State Metadata` but can change both controllers and destroy the Alias Output. +A controller can be either Ed25519 Address, Alias Address or an NFT Address and at most one of each can be set for an Alias Output. + +In order to create a new Alias Output, a transaction must be made that includes another Output, for example a Basic Output, as input and the new Alias Output, along with other outputs if needed, as outputs. + +### Storage Deposit + +The arbitrary data stored in the `State Metadata` of the Alias output must be covered by a storage deposit using IOTA coins. This helps to control the ledger size from growing uncontrollably while guaranteeing the data is indefinitely stored on the ledger which is important for resolving DID Documents. This deposit is fully refundable and can be reclaimed when the output is destroyed. Both, the state controller and the governor can control the IOTA coins stored in the Alias Output. Nodes expose an API to calculate the required deposit depending on the size of the data stored. + +### Alias Id + +Each Alias Output has an `Alias ID`. This ID is assigned after a transaction creates a new Alias Output. The actual DID is derived from this `Alias ID`, hence it is be unknown before publishing the transaction. Consequently, the DID inside the `State Metadata` will be replaced by the placeholder `did:0:0` to indicate self. + +If a transaction has an Alias Output as input, its `Alias ID` can be kept by one of its outputs. This feature is necessary for updating the DID Documents since the DID itself is derived from the Alias Output. + + + + + + diff --git a/documentation/docs/concepts/decentralized_identifiers/create.mdx b/documentation/docs/concepts/decentralized_identifiers/create.mdx index a9cd6d66bd..7c3f10c99c 100644 --- a/documentation/docs/concepts/decentralized_identifiers/create.mdx +++ b/documentation/docs/concepts/decentralized_identifiers/create.mdx @@ -11,11 +11,11 @@ keywords: - Publish --- import CodeSnippet from '../../../src/components/CodeSnippetComponent' -import createDidRustExample from '!!raw-loader!../../../../examples_legacy/account/create_did.rs'; +import createDidRustExample from '!!raw-loader!../../../../examples/0_basic/0_create_did.rs'; -When someone or something wants to benefit from Self-Sovereign Identity, they must first create a Decentralized Identity. This identity consists of many parts that have different functions. This page will cover both the basics and the details about identity creation, storage, and publishing to the Tangle. -The example below utilizes the high-level account module of the IOTA Identity framework to create an identity. The account is the easiest method of using IOTA Identity. It is recommended to use the account for your use cases, although a lower-level API is also available, providing more flexibility at the cost of more complexity. +When someone or something wants to benefit from Self-Sovereign Identity, they must first create a Decentralized Identity. This identity consists of many parts that have different functions. This page will cover both the basics and the details about identity creation and publishing. + ## Creating an Identity Using the Account @@ -25,17 +25,28 @@ Select your programming language of choice and press the green play button to ex ::: - -The first step in this example is the creation of an account. This acts as a stateful object that manages one or more identities. The account provides an interface to execute high-level operations on identities, such as creating, updating, and storing them. +This examples creates a new address with funds using the faucet on the testnet. Next, it creates a new DID Document with a verification method. This DID Document is then published in an Alias Output making the DID available in the ledger state and resolvable by any node. + +Note that using the faucet is only possible on the testnet. Once the Stardust update is released on the Shimmer network or the IOTA network, an output with funds is required to create a new Alias Output that represents a DID. + +Updating the identity is also possible, which will be discussed in the next section, but all previous states of the DID Document will still be stored in permanodes or any third party recording the network activity. -Next, the identity is created and published to the IOTA Tangle. This operation will generate a private key, storing it in the account, generating a DID, DID Document, and publishing it to the Tangle. Once it is uploaded to the Tangle, it becomes immutable, meaning that this version of the identity can never be altered or removed. The only way to update or delete an identity is by publishing a new version, which we will discuss in the next section. This immutability is what makes a Decentralized Identity solution based on Distributed Ledger Technology (DLT) trustworthy. The public keys inside the DID Document can never be changed without having access to the private key, allowing the users to completely control their own identities. The rest of the example shows how to retrieve (resolve) the identity from the Tangle and how it can be deleted. ### Identity Generation Process -The generation of an identity starts with a randomly generated asymmetric key pair. This can be generated by the IOTA Identity framework or can be provided as a parameter during the creation process. The public key is hashed using the `Blake2b-256` algorithm. This hash becomes the DID, creating a permanent and provable link between the initial keypair and the DID. The public key is then embedded into the initial DID Document and is used for verifying signatures created with the corresponding private key. This process can be observed and manipulated in depth by using the low-level API available for the IOTA Identity framework. These low-level APIs are available in [Rust](../../libraries/rust/getting_started#api-reference) and [WASM](../../libraries/wasm/api_reference) but are only recommended for complex use cases that require maximum flexibility in the framework. +In order to create a Alias Output, an existing output is required to make a transaction. For that a new Address is generated and a Basic Output controlled by this address is created using the faucet. + +A DID Document is then created which includes one verification method. At this point the DID itself is unknown since the Alias Output is not published yet and didn't get an `Alias ID` assigned. For that a placeholder `did:stardust:0x0000000000000000000000000000000000000000000000000000000000000000` is used to reference the DID inside the document. + +An Alias Output is created which contains an encoded version of the DID Document in its `State Metadata` and has the state controller and the governor set to the generated Ed25519 address. Note that controllers don't have to be Ed25519 addresses, they can be any type of output. However, they must be unlocked in order perform a state or governance transition when the DID Document is updated or destroyed. + +The byte cost for the document is then automatically calculated and a new transaction is published that includes the Basic Output as input and the newly generated Alias Output as output as well as a Basic Output which contains the remaining IOTA coins. + +Once the transaction is confirmed, the `Alias ID` would be assigned and the DID can be derived from it. Now The DID Document is stored on the ledger and can be resolved using any node. See the further examples for [updating](./update) and [resolving](./resolve) DID. diff --git a/documentation/docs/concepts/decentralized_identifiers/overview.md b/documentation/docs/concepts/decentralized_identifiers/overview.md index be1dbb39b2..e3e402425c 100644 --- a/documentation/docs/concepts/decentralized_identifiers/overview.md +++ b/documentation/docs/concepts/decentralized_identifiers/overview.md @@ -13,12 +13,13 @@ keywords: # Decentralized Identifiers (DID) -The Decentralized Identifiers (DID) standard from the World Wide Web Consortium (W3C) is the fundamental standard that supports the concept of a decentralized digital identity. A DID is a unique identifier that contains information that can be resolved to a DID Document. This document contains data such as public keys, enabling the holder to prove ownership over their personal data, but also URIs that link to public information about the identity. This implementation complies to the [DID specifications v1.0 Working](https://www.w3.org/TR/did-core//). +A DID is a unique identifier that can be resolved to a DID Document. This document contains data such as public keys, enabling the holder to prove ownership over their personal data, but also URIs that link to public information about the identity. DIDs are the fundamental building blocks of decentralized digital identity. +This implementation complies to the [DID specifications v1.0](https://www.w3.org/TR/did-core//) from the World Wide Web Consortium (W3C). -In the IOTA Identity framework, we have implemented the DID standard according to the `iota` [DID Method Specification](../../specs/did/iota_did_method_spec.md). We recommend seeing the `iota` DID Method Specification as the golden standard for DID on IOTA. Other implementations of DID on IOTA are recommended to follow the `iota` DID Method Specification. However, it is not necessary to implement a novel Method implementation for every project, so feel free to utilize this framework directly. +In the IOTA Identity framework, we have implemented the DID standard according to the `iota` [DID Method Specification](../../specs/did/iota_did_method_spec.md). Other implementations of DID on IOTA must follow the `iota` DID Method Specification if they also want to use the `iota` method name. It should not be necessary to re-implement the specification, if you can use library though. An example of a DID conforming to the `iota` method specification: -`did:iota:8dQAzVbbf6FLW9ckwyCBnKmcMGcUV9LYJoXtgQkHcNQy` +`did:iota:0xe4edef97da1257e83cbeb49159cfdd2da6ac971ac447f233f8439cf29376ebfe` ## Chapter Overview @@ -28,15 +29,15 @@ In this chapter, we will explain the basic aspects of the DID standard. We will A Decentralized Identifier, or DID, is a unique identifier that is tied to a subject. This subject can be anything, like a person, an organization, an IoT device, or even an object. The identifier can be used by the subject to identify themselves through a digital format, providing a basis for online identification. The identifier looks like a set of random characters that includes some prefixes to determine which standard and implementation is used: -`did:iota:8dQAzVbbf6FLW9ckwyCBnKmcMGcUV9LYJoXtgQkHcNQy` +`did:iota:0xe4edef97da1257e83cbeb49159cfdd2da6ac971ac447f233f8439cf29376ebfe` The World Wide Web Consortium (W3C) is a well-known standardization body that has standardized how DIDs should look and work. This provides a basis for different technologies that implement the DID standard to achieve interoperability. A full list of all implementations can be found [here.](https://www.w3.org/TR/did-spec-registries/#did-methods) Please keep in mind that unfortunately most of these methods are outdated and not maintained. ## DID Documents -The purpose of a DID is to help navigate to a DID Document, which is a document containing more information regarding the identity subject. This document contains data such as public keys, enabling the subject to prove ownership over their personal data, but also URIs that link to public information about the identity. +The purpose of a DID is to help navigate to a DID Document, which is a document containing more information regarding the identity subject. This document contains data such as public keys, enabling the subject to prove ownership over their personal data, but can contain additional information on how to interact with the subject. -The identifier contains all information to resolve a DID, providing the latest DID Document. The first three characters `did` indicate that the DID standard from W3C must be used to resolve the identifier. It is followed by a unique method name, in our case `iota`, to indicate that the IOTA method is used. The IOTA method is a specific implementation that follows the following [method spec](../../specs/did/iota_did_method_spec.md). This provides unique rules for the protocol to follow to result in the latest DID Document. In our case, it describes how DID Documents are uploaded and queried to and from the IOTA Tangle. Lastly, a DID contains a set of random characters that are unique per identity, this makes the identity unique and makes sure every identity resolves to a unique DID Document. +The identifier contains all information to resolve a DID, providing the latest DID Document. The first three characters `did` indicate that the DID standard from W3C must be used to resolve the identifier. It is followed by a unique method name, in our case `iota`, to indicate that the IOTA method is used. The IOTA method is a specific implementation following the [IOTA DID Method Specification](../../specs/did/iota_did_method_spec.md). This provides unique rules for the protocol to follow in order to manage a DID Document. In our case, it describes how DID Documents are uploaded and queried to and from the IOTA ledger. Lastly, a DID contains a set of random characters that are unique per identity, this makes the identity unique and makes sure every identity resolves to a unique DID Document. :::tip Requires basic knowledge of Asymmetric Encryption @@ -64,15 +65,19 @@ DIDs become more interesting in combination with Verifiable Credentials, which w IOTA Identity is a framework to implement Self-Sovereign Identities on IOTA. Inherently, IOTA provides some unique features that have a major impact on the usability of the framework. -### Feeless +### IOTA ledger benefits -IOTA is a feeless Distributed Ledger Technology, which means that messages can immutably be stored inside the Tangle at no cost, nor a requirement of holding any cryptocurrency tokens. That means that SSI applications can directly deploy towards the main network without any problems, as compared to most other SSI solutions running on a test network or having cryptocurrency requirements. This doesn't just make IOTA Identity have predictable costs and prevent issues around cryptocurrency holding taxes and legislation, it also makes it a fair network as anyone would be able to create one or more identities at no cost. The wealth of someone is irrelevant, making it the most inclusive SSI solution. +As DID Documents are stored in the ledger state by being covered by the storage deposit, this guarantees that all nodes will have an up-to-date copy of the latest DID Document. Resolving a DID into its document can usually be done by any IOTA node in the network. This solves many issues regarding availability, accessibility or synchronization. -### Ease-of-use +### Layer1 interactions + +DID Document are stored in an Alias Outputs, this allows them to directly interact with Layer 1 artifacts like NFTs and native assets. For instance an Alias Output representing a DID can hold native assets or control NFTs. Transferring funds between DIDs is also possible on Layer 1. -Without the need for a token, IOTA Identity can directly be used on the main network without having to purchase and manage a cryptocurrency token. In addition, the framework provides easy-to-use APIs that allow both standardized behavior or flexible, yet more complex access. Lastly, IOTA Identity provides a [Stronghold](https://wiki.iota.org/stronghold.rs/welcome/ "Stronghold is an open-source software library that was originally built to protect IOTA Seeds, but can be used to protect any digital secret.") solution for managing secrets securely, without requiring developers to reinvent the security wheel. -### General Purpose DLT +### Ease-of-use + +Iota Identity helps with abstracting the details of the DID standard by providing easy-to-use APIs that allow standardized behavior. It also allows more flexible and complex management of DID Documents. -IOTA is a general-purpose DLT as compared to some for-purpose DLTs with restricted use cases. That means that SSI can easily be combined with other DLT features such as payments, data streams, smart contracts, and access control. It will no longer be needed to utilize multiple DLT projects alongside each other. +### Storage solution +IOTA Identity provides a [Stronghold](https://wiki.iota.org/stronghold.rs/welcome/ "Stronghold is an open-source software library that was originally built to protect IOTA Seeds, but can be used to protect any digital secret.") solution for managing secrets securely, without requiring developers to reinvent the security wheel. diff --git a/documentation/sidebars.js b/documentation/sidebars.js index 92f26a07ef..2e8abca683 100644 --- a/documentation/sidebars.js +++ b/documentation/sidebars.js @@ -44,6 +44,7 @@ module.exports = { { 'Decentralized Identifiers (DID)': [ 'concepts/decentralized_identifiers/overview', + 'concepts/decentralized_identifiers/alias', 'concepts/decentralized_identifiers/create', 'concepts/decentralized_identifiers/update', 'concepts/decentralized_identifiers/resolve', From 2a2123aad7bdc64fbb89ce72d6e16f34fea5e460 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Ha=C3=9F?= Date: Tue, 6 Sep 2022 14:47:24 +0200 Subject: [PATCH 46/89] disable nodejs-stronghold bindings in CI (#999) --- .github/workflows/build-and-test.yml | 18 ++++---- .../wasm-automatic-release-and-publish.yml | 46 +++++++++---------- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 3bf0075662..fb700cc3ba 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -205,12 +205,12 @@ jobs: run: npm run test:examples working-directory: bindings/wasm - build-and-test-stronghold-nodejs: - needs: - - check-for-run-condition - - build-wasm - if: ${{ needs.check-for-run-condition.outputs.should-run == 'true' }} - # owner/repository of workflow has to be static, see https://github.community/t/env-variables-in-uses/17466 - uses: iotaledger/identity.rs/.github/workflows/shared-build-and-test-stronghold-nodejs.yml@dev - with: - input-artifact-name: identity-wasm-bindings-build + # build-and-test-stronghold-nodejs: + # needs: + # - check-for-run-condition + # - build-wasm + # if: ${{ needs.check-for-run-condition.outputs.should-run == 'true' }} + # # owner/repository of workflow has to be static, see https://github.community/t/env-variables-in-uses/17466 + # uses: iotaledger/identity.rs/.github/workflows/shared-build-and-test-stronghold-nodejs.yml@dev + # with: + # input-artifact-name: identity-wasm-bindings-build diff --git a/.github/workflows/wasm-automatic-release-and-publish.yml b/.github/workflows/wasm-automatic-release-and-publish.yml index b1013342c7..be48a4042c 100644 --- a/.github/workflows/wasm-automatic-release-and-publish.yml +++ b/.github/workflows/wasm-automatic-release-and-publish.yml @@ -44,27 +44,27 @@ jobs: npm-token: ${{ secrets.NPM_TOKEN }} tag: ${{ needs.call-create-release-workflow.outputs.is-dev-release && 'dev' }} - build-stronghold-nodejs: - needs: build-wasm - if: ${{ needs.call-create-release-workflow.outputs.is-release }} - # owner/repository of workflow has to be static, see https://github.community/t/env-variables-in-uses/17466 - uses: iotaledger/identity.rs/.github/workflows/shared-build-and-test-stronghold-nodejs.yml@dev - with: - input-artifact-name: identity-wasm-bindings-build - upload-binaries-as-artifacts: true - output-artifact-name: identity-stronghold-nodejs-bindings-build - run-tests: false + # build-stronghold-nodejs: + # needs: build-wasm + # if: ${{ needs.call-create-release-workflow.outputs.is-release }} + # # owner/repository of workflow has to be static, see https://github.community/t/env-variables-in-uses/17466 + # uses: iotaledger/identity.rs/.github/workflows/shared-build-and-test-stronghold-nodejs.yml@dev + # with: + # input-artifact-name: identity-wasm-bindings-build + # upload-binaries-as-artifacts: true + # output-artifact-name: identity-stronghold-nodejs-bindings-build + # run-tests: false - release-stronghold-nodejs: - if: ${{ needs.call-create-release-workflow.outputs.is-release }} - runs-on: ubuntu-latest - needs: [call-create-release-workflow, build-stronghold-nodejs] - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Release to npm - uses: './.github/actions/publish/publish-stronghold-nodejs' - with: - npm-token: ${{ secrets.NPM_TOKEN }} - tag: ${{ needs.call-create-release-workflow.outputs.is-dev-release && 'dev' }} - input-artifact-name: identity-stronghold-nodejs-bindings-build + # release-stronghold-nodejs: + # if: ${{ needs.call-create-release-workflow.outputs.is-release }} + # runs-on: ubuntu-latest + # needs: [call-create-release-workflow, build-stronghold-nodejs] + # steps: + # - name: Checkout + # uses: actions/checkout@v2 + # - name: Release to npm + # uses: './.github/actions/publish/publish-stronghold-nodejs' + # with: + # npm-token: ${{ secrets.NPM_TOKEN }} + # tag: ${{ needs.call-create-release-workflow.outputs.is-dev-release && 'dev' }} + # input-artifact-name: identity-stronghold-nodejs-bindings-build From 1e02dda6ad64d6bd37229591a01e7f9d5dba046e Mon Sep 17 00:00:00 2001 From: Abdulrahim Al Methiab <31316147+abdulmth@users.noreply.github.com> Date: Wed, 7 Sep 2022 13:04:24 +0200 Subject: [PATCH 47/89] Add Wasm bindings for `CoreDocument` (#994) --- bindings/wasm/docs/api-reference.md | 761 +++++++++++++++++- bindings/wasm/src/did/mod.rs | 4 + bindings/wasm/src/did/wasm_core_did.rs | 97 ++- bindings/wasm/src/did/wasm_core_document.rs | 516 ++++++++++++ bindings/wasm/src/did/wasm_core_service.rs | 105 +++ bindings/wasm/src/did/wasm_core_url.rs | 105 +++ .../src/did/wasm_core_verification_method.rs | 120 +++ .../wasm/src/did/wasm_verification_method.rs | 1 + .../wasm/src/stardust/stardust_document.rs | 2 +- .../stardust/stardust_verification_method.rs | 48 +- bindings/wasm/tests/core.ts | 279 +++++++ 11 files changed, 2019 insertions(+), 19 deletions(-) create mode 100644 bindings/wasm/src/did/wasm_core_document.rs create mode 100644 bindings/wasm/src/did/wasm_core_service.rs create mode 100644 bindings/wasm/src/did/wasm_core_url.rs create mode 100644 bindings/wasm/src/did/wasm_core_verification_method.rs create mode 100644 bindings/wasm/tests/core.ts diff --git a/bindings/wasm/docs/api-reference.md b/bindings/wasm/docs/api-reference.md index 069c16bc0d..aa6e0c6ea0 100644 --- a/bindings/wasm/docs/api-reference.md +++ b/bindings/wasm/docs/api-reference.md @@ -27,8 +27,18 @@ the configuration of previously built accounts.

Client
CoreDID
-

A Decentralized Identifier (DID).

+

A method-agnostic Decentralized Identifier (DID).

+
CoreDIDUrl
+

A method agnostic DID Url.

+
+
CoreDocument
+
+
CoreService
+

A DID Document Service used to enable trusted interactions associated with a DID subject.

+
+
CoreVerificationMethod
+
Credential
CredentialValidationOptions
@@ -179,6 +189,8 @@ See IVerifierOptions.

## Members
+
StateMetadataEncoding
+
StatusCheck

Controls validation behaviour when checking whether or not a credential has been revoked by its credentialStatus.

@@ -230,8 +242,6 @@ This variant is the default used if no other variant is specified when construct
DIDMessageEncoding
-
StateMetadataEncoding
-
## Functions @@ -980,19 +990,115 @@ Creates a new `Client` with the given settings. ## CoreDID -A Decentralized Identifier (DID). +A method-agnostic Decentralized Identifier (DID). **Kind**: global class * [CoreDID](#CoreDID) * _instance_ + * [.setMethodName(value)](#CoreDID+setMethodName) + * [.setMethodId(value)](#CoreDID+setMethodId) + * [.scheme()](#CoreDID+scheme) ⇒ string + * [.authority()](#CoreDID+authority) ⇒ string + * [.method()](#CoreDID+method) ⇒ string + * [.methodId()](#CoreDID+methodId) ⇒ string + * [.join(segment)](#CoreDID+join) ⇒ [CoreDIDUrl](#CoreDIDUrl) + * [.toUrl()](#CoreDID+toUrl) ⇒ [CoreDIDUrl](#CoreDIDUrl) + * [.intoUrl()](#CoreDID+intoUrl) ⇒ [CoreDIDUrl](#CoreDIDUrl) * [.toString()](#CoreDID+toString) ⇒ string * [.toJSON()](#CoreDID+toJSON) ⇒ any * [.clone()](#CoreDID+clone) ⇒ [CoreDID](#CoreDID) * _static_ * [.parse(input)](#CoreDID.parse) ⇒ [CoreDID](#CoreDID) + * [.validMethodName(value)](#CoreDID.validMethodName) ⇒ boolean + * [.validMethodId(value)](#CoreDID.validMethodId) ⇒ boolean * [.fromJSON(json)](#CoreDID.fromJSON) ⇒ [CoreDID](#CoreDID) + + +### coreDID.setMethodName(value) +Set the method name of the `CoreDID`. + +**Kind**: instance method of [CoreDID](#CoreDID) + +| Param | Type | +| --- | --- | +| value | string | + + + +### coreDID.setMethodId(value) +Set the method-specific-id of the `DID`. + +**Kind**: instance method of [CoreDID](#CoreDID) + +| Param | Type | +| --- | --- | +| value | string | + + + +### coreDID.scheme() ⇒ string +Returns the `CoreDID` scheme. + +E.g. +- `"did:example:12345678" -> "did"` +- `"did:iota:smr:12345678" -> "did"` + +**Kind**: instance method of [CoreDID](#CoreDID) + + +### coreDID.authority() ⇒ string +Returns the `CoreDID` authority: the method name and method-id. + +E.g. +- `"did:example:12345678" -> "example:12345678"` +- `"did:iota:smr:12345678" -> "iota:smr:12345678"` + +**Kind**: instance method of [CoreDID](#CoreDID) + + +### coreDID.method() ⇒ string +Returns the `CoreDID` method name. + +E.g. +- `"did:example:12345678" -> "example"` +- `"did:iota:smr:12345678" -> "iota"` + +**Kind**: instance method of [CoreDID](#CoreDID) + + +### coreDID.methodId() ⇒ string +Returns the `CoreDID` method-specific ID. + +E.g. +- `"did:example:12345678" -> "12345678"` +- `"did:iota:smr:12345678" -> "smr:12345678"` + +**Kind**: instance method of [CoreDID](#CoreDID) + + +### coreDID.join(segment) ⇒ [CoreDIDUrl](#CoreDIDUrl) +Construct a new `CoreDIDUrl` by joining with a relative DID Url string. + +**Kind**: instance method of [CoreDID](#CoreDID) + +| Param | Type | +| --- | --- | +| segment | string | + + + +### coreDID.toUrl() ⇒ [CoreDIDUrl](#CoreDIDUrl) +Clones the `CoreDID` into a `CoreDIDUrl`. + +**Kind**: instance method of [CoreDID](#CoreDID) + + +### coreDID.intoUrl() ⇒ [CoreDIDUrl](#CoreDIDUrl) +Converts the `CoreDID` into a `CoreDIDUrl`, consuming it. + +**Kind**: instance method of [CoreDID](#CoreDID) ### coreDID.toString() ⇒ string @@ -1014,11 +1120,11 @@ Deep clones the object. ### CoreDID.parse(input) ⇒ [CoreDID](#CoreDID) -Parses a [`CoreDID`] from the given `input`. +Parses a `CoreDID` from the given `input`. -# Errors +### Errors -Returns `Err` if the input is not a valid [`CoreDID`]. +Throws an error if the input is not a valid `CoreDID`. **Kind**: static method of [CoreDID](#CoreDID) @@ -1026,6 +1132,28 @@ Returns `Err` if the input is not a valid [`CoreDID`]. | --- | --- | | input | string | + + +### CoreDID.validMethodName(value) ⇒ boolean +Validates whether a string is a valid DID method name. + +**Kind**: static method of [CoreDID](#CoreDID) + +| Param | Type | +| --- | --- | +| value | string | + + + +### CoreDID.validMethodId(value) ⇒ boolean +Validates whether a string is a valid `DID` method-id. + +**Kind**: static method of [CoreDID](#CoreDID) + +| Param | Type | +| --- | --- | +| value | string | + ### CoreDID.fromJSON(json) ⇒ [CoreDID](#CoreDID) @@ -1037,6 +1165,613 @@ Deserializes an instance from a JSON object. | --- | --- | | json | any | + + +## CoreDIDUrl +A method agnostic DID Url. + +**Kind**: global class + +* [CoreDIDUrl](#CoreDIDUrl) + * _instance_ + * [.did()](#CoreDIDUrl+did) ⇒ [CoreDID](#CoreDID) + * [.urlStr()](#CoreDIDUrl+urlStr) ⇒ string + * [.fragment()](#CoreDIDUrl+fragment) ⇒ string \| undefined + * [.setFragment(value)](#CoreDIDUrl+setFragment) + * [.path()](#CoreDIDUrl+path) ⇒ string \| undefined + * [.setPath(value)](#CoreDIDUrl+setPath) + * [.query()](#CoreDIDUrl+query) ⇒ string \| undefined + * [.setQuery(value)](#CoreDIDUrl+setQuery) + * [.join(segment)](#CoreDIDUrl+join) ⇒ [CoreDIDUrl](#CoreDIDUrl) + * [.toString()](#CoreDIDUrl+toString) ⇒ string + * [.toJSON()](#CoreDIDUrl+toJSON) ⇒ any + * [.clone()](#CoreDIDUrl+clone) ⇒ [CoreDIDUrl](#CoreDIDUrl) + * _static_ + * [.parse(input)](#CoreDIDUrl.parse) ⇒ [CoreDIDUrl](#CoreDIDUrl) + * [.fromJSON(json)](#CoreDIDUrl.fromJSON) ⇒ [CoreDIDUrl](#CoreDIDUrl) + + + +### coreDIDUrl.did() ⇒ [CoreDID](#CoreDID) +Return a copy of the `CoreDID` section of the `CoreDIDUrl`. + +**Kind**: instance method of [CoreDIDUrl](#CoreDIDUrl) + + +### coreDIDUrl.urlStr() ⇒ string +Return a copy of the relative DID Url as a string, including only the path, query, and fragment. + +**Kind**: instance method of [CoreDIDUrl](#CoreDIDUrl) + + +### coreDIDUrl.fragment() ⇒ string \| undefined +Returns a copy of the `CoreDIDUrl` method fragment, if any. Excludes the leading '#'. + +**Kind**: instance method of [CoreDIDUrl](#CoreDIDUrl) + + +### coreDIDUrl.setFragment(value) +Sets the `fragment` component of the `CoreDIDUrl`. + +**Kind**: instance method of [CoreDIDUrl](#CoreDIDUrl) + +| Param | Type | +| --- | --- | +| value | string \| undefined | + + + +### coreDIDUrl.path() ⇒ string \| undefined +Returns a copy of the `CoreDIDUrl` path. + +**Kind**: instance method of [CoreDIDUrl](#CoreDIDUrl) + + +### coreDIDUrl.setPath(value) +Sets the `path` component of the `CoreDIDUrl`. + +**Kind**: instance method of [CoreDIDUrl](#CoreDIDUrl) + +| Param | Type | +| --- | --- | +| value | string \| undefined | + + + +### coreDIDUrl.query() ⇒ string \| undefined +Returns a copy of the `CoreDIDUrl` method query, if any. Excludes the leading '?'. + +**Kind**: instance method of [CoreDIDUrl](#CoreDIDUrl) + + +### coreDIDUrl.setQuery(value) +Sets the `query` component of the `CoreDIDUrl`. + +**Kind**: instance method of [CoreDIDUrl](#CoreDIDUrl) + +| Param | Type | +| --- | --- | +| value | string \| undefined | + + + +### coreDIDUrl.join(segment) ⇒ [CoreDIDUrl](#CoreDIDUrl) +Append a string representing a path, query, and/or fragment, returning a new `CoreDIDUrl`. + +Must begin with a valid delimiter character: '/', '?', '#'. Overwrites the existing URL +segment and any following segments in order of path, query, then fragment. + +I.e. +- joining a path will clear the query and fragment. +- joining a query will clear the fragment. +- joining a fragment will only overwrite the fragment. + +**Kind**: instance method of [CoreDIDUrl](#CoreDIDUrl) + +| Param | Type | +| --- | --- | +| segment | string | + + + +### coreDIDUrl.toString() ⇒ string +Returns the `CoreDIDUrl` as a string. + +**Kind**: instance method of [CoreDIDUrl](#CoreDIDUrl) + + +### coreDIDUrl.toJSON() ⇒ any +Serializes this to a JSON object. + +**Kind**: instance method of [CoreDIDUrl](#CoreDIDUrl) + + +### coreDIDUrl.clone() ⇒ [CoreDIDUrl](#CoreDIDUrl) +Deep clones the object. + +**Kind**: instance method of [CoreDIDUrl](#CoreDIDUrl) + + +### CoreDIDUrl.parse(input) ⇒ [CoreDIDUrl](#CoreDIDUrl) +Parses a `CoreDIDUrl` from the input string. + +**Kind**: static method of [CoreDIDUrl](#CoreDIDUrl) + +| Param | Type | +| --- | --- | +| input | string | + + + +### CoreDIDUrl.fromJSON(json) ⇒ [CoreDIDUrl](#CoreDIDUrl) +Deserializes an instance from a JSON object. + +**Kind**: static method of [CoreDIDUrl](#CoreDIDUrl) + +| Param | Type | +| --- | --- | +| json | any | + + + +## CoreDocument +**Kind**: global class + +* [CoreDocument](#CoreDocument) + * [new CoreDocument(values)](#new_CoreDocument_new) + * _instance_ + * [.id()](#CoreDocument+id) ⇒ [CoreDID](#CoreDID) + * [.controller()](#CoreDocument+controller) ⇒ [Array.<CoreDID>](#CoreDID) + * [.setController(controllers)](#CoreDocument+setController) + * [.alsoKnownAs()](#CoreDocument+alsoKnownAs) ⇒ Array.<string> + * [.setAlsoKnownAs(urls)](#CoreDocument+setAlsoKnownAs) + * [.verificatonMethod()](#CoreDocument+verificatonMethod) ⇒ [Array.<CoreVerificationMethod>](#CoreVerificationMethod) + * [.authentication()](#CoreDocument+authentication) ⇒ Array.<(CoreDIDUrl\|CoreVerificationMethod)> + * [.assertionMethod()](#CoreDocument+assertionMethod) ⇒ Array.<(CoreDIDUrl\|CoreVerificationMethod)> + * [.keyAgreement()](#CoreDocument+keyAgreement) ⇒ Array.<(CoreDIDUrl\|CoreVerificationMethod)> + * [.capabilityDelegation()](#CoreDocument+capabilityDelegation) ⇒ Array.<(CoreDIDUrl\|CoreVerificationMethod)> + * [.capabilityInvocation()](#CoreDocument+capabilityInvocation) ⇒ Array.<(CoreDIDUrl\|CoreVerificationMethod)> + * [.properties()](#CoreDocument+properties) ⇒ Map.<string, any> + * [.setPropertyUnchecked(key, value)](#CoreDocument+setPropertyUnchecked) + * [.service()](#CoreDocument+service) ⇒ [Array.<CoreService>](#CoreService) + * [.resolveService(query)](#CoreDocument+resolveService) ⇒ [CoreService](#CoreService) \| undefined + * [.methods()](#CoreDocument+methods) ⇒ [Array.<CoreVerificationMethod>](#CoreVerificationMethod) + * [.insertMethod(method, scope)](#CoreDocument+insertMethod) + * [.removeMethod(did)](#CoreDocument+removeMethod) + * [.resolveMethod(query, scope)](#CoreDocument+resolveMethod) ⇒ [CoreVerificationMethod](#CoreVerificationMethod) \| undefined + * [.attachMethodRelationship(didUrl, relationship)](#CoreDocument+attachMethodRelationship) ⇒ boolean + * [.detachMethodRelationship(didUrl, relationship)](#CoreDocument+detachMethodRelationship) ⇒ boolean + * [.verifyData(data, options)](#CoreDocument+verifyData) ⇒ boolean + * [.revokeCredentials(serviceQuery, indices)](#CoreDocument+revokeCredentials) + * [.unrevokeCredentials(serviceQuery, indices)](#CoreDocument+unrevokeCredentials) + * [.toJSON()](#CoreDocument+toJSON) ⇒ any + * [.clone()](#CoreDocument+clone) ⇒ [CoreDocument](#CoreDocument) + * _static_ + * [.fromJSON(json)](#CoreDocument.fromJSON) ⇒ [CoreDocument](#CoreDocument) + + + +### new CoreDocument(values) +Creates a new `CoreDocument` with the given properties. + + +| Param | Type | +| --- | --- | +| values | ICoreDocument | + + + +### coreDocument.id() ⇒ [CoreDID](#CoreDID) +Returns a copy of the DID Document `id`. + +**Kind**: instance method of [CoreDocument](#CoreDocument) + + +### coreDocument.controller() ⇒ [Array.<CoreDID>](#CoreDID) +Returns a copy of the document controllers. + +**Kind**: instance method of [CoreDocument](#CoreDocument) + + +### coreDocument.setController(controllers) +Sets the controllers of the DID Document. + +Note: Duplicates will be ignored. +Use `null` to remove all controllers. + +**Kind**: instance method of [CoreDocument](#CoreDocument) + +| Param | Type | +| --- | --- | +| controllers | [CoreDID](#CoreDID) \| [Array.<CoreDID>](#CoreDID) \| null | + + + +### coreDocument.alsoKnownAs() ⇒ Array.<string> +Returns a copy of the document's `alsoKnownAs` set. + +**Kind**: instance method of [CoreDocument](#CoreDocument) + + +### coreDocument.setAlsoKnownAs(urls) +Sets the `alsoKnownAs` property in the DID document. + +**Kind**: instance method of [CoreDocument](#CoreDocument) + +| Param | Type | +| --- | --- | +| urls | string \| Array.<string> \| null | + + + +### coreDocument.verificatonMethod() ⇒ [Array.<CoreVerificationMethod>](#CoreVerificationMethod) +Returns a copy of the document's `verificationMethod` set. + +**Kind**: instance method of [CoreDocument](#CoreDocument) + + +### coreDocument.authentication() ⇒ Array.<(CoreDIDUrl\|CoreVerificationMethod)> +Returns a copy of the document's `authentication` set. + +**Kind**: instance method of [CoreDocument](#CoreDocument) + + +### coreDocument.assertionMethod() ⇒ Array.<(CoreDIDUrl\|CoreVerificationMethod)> +Returns a copy of the document's `assertionMethod` set. + +**Kind**: instance method of [CoreDocument](#CoreDocument) + + +### coreDocument.keyAgreement() ⇒ Array.<(CoreDIDUrl\|CoreVerificationMethod)> +Returns a copy of the document's `keyAgreement` set. + +**Kind**: instance method of [CoreDocument](#CoreDocument) + + +### coreDocument.capabilityDelegation() ⇒ Array.<(CoreDIDUrl\|CoreVerificationMethod)> +Returns a copy of the document's `capabilityDelegation` set. + +**Kind**: instance method of [CoreDocument](#CoreDocument) + + +### coreDocument.capabilityInvocation() ⇒ Array.<(CoreDIDUrl\|CoreVerificationMethod)> +Returns a copy of the document's `capabilityInvocation` set. + +**Kind**: instance method of [CoreDocument](#CoreDocument) + + +### coreDocument.properties() ⇒ Map.<string, any> +Returns a copy of the custom DID Document properties. + +**Kind**: instance method of [CoreDocument](#CoreDocument) + + +### coreDocument.setPropertyUnchecked(key, value) +Sets a custom property in the DID Document. +If the value is set to `null`, the custom property will be removed. + +### WARNING +This method can overwrite existing properties like `id` and result in an invalid document. + +**Kind**: instance method of [CoreDocument](#CoreDocument) + +| Param | Type | +| --- | --- | +| key | string | +| value | any | + + + +### coreDocument.service() ⇒ [Array.<CoreService>](#CoreService) +Returns a set of all [CoreService](#CoreService) in the document. + +**Kind**: instance method of [CoreDocument](#CoreDocument) + + +### coreDocument.resolveService(query) ⇒ [CoreService](#CoreService) \| undefined +Returns the first [CoreService](#CoreService) with an `id` property matching the provided `query`, +if present. + +**Kind**: instance method of [CoreDocument](#CoreDocument) + +| Param | Type | +| --- | --- | +| query | [CoreDIDUrl](#CoreDIDUrl) \| string | + + + +### coreDocument.methods() ⇒ [Array.<CoreVerificationMethod>](#CoreVerificationMethod) +Returns a list of all [CoreVerificationMethod](#CoreVerificationMethod) in the DID Document. + +**Kind**: instance method of [CoreDocument](#CoreDocument) + + +### coreDocument.insertMethod(method, scope) +Adds a new `method` to the document in the given `scope`. + +**Kind**: instance method of [CoreDocument](#CoreDocument) + +| Param | Type | +| --- | --- | +| method | [CoreVerificationMethod](#CoreVerificationMethod) | +| scope | [MethodScope](#MethodScope) | + + + +### coreDocument.removeMethod(did) +Removes all references to the specified Verification Method. + +**Kind**: instance method of [CoreDocument](#CoreDocument) + +| Param | Type | +| --- | --- | +| did | [CoreDIDUrl](#CoreDIDUrl) | + + + +### coreDocument.resolveMethod(query, scope) ⇒ [CoreVerificationMethod](#CoreVerificationMethod) \| undefined +Returns a copy of the first verification method with an `id` property +matching the provided `query` and the verification relationship +specified by `scope`, if present. + +**Kind**: instance method of [CoreDocument](#CoreDocument) + +| Param | Type | +| --- | --- | +| query | [CoreDIDUrl](#CoreDIDUrl) \| string | +| scope | [MethodScope](#MethodScope) \| undefined | + + + +### coreDocument.attachMethodRelationship(didUrl, relationship) ⇒ boolean +Attaches the relationship to the given method, if the method exists. + +Note: The method needs to be in the set of verification methods, +so it cannot be an embedded one. + +**Kind**: instance method of [CoreDocument](#CoreDocument) + +| Param | Type | +| --- | --- | +| didUrl | [CoreDIDUrl](#CoreDIDUrl) | +| relationship | number | + + + +### coreDocument.detachMethodRelationship(didUrl, relationship) ⇒ boolean +Detaches the given relationship from the given method, if the method exists. + +**Kind**: instance method of [CoreDocument](#CoreDocument) + +| Param | Type | +| --- | --- | +| didUrl | [CoreDIDUrl](#CoreDIDUrl) | +| relationship | number | + + + +### coreDocument.verifyData(data, options) ⇒ boolean +Verifies the authenticity of `data` using the target verification method. + +**Kind**: instance method of [CoreDocument](#CoreDocument) + +| Param | Type | +| --- | --- | +| data | any | +| options | [VerifierOptions](#VerifierOptions) | + + + +### coreDocument.revokeCredentials(serviceQuery, indices) +If the document has a `RevocationBitmap` service identified by `serviceQuery`, +revoke all specified `indices`. + +**Kind**: instance method of [CoreDocument](#CoreDocument) + +| Param | Type | +| --- | --- | +| serviceQuery | [CoreDIDUrl](#CoreDIDUrl) \| string | +| indices | number \| Array.<number> | + + + +### coreDocument.unrevokeCredentials(serviceQuery, indices) +If the document has a `RevocationBitmap` service identified by `serviceQuery`, +unrevoke all specified `indices`. + +**Kind**: instance method of [CoreDocument](#CoreDocument) + +| Param | Type | +| --- | --- | +| serviceQuery | [CoreDIDUrl](#CoreDIDUrl) \| string | +| indices | number \| Array.<number> | + + + +### coreDocument.toJSON() ⇒ any +Serializes this to a JSON object. + +**Kind**: instance method of [CoreDocument](#CoreDocument) + + +### coreDocument.clone() ⇒ [CoreDocument](#CoreDocument) +Deep clones the object. + +**Kind**: instance method of [CoreDocument](#CoreDocument) + + +### CoreDocument.fromJSON(json) ⇒ [CoreDocument](#CoreDocument) +Deserializes an instance from a JSON object. + +**Kind**: static method of [CoreDocument](#CoreDocument) + +| Param | Type | +| --- | --- | +| json | any | + + + +## CoreService +A DID Document Service used to enable trusted interactions associated with a DID subject. + +**Kind**: global class + +* [CoreService](#CoreService) + * [new CoreService(service)](#new_CoreService_new) + * _instance_ + * [.id()](#CoreService+id) ⇒ [CoreDIDUrl](#CoreDIDUrl) + * [.type()](#CoreService+type) ⇒ Array.<string> + * [.serviceEndpoint()](#CoreService+serviceEndpoint) ⇒ string \| Array.<string> \| Map.<string, Array.<string>> + * [.properties()](#CoreService+properties) ⇒ Map.<string, any> + * [.toJSON()](#CoreService+toJSON) ⇒ any + * [.clone()](#CoreService+clone) ⇒ [CoreService](#CoreService) + * _static_ + * [.fromJSON(json)](#CoreService.fromJSON) ⇒ [CoreService](#CoreService) + + + +### new CoreService(service) + +| Param | Type | +| --- | --- | +| service | ICoreService | + + + +### coreService.id() ⇒ [CoreDIDUrl](#CoreDIDUrl) +Returns a copy of the `CoreService` id. + +**Kind**: instance method of [CoreService](#CoreService) + + +### coreService.type() ⇒ Array.<string> +Returns a copy of the `CoreService` type. + +**Kind**: instance method of [CoreService](#CoreService) + + +### coreService.serviceEndpoint() ⇒ string \| Array.<string> \| Map.<string, Array.<string>> +Returns a copy of the `CoreService` endpoint. + +**Kind**: instance method of [CoreService](#CoreService) + + +### coreService.properties() ⇒ Map.<string, any> +Returns a copy of the custom properties on the `CoreService`. + +**Kind**: instance method of [CoreService](#CoreService) + + +### coreService.toJSON() ⇒ any +Serializes this to a JSON object. + +**Kind**: instance method of [CoreService](#CoreService) + + +### coreService.clone() ⇒ [CoreService](#CoreService) +Deep clones the object. + +**Kind**: instance method of [CoreService](#CoreService) + + +### CoreService.fromJSON(json) ⇒ [CoreService](#CoreService) +Deserializes an instance from a JSON object. + +**Kind**: static method of [CoreService](#CoreService) + +| Param | Type | +| --- | --- | +| json | any | + + + +## CoreVerificationMethod +**Kind**: global class + +* [CoreVerificationMethod](#CoreVerificationMethod) + * [new CoreVerificationMethod(did, keyType, publicKey, fragment)](#new_CoreVerificationMethod_new) + * _instance_ + * [.id()](#CoreVerificationMethod+id) ⇒ [CoreDIDUrl](#CoreDIDUrl) + * [.controller()](#CoreVerificationMethod+controller) ⇒ [CoreDID](#CoreDID) + * [.setController(did)](#CoreVerificationMethod+setController) + * [.type()](#CoreVerificationMethod+type) ⇒ [MethodType](#MethodType) + * [.data()](#CoreVerificationMethod+data) ⇒ [MethodData](#MethodData) + * [.toJSON()](#CoreVerificationMethod+toJSON) ⇒ any + * [.clone()](#CoreVerificationMethod+clone) ⇒ [CoreVerificationMethod](#CoreVerificationMethod) + * _static_ + * [.fromJSON(json)](#CoreVerificationMethod.fromJSON) ⇒ [CoreVerificationMethod](#CoreVerificationMethod) + + + +### new CoreVerificationMethod(did, keyType, publicKey, fragment) +Creates a new `CoreVerificationMethod` from the given `did` and public key. + + +| Param | Type | +| --- | --- | +| did | [CoreDID](#CoreDID) | +| keyType | number | +| publicKey | Uint8Array | +| fragment | string | + + + +### coreVerificationMethod.id() ⇒ [CoreDIDUrl](#CoreDIDUrl) +Returns a copy of the `CoreDIDUrl` of the `CoreVerificationMethod`'s `id`. + +**Kind**: instance method of [CoreVerificationMethod](#CoreVerificationMethod) + + +### coreVerificationMethod.controller() ⇒ [CoreDID](#CoreDID) +Returns a copy of the `controller` `DID` of the `CoreVerificationMethod`. + +**Kind**: instance method of [CoreVerificationMethod](#CoreVerificationMethod) + + +### coreVerificationMethod.setController(did) +Sets the `controller` `DID` of the `CoreVerificationMethod` object. + +**Kind**: instance method of [CoreVerificationMethod](#CoreVerificationMethod) + +| Param | Type | +| --- | --- | +| did | [CoreDID](#CoreDID) | + + + +### coreVerificationMethod.type() ⇒ [MethodType](#MethodType) +Returns a copy of the `CoreVerificationMethod` type. + +**Kind**: instance method of [CoreVerificationMethod](#CoreVerificationMethod) + + +### coreVerificationMethod.data() ⇒ [MethodData](#MethodData) +Returns a copy of the `CoreVerificationMethod` public key data. + +**Kind**: instance method of [CoreVerificationMethod](#CoreVerificationMethod) + + +### coreVerificationMethod.toJSON() ⇒ any +Serializes this to a JSON object. + +**Kind**: instance method of [CoreVerificationMethod](#CoreVerificationMethod) + + +### coreVerificationMethod.clone() ⇒ [CoreVerificationMethod](#CoreVerificationMethod) +Deep clones the object. + +**Kind**: instance method of [CoreVerificationMethod](#CoreVerificationMethod) + + +### CoreVerificationMethod.fromJSON(json) ⇒ [CoreVerificationMethod](#CoreVerificationMethod) +Deserializes an instance from a JSON object. + +**Kind**: static method of [CoreVerificationMethod](#CoreVerificationMethod) + +| Param | Type | +| --- | --- | +| json | any | + ## Credential @@ -4891,7 +5626,7 @@ Deep clones the object. ### StardustDID.METHOD ⇒ string -The IOTA UTXO DID method name (`"stardust"`). +The IOTA UTXO DID method name (`"iota"`). **Kind**: static property of [StardustDID](#StardustDID) @@ -4905,7 +5640,7 @@ The default Tangle network (`"main"`). ### StardustDID.placeholder(network) ⇒ [StardustDID](#StardustDID) Creates a new placeholder [`StardustDID`] with the given network name. -E.g. `did:stardust:smr:0x0000000000000000000000000000000000000000000000000000000000000000`. +E.g. `did:iota:smr:0x0000000000000000000000000000000000000000000000000000000000000000`. **Kind**: static method of [StardustDID](#StardustDID) @@ -6279,6 +7014,10 @@ This is possible because Ed25519 is birationally equivalent to Curve25519 used b | --- | --- | | publicKey | Uint8Array | + + +## StateMetadataEncoding +**Kind**: global variable ## StatusCheck @@ -6375,10 +7114,6 @@ Supported types representing a DID that can be generated by the storage interfac ## DIDMessageEncoding **Kind**: global variable - - -## StateMetadataEncoding -**Kind**: global variable ## start() diff --git a/bindings/wasm/src/did/mod.rs b/bindings/wasm/src/did/mod.rs index 27eafdde20..00bdcd6ff0 100644 --- a/bindings/wasm/src/did/mod.rs +++ b/bindings/wasm/src/did/mod.rs @@ -27,6 +27,10 @@ pub use self::wasm_verification_method::WasmVerificationMethod; pub use self::wasm_verifier_options::WasmVerifierOptions; mod wasm_core_did; +mod wasm_core_document; +mod wasm_core_service; +mod wasm_core_url; +mod wasm_core_verification_method; mod wasm_did_url; mod wasm_diff_message; mod wasm_document; diff --git a/bindings/wasm/src/did/wasm_core_did.rs b/bindings/wasm/src/did/wasm_core_did.rs index f05069b428..a505b8f2d9 100644 --- a/bindings/wasm/src/did/wasm_core_did.rs +++ b/bindings/wasm/src/did/wasm_core_did.rs @@ -2,27 +2,116 @@ // SPDX-License-Identifier: Apache-2.0 use identity_iota::did::CoreDID; +use identity_iota::did::DID; use wasm_bindgen::prelude::*; +use crate::did::wasm_core_url::WasmCoreDIDUrl; use crate::did::WasmIotaDID; use crate::error::Result; use crate::error::WasmResult; -/// A Decentralized Identifier (DID). +/// A method-agnostic Decentralized Identifier (DID). #[wasm_bindgen(js_name = CoreDID, inspectable)] pub struct WasmCoreDID(pub(crate) CoreDID); #[wasm_bindgen(js_class = CoreDID)] impl WasmCoreDID { - /// Parses a [`CoreDID`] from the given `input`. + /// Parses a `CoreDID` from the given `input`. /// - /// # Errors + /// ### Errors /// - /// Returns `Err` if the input is not a valid [`CoreDID`]. + /// Throws an error if the input is not a valid `CoreDID`. + #[wasm_bindgen] pub fn parse(input: &str) -> Result { CoreDID::parse(input).wasm_result().map(Self) } + /// Set the method name of the `CoreDID`. + #[wasm_bindgen(js_name = "setMethodName")] + pub fn set_method_name(&mut self, value: String) -> Result<()> { + self.0.set_method_name(&value).wasm_result() + } + + /// Validates whether a string is a valid DID method name. + #[wasm_bindgen(js_name = "validMethodName")] + pub fn valid_method_name(value: String) -> bool { + CoreDID::valid_method_name(&value).is_ok() + } + + /// Set the method-specific-id of the `DID`. + #[wasm_bindgen(js_name = "setMethodId")] + pub fn set_method_id(&mut self, value: String) -> Result<()> { + self.0.set_method_id(&value).wasm_result() + } + + /// Validates whether a string is a valid `DID` method-id. + #[wasm_bindgen(js_name = "validMethodId")] + pub fn valid_method_id(value: String) -> bool { + CoreDID::valid_method_id(&value).is_ok() + } + + // =========================================================================== + // DID trait + // =========================================================================== + + /// Returns the `CoreDID` scheme. + /// + /// E.g. + /// - `"did:example:12345678" -> "did"` + /// - `"did:iota:smr:12345678" -> "did"` + #[wasm_bindgen] + pub fn scheme(&self) -> String { + self.0.scheme().to_owned() + } + + /// Returns the `CoreDID` authority: the method name and method-id. + /// + /// E.g. + /// - `"did:example:12345678" -> "example:12345678"` + /// - `"did:iota:smr:12345678" -> "iota:smr:12345678"` + #[wasm_bindgen] + pub fn authority(&self) -> String { + self.0.authority().to_owned() + } + + /// Returns the `CoreDID` method name. + /// + /// E.g. + /// - `"did:example:12345678" -> "example"` + /// - `"did:iota:smr:12345678" -> "iota"` + #[wasm_bindgen] + pub fn method(&self) -> String { + self.0.method().to_owned() + } + + /// Returns the `CoreDID` method-specific ID. + /// + /// E.g. + /// - `"did:example:12345678" -> "12345678"` + /// - `"did:iota:smr:12345678" -> "smr:12345678"` + #[wasm_bindgen(js_name = methodId)] + pub fn method_id(&self) -> String { + self.0.method_id().to_owned() + } + + /// Construct a new `CoreDIDUrl` by joining with a relative DID Url string. + #[wasm_bindgen] + pub fn join(&self, segment: &str) -> Result { + self.0.clone().join(segment).wasm_result().map(WasmCoreDIDUrl) + } + + /// Clones the `CoreDID` into a `CoreDIDUrl`. + #[wasm_bindgen(js_name = toUrl)] + pub fn to_url(&self) -> WasmCoreDIDUrl { + WasmCoreDIDUrl::from(self.0.to_url()) + } + + /// Converts the `CoreDID` into a `CoreDIDUrl`, consuming it. + #[wasm_bindgen(js_name = intoUrl)] + pub fn into_url(self) -> WasmCoreDIDUrl { + WasmCoreDIDUrl::from(self.0.into_url()) + } + /// Returns the `CoreDID` as a string. #[allow(clippy::inherent_to_string)] #[wasm_bindgen(js_name = toString)] diff --git a/bindings/wasm/src/did/wasm_core_document.rs b/bindings/wasm/src/did/wasm_core_document.rs new file mode 100644 index 0000000000..ac30069e2e --- /dev/null +++ b/bindings/wasm/src/did/wasm_core_document.rs @@ -0,0 +1,516 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use super::WasmCoreDID; +use crate::common::ArrayString; +use crate::common::MapStringAny; +use crate::common::OptionOneOrManyString; +use crate::common::UOneOrManyNumber; +use crate::crypto::WasmProofOptions; +use crate::did::wasm_core_service::WasmCoreService; +use crate::did::wasm_core_url::WasmCoreDIDUrl; +use crate::did::wasm_core_verification_method::WasmCoreVerificationMethod; +use crate::did::RefMethodScope; +use crate::did::WasmMethodRelationship; +use crate::did::WasmMethodScope; +use crate::did::WasmVerifierOptions; +use crate::error::Result; +use crate::error::WasmResult; +use identity_iota::core::Object; +use identity_iota::core::OneOrMany; +use identity_iota::core::OneOrSet; +use identity_iota::core::OrderedSet; +use identity_iota::core::Url; +use identity_iota::crypto::PrivateKey; +use identity_iota::crypto::ProofOptions; +use identity_iota::did::verifiable::VerifiableProperties; +use identity_iota::did::CoreDID; +use identity_iota::did::CoreDocument; +use identity_iota::did::Document; +use identity_iota::did::MethodRef; +use identity_iota::did::MethodScope; +use identity_iota::did::Service; +use identity_iota::did::VerificationMethod; + +use proc_typescript::typescript; +use wasm_bindgen::prelude::*; +use wasm_bindgen::JsCast; + +/// A method-agnostic DID Document. +#[wasm_bindgen(js_name = CoreDocument, inspectable)] +pub struct WasmCoreDocument(pub(crate) CoreDocument); + +#[wasm_bindgen(js_class = CoreDocument)] +impl WasmCoreDocument { + /// Creates a new `CoreDocument` with the given properties. + #[wasm_bindgen(constructor)] + pub fn new(values: ICoreDocument) -> Result { + let core_doc: CoreDocument = values.into_serde().wasm_result()?; + Ok(WasmCoreDocument(core_doc)) + } + + /// Returns a copy of the DID Document `id`. + #[wasm_bindgen] + pub fn id(&self) -> WasmCoreDID { + WasmCoreDID::from(self.0.id().clone()) + } + + /// Sets the DID of the document. + #[wasm_bindgen(js_name = setId)] + pub fn set_id(&mut self, id: &WasmCoreDID) { + *self.0.id_mut() = id.0.clone(); + } + + /// Returns a copy of the document controllers. + #[wasm_bindgen] + pub fn controller(&self) -> ArrayCoreDID { + match self.0.controller() { + Some(controllers) => controllers + .iter() + .cloned() + .map(WasmCoreDID::from) + .map(JsValue::from) + .collect::() + .unchecked_into::(), + None => js_sys::Array::new().unchecked_into::(), + } + } + + /// Sets the controllers of the DID Document. + /// + /// Note: Duplicates will be ignored. + /// Use `null` to remove all controllers. + #[wasm_bindgen(js_name = setController)] + pub fn set_controller(&mut self, controllers: &OptionOneOrManyCoreDID) -> Result<()> { + let controllers: Option> = controllers.into_serde().wasm_result()?; + let controller_set: Option> = if let Some(controllers) = controllers.map(OneOrMany::into_vec) { + if controllers.is_empty() { + None + } else { + Some(OneOrSet::try_from(OrderedSet::from_iter(controllers)).wasm_result()?) + } + } else { + None + }; + *self.0.controller_mut() = controller_set; + Ok(()) + } + + /// Returns a copy of the document's `alsoKnownAs` set. + #[wasm_bindgen(js_name = alsoKnownAs)] + pub fn also_known_as(&self) -> ArrayString { + self + .0 + .also_known_as() + .iter() + .map(|url| url.to_string()) + .map(JsValue::from) + .collect::() + .unchecked_into::() + } + + /// Sets the `alsoKnownAs` property in the DID document. + #[wasm_bindgen(js_name = setAlsoKnownAs)] + pub fn set_also_known_as(&mut self, urls: &OptionOneOrManyString) -> Result<()> { + let urls: Option> = urls.into_serde().wasm_result()?; + let mut urls_set: OrderedSet = OrderedSet::new(); + if let Some(urls) = urls { + for url in urls.into_vec() { + urls_set.append(Url::parse(url).wasm_result()?); + } + } + *self.0.also_known_as_mut() = urls_set; + Ok(()) + } + + /// Returns a copy of the document's `verificationMethod` set. + #[wasm_bindgen(js_name = verificatonMethod)] + pub fn verification_method(&self) -> ArrayCoreVerificationMethod { + self + .0 + .verification_method() + .iter() + .cloned() + .map(WasmCoreVerificationMethod::from) + .map(JsValue::from) + .collect::() + .unchecked_into::() + } + + /// Returns a copy of the document's `authentication` set. + #[wasm_bindgen] + pub fn authentication(&self) -> ArrayCoreMethodRef { + self + .0 + .authentication() + .iter() + .cloned() + .map(|method_ref| match method_ref { + MethodRef::Embed(verification_method) => JsValue::from(WasmCoreVerificationMethod(verification_method)), + MethodRef::Refer(did_url) => JsValue::from(WasmCoreDIDUrl(did_url)), + }) + .collect::() + .unchecked_into::() + } + + /// Returns a copy of the document's `assertionMethod` set. + #[wasm_bindgen(js_name = assertionMethod)] + pub fn assertion_method(&self) -> ArrayCoreMethodRef { + self + .0 + .assertion_method() + .iter() + .cloned() + .map(|method_ref| match method_ref { + MethodRef::Embed(verification_method) => JsValue::from(WasmCoreVerificationMethod(verification_method)), + MethodRef::Refer(did_url) => JsValue::from(WasmCoreDIDUrl(did_url)), + }) + .collect::() + .unchecked_into::() + } + + /// Returns a copy of the document's `keyAgreement` set. + #[wasm_bindgen(js_name = keyAgreement)] + pub fn key_agreement(&self) -> ArrayCoreMethodRef { + self + .0 + .key_agreement() + .iter() + .cloned() + .map(|method_ref| match method_ref { + MethodRef::Embed(verification_method) => JsValue::from(WasmCoreVerificationMethod(verification_method)), + MethodRef::Refer(did_url) => JsValue::from(WasmCoreDIDUrl(did_url)), + }) + .collect::() + .unchecked_into::() + } + + /// Returns a copy of the document's `capabilityDelegation` set. + #[wasm_bindgen(js_name = capabilityDelegation)] + pub fn capability_delegation(&self) -> ArrayCoreMethodRef { + self + .0 + .capability_delegation() + .iter() + .cloned() + .map(|method_ref| match method_ref { + MethodRef::Embed(verification_method) => JsValue::from(WasmCoreVerificationMethod(verification_method)), + MethodRef::Refer(did_url) => JsValue::from(WasmCoreDIDUrl(did_url)), + }) + .collect::() + .unchecked_into::() + } + + /// Returns a copy of the document's `capabilityInvocation` set. + #[wasm_bindgen(js_name = capabilityInvocation)] + pub fn capability_invocation(&self) -> ArrayCoreMethodRef { + self + .0 + .capability_invocation() + .iter() + .cloned() + .map(|method_ref| match method_ref { + MethodRef::Embed(verification_method) => JsValue::from(WasmCoreVerificationMethod(verification_method)), + MethodRef::Refer(did_url) => JsValue::from(WasmCoreDIDUrl(did_url)), + }) + .collect::() + .unchecked_into::() + } + + /// Returns a copy of the custom DID Document properties. + #[wasm_bindgen] + pub fn properties(&self) -> Result { + MapStringAny::try_from(self.0.properties()) + } + + /// Sets a custom property in the DID Document. + /// If the value is set to `null`, the custom property will be removed. + /// + /// ### WARNING + /// This method can overwrite existing properties like `id` and result in an invalid document. + #[wasm_bindgen(js_name = setPropertyUnchecked)] + pub fn set_property_unchecked(&mut self, key: String, value: &JsValue) -> Result<()> { + let value: Option = value.into_serde().wasm_result()?; + match value { + Some(value) => { + self.0.properties_mut().insert(key, value); + } + None => { + self.0.properties_mut().remove(&key); + } + } + Ok(()) + } + + // =========================================================================== + // Services + // ===========================================================sdfs================ + + /// Returns a set of all {@link CoreService} in the document. + #[wasm_bindgen] + pub fn service(&self) -> ArrayCoreService { + self + .0 + .service() + .iter() + .cloned() + .map(WasmCoreService) + .map(JsValue::from) + .collect::() + .unchecked_into::() + } + + /// Add a new {@link CoreService} to the document. + /// + /// Returns `true` if the service was added. + #[wasm_bindgen(js_name = insertService)] + pub fn insert_service(&mut self, service: &WasmCoreService) -> bool { + self.0.service_mut().append(service.0.clone()) + } + + /// Remoce a {@link CoreService} identified by the given {@link CoreDIDUrl} from the document. + /// + /// Returns `true` if the service was removed. + #[wasm_bindgen(js_name = removeService)] + #[allow(non_snake_case)] + pub fn remove_service(&mut self, didUrl: &WasmCoreDIDUrl) -> bool { + self.0.service_mut().remove(&didUrl.0.clone()) + } + + /// Returns the first {@link CoreService} with an `id` property matching the provided `query`, + /// if present. + #[wasm_bindgen(js_name = resolveService)] + pub fn resolve_service(&self, query: &UCoreDIDUrlQuery) -> Option { + let service_query: String = query.into_serde().ok()?; + self + .0 + .resolve_service(&service_query) + .cloned() + .map(WasmCoreService::from) + } + + // =========================================================================== + // Verification Methods + // =========================================================================== + + /// Returns a list of all {@link CoreVerificationMethod} in the DID Document. + #[wasm_bindgen] + pub fn methods(&self) -> ArrayCoreVerificationMethod { + self + .0 + .methods() + .cloned() + .map(WasmCoreVerificationMethod::from) + .map(JsValue::from) + .collect::() + .unchecked_into::() + } + + /// Returns an array of all verification relationships. + #[wasm_bindgen(js_name = verificationRelationships)] + pub fn verification_relationships(&self) -> ArrayCoreMethodRef { + self + .0 + .verification_relationships() + .cloned() + .map(|method_ref| match method_ref { + MethodRef::Embed(verification_method) => JsValue::from(WasmCoreVerificationMethod(verification_method)), + MethodRef::Refer(did_url) => JsValue::from(WasmCoreDIDUrl(did_url)), + }) + .collect::() + .unchecked_into::() + } + + /// Adds a new `method` to the document in the given `scope`. + #[wasm_bindgen(js_name = insertMethod)] + pub fn insert_method(&mut self, method: &WasmCoreVerificationMethod, scope: &WasmMethodScope) -> Result<()> { + self.0.insert_method(method.0.clone(), scope.0).wasm_result()?; + Ok(()) + } + + /// Removes all references to the specified Verification Method. + #[wasm_bindgen(js_name = removeMethod)] + pub fn remove_method(&mut self, did: &WasmCoreDIDUrl) -> Result<()> { + self.0.remove_method(&did.0).wasm_result() + } + + /// Returns a copy of the first verification method with an `id` property + /// matching the provided `query` and the verification relationship + /// specified by `scope`, if present. + #[wasm_bindgen(js_name = resolveMethod)] + pub fn resolve_method( + &self, + query: &UCoreDIDUrlQuery, + scope: Option, + ) -> Result> { + let method_query: String = query.into_serde().wasm_result()?; + let method_scope: Option = scope.map(|js| js.into_serde().wasm_result()).transpose()?; + + let method: Option<&VerificationMethod> = self.0.resolve_method(&method_query, method_scope); + Ok(method.cloned().map(WasmCoreVerificationMethod)) + } + + /// Attaches the relationship to the given method, if the method exists. + /// + /// Note: The method needs to be in the set of verification methods, + /// so it cannot be an embedded one. + #[allow(non_snake_case)] + #[wasm_bindgen(js_name = attachMethodRelationship)] + pub fn attach_method_relationship( + &mut self, + didUrl: &WasmCoreDIDUrl, + relationship: WasmMethodRelationship, + ) -> Result { + self + .0 + .attach_method_relationship(&didUrl.0, relationship.into()) + .wasm_result() + } + + /// Detaches the given relationship from the given method, if the method exists. + #[allow(non_snake_case)] + #[wasm_bindgen(js_name = detachMethodRelationship)] + pub fn detach_method_relationship( + &mut self, + didUrl: &WasmCoreDIDUrl, + relationship: WasmMethodRelationship, + ) -> Result { + self + .0 + .detach_method_relationship(&didUrl.0, relationship.into()) + .wasm_result() + } + + // =========================================================================== + // Verification + // =========================================================================== + + /// Verifies the authenticity of `data` using the target verification method. + #[wasm_bindgen(js_name = verifyData)] + pub fn verify_data(&self, data: &JsValue, options: &WasmVerifierOptions) -> Result { + let data: VerifiableProperties = data.into_serde().wasm_result()?; + Ok(self.0.verify_data(&data, &options.0).is_ok()) + } + + // =========================================================================== + // Revocation + // =========================================================================== + + /// If the document has a `RevocationBitmap` service identified by `serviceQuery`, + /// revoke all specified `indices`. + #[wasm_bindgen(js_name = revokeCredentials)] + #[allow(non_snake_case)] + pub fn revoke_credentials(&mut self, serviceQuery: &UCoreDIDUrlQuery, indices: UOneOrManyNumber) -> Result<()> { + let query: String = serviceQuery.into_serde().wasm_result()?; + let indices: OneOrMany = indices.into_serde().wasm_result()?; + + self.0.revoke_credentials(&query, indices.as_slice()).wasm_result() + } + + /// If the document has a `RevocationBitmap` service identified by `serviceQuery`, + /// unrevoke all specified `indices`. + #[wasm_bindgen(js_name = unrevokeCredentials)] + #[allow(non_snake_case)] + pub fn unrevoke_credentials(&mut self, serviceQuery: &UCoreDIDUrlQuery, indices: UOneOrManyNumber) -> Result<()> { + let query: String = serviceQuery.into_serde().wasm_result()?; + let indices: OneOrMany = indices.into_serde().wasm_result()?; + + self.0.unrevoke_credentials(&query, indices.as_slice()).wasm_result() + } + + // =========================================================================== + // Signatures + // =========================================================================== + + /// Creates a signature for the given `data` with the specified DID Document + /// Verification Method. + /// + /// NOTE: use `signSelf` or `signDocument` for DID Documents. + #[allow(non_snake_case)] + #[wasm_bindgen(js_name = signData)] + pub fn sign_data( + &self, + data: &JsValue, + privateKey: Vec, + methodQuery: &UCoreDIDUrlQuery, + options: &WasmProofOptions, + ) -> Result { + let mut data: VerifiableProperties = data.into_serde().wasm_result()?; + let private_key: PrivateKey = privateKey.into(); + let method_query: String = methodQuery.into_serde().wasm_result()?; + let options: ProofOptions = options.0.clone(); + + let signer = self.0.signer(&private_key); + let signer = signer.options(options); + let signer = signer.method(&method_query); + signer.sign(&mut data).wasm_result()?; + JsValue::from_serde(&data).wasm_result() + } +} + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(typescript_type = "ICoreDocument")] + pub type ICoreDocument; + + #[wasm_bindgen(typescript_type = "CoreDID[]")] + pub type ArrayCoreDID; + + #[wasm_bindgen(typescript_type = "CoreVerificationMethod[]")] + pub type ArrayCoreVerificationMethod; + + #[wasm_bindgen(typescript_type = "Array")] + pub type ArrayCoreMethodRef; + + #[wasm_bindgen(typescript_type = "CoreDIDUrl | string")] + pub type UCoreDIDUrlQuery; + + #[wasm_bindgen(typescript_type = "CoreDID | CoreDID[] | null")] + pub type OptionOneOrManyCoreDID; + + #[wasm_bindgen(typescript_type = "CoreService[]")] + pub type ArrayCoreService; +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +#[typescript(name = "ICoreDocument", readonly, optional)] +#[allow(non_snake_case, dead_code)] +struct ICoreDocumentHelper { + #[typescript(optional = false, type = "string | CoreDID | StardustDID")] + id: Option, + + #[typescript(type = "(string | CoreDID | StardustDID)[]")] + controller: Option>, + + #[typescript(type = "string[]")] + alsoKnownAs: Option>, + + #[typescript(type = "(CoreVerificationMethod | StardustVerificationMethod)[]")] + verificationMethod: Option>>, + + #[typescript(type = "(CoreVerificationMethod | CoreDIDUrl | StardustVerificationMethod | StardustDIDUrl)[]")] + authentication: Option>>, + + #[typescript(type = "(CoreVerificationMethod | CoreDIDUrl | StardustVerificationMethod | StardustDIDUrl)[]")] + assertionMethod: Option>>, + + #[typescript(type = "(CoreVerificationMethod | CoreDIDUrl | StardustVerificationMethod | StardustDIDUrl)[]")] + keyAgreement: Option>>, + + #[typescript(type = "(CoreVerificationMethod | CoreDIDUrl | StardustVerificationMethod | StardustDIDUrl)[]")] + capabilityDelegation: Option>>, + + #[typescript(type = "(CoreVerificationMethod | CoreDIDUrl | StardustVerificationMethod | StardustDIDUrl)[]")] + capabilityInvocation: Option>>, + + #[typescript(type = "(CoreService | StardustService)[]")] + service: Option>>, + + #[serde(flatten)] + #[typescript(optional = false, name = "[properties: string]", type = "unknown")] + properties: Object, +} + +impl_wasm_json!(WasmCoreDocument, CoreDocument); +impl_wasm_clone!(WasmCoreDocument, CoreDocument); diff --git a/bindings/wasm/src/did/wasm_core_service.rs b/bindings/wasm/src/did/wasm_core_service.rs new file mode 100644 index 0000000000..e4c3a9689f --- /dev/null +++ b/bindings/wasm/src/did/wasm_core_service.rs @@ -0,0 +1,105 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use crate::common::deserialize_map_or_any; +use crate::common::ArrayString; +use crate::common::MapStringAny; +use crate::did::wasm_core_url::WasmCoreDIDUrl; +use crate::did::IService; +use crate::did::UServiceEndpoint; +use crate::error::Result; +use crate::error::WasmResult; +use identity_iota::core::OneOrMany; +use identity_iota::did::CoreDIDUrl; +use identity_iota::did::Service; +use identity_iota::did::ServiceEndpoint; +use wasm_bindgen::prelude::*; +use wasm_bindgen::JsCast; + +/// A DID Document Service used to enable trusted interactions associated with a DID subject. +#[wasm_bindgen(js_name = CoreService, inspectable)] +pub struct WasmCoreService(pub(crate) Service); + +#[wasm_bindgen(js_class = CoreService)] +impl WasmCoreService { + #[wasm_bindgen(constructor)] + pub fn new(service: ICoreService) -> Result { + let id: CoreDIDUrl = service.id().into_serde().wasm_result()?; + + let base_service: &IService = service.as_ref(); + let types: OneOrMany = service.type_().into_serde().wasm_result()?; + let service_endpoint: ServiceEndpoint = deserialize_map_or_any(&base_service.service_endpoint())?; + let properties: Option = deserialize_map_or_any(&base_service.properties())?; + + Service::builder(properties.unwrap_or_default()) + .id(id) + .types(types) + .service_endpoint(service_endpoint) + .build() + .map(WasmCoreService) + .wasm_result() + } + + /// Returns a copy of the `CoreService` id. + #[wasm_bindgen] + pub fn id(&self) -> WasmCoreDIDUrl { + WasmCoreDIDUrl::from(self.0.id().clone()) + } + + /// Returns a copy of the `CoreService` type. + #[wasm_bindgen(js_name = type)] + pub fn type_(&self) -> ArrayString { + self + .0 + .type_() + .iter() + .cloned() + .map(JsValue::from) + .collect::() + .unchecked_into::() + } + + /// Returns a copy of the `CoreService` endpoint. + #[wasm_bindgen(js_name = serviceEndpoint)] + pub fn service_endpoint(&self) -> UServiceEndpoint { + UServiceEndpoint::from(self.0.service_endpoint()) + } + + /// Returns a copy of the custom properties on the `CoreService`. + #[wasm_bindgen] + pub fn properties(&self) -> Result { + MapStringAny::try_from(self.0.properties()) + } +} + +impl_wasm_json!(WasmCoreService, CoreService); +impl_wasm_clone!(WasmCoreService, CoreService); + +impl From for WasmCoreService { + fn from(service: Service) -> Self { + Self(service) + } +} + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(typescript_type = "ICoreService", extends = IService)] + pub type ICoreService; + + #[wasm_bindgen(method, getter)] + pub fn id(this: &ICoreService) -> JsValue; +} + +#[wasm_bindgen(typescript_custom_section)] +const I_CORE_SERVICE: &'static str = r#" +/** + * Holds options to create a new `CoreService`. + */ +interface ICoreService extends IService { + /** + * Identifier of the service. + * + * Must be a valid DIDUrl with a fragment. + */ + readonly id: CoreDIDUrl | string; +}"#; diff --git a/bindings/wasm/src/did/wasm_core_url.rs b/bindings/wasm/src/did/wasm_core_url.rs new file mode 100644 index 0000000000..f801e538a8 --- /dev/null +++ b/bindings/wasm/src/did/wasm_core_url.rs @@ -0,0 +1,105 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use crate::did::WasmCoreDID; +use crate::error::Result; +use crate::error::WasmResult; +use identity_iota::did::CoreDIDUrl; +use wasm_bindgen::prelude::*; + +/// A method agnostic DID Url. +#[wasm_bindgen(js_name = CoreDIDUrl, inspectable)] +pub struct WasmCoreDIDUrl(pub(crate) CoreDIDUrl); + +#[wasm_bindgen(js_class = CoreDIDUrl)] +impl WasmCoreDIDUrl { + /// Parses a `CoreDIDUrl` from the input string. + #[wasm_bindgen] + pub fn parse(input: &str) -> Result { + CoreDIDUrl::parse(input).map(WasmCoreDIDUrl).wasm_result() + } + + /// Return a copy of the `CoreDID` section of the `CoreDIDUrl`. + #[wasm_bindgen] + pub fn did(&self) -> WasmCoreDID { + WasmCoreDID::from(self.0.did().clone()) + } + + /// Return a copy of the relative DID Url as a string, including only the path, query, and fragment. + #[wasm_bindgen(js_name = urlStr)] + pub fn url_str(&self) -> String { + self.0.url().to_string() + } + + /// Returns a copy of the `CoreDIDUrl` method fragment, if any. Excludes the leading '#'. + #[wasm_bindgen] + pub fn fragment(&self) -> Option { + self.0.fragment().map(str::to_owned) + } + + /// Sets the `fragment` component of the `CoreDIDUrl`. + #[wasm_bindgen(js_name = setFragment)] + pub fn set_fragment(&mut self, value: Option) -> Result<()> { + self.0.set_fragment(value.as_deref()).wasm_result() + } + + /// Returns a copy of the `CoreDIDUrl` path. + #[wasm_bindgen] + pub fn path(&self) -> Option { + self.0.path().map(str::to_owned) + } + + /// Sets the `path` component of the `CoreDIDUrl`. + #[wasm_bindgen(js_name = setPath)] + pub fn set_path(&mut self, value: Option) -> Result<()> { + self.0.set_path(value.as_deref()).wasm_result() + } + + /// Returns a copy of the `CoreDIDUrl` method query, if any. Excludes the leading '?'. + #[wasm_bindgen] + pub fn query(&self) -> Option { + self.0.query().map(str::to_owned) + } + + /// Sets the `query` component of the `CoreDIDUrl`. + #[wasm_bindgen(js_name = setQuery)] + pub fn set_query(&mut self, value: Option) -> Result<()> { + self.0.set_query(value.as_deref()).wasm_result() + } + + /// Append a string representing a path, query, and/or fragment, returning a new `CoreDIDUrl`. + /// + /// Must begin with a valid delimiter character: '/', '?', '#'. Overwrites the existing URL + /// segment and any following segments in order of path, query, then fragment. + /// + /// I.e. + /// - joining a path will clear the query and fragment. + /// - joining a query will clear the fragment. + /// - joining a fragment will only overwrite the fragment. + #[wasm_bindgen] + pub fn join(&self, segment: &str) -> Result { + self.0.join(segment).map(WasmCoreDIDUrl::from).wasm_result() + } + + /// Returns the `CoreDIDUrl` as a string. + #[allow(clippy::inherent_to_string)] + #[wasm_bindgen(js_name = toString)] + pub fn to_string(&self) -> String { + self.0.to_string() + } +} + +impl_wasm_json!(WasmCoreDIDUrl, CoreDIDUrl); +impl_wasm_clone!(WasmCoreDIDUrl, CoreDIDUrl); + +impl From for WasmCoreDIDUrl { + fn from(did_url: CoreDIDUrl) -> Self { + Self(did_url) + } +} + +impl From for CoreDIDUrl { + fn from(wasm_did_url: WasmCoreDIDUrl) -> Self { + wasm_did_url.0 + } +} diff --git a/bindings/wasm/src/did/wasm_core_verification_method.rs b/bindings/wasm/src/did/wasm_core_verification_method.rs new file mode 100644 index 0000000000..9398c417f5 --- /dev/null +++ b/bindings/wasm/src/did/wasm_core_verification_method.rs @@ -0,0 +1,120 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use crate::common::MapStringAny; +use crate::crypto::WasmKeyType; +use crate::did::wasm_core_url::WasmCoreDIDUrl; +use crate::did::WasmCoreDID; +use crate::did::WasmMethodData; +use crate::did::WasmMethodType; +use crate::error::Result; +use crate::error::WasmResult; +use identity_iota::crypto::PublicKey; +use identity_iota::did::VerificationMethod; +use wasm_bindgen::prelude::*; + +/// A DID Document Verification Method. +#[wasm_bindgen(js_name = CoreVerificationMethod, inspectable)] +pub struct WasmCoreVerificationMethod(pub(crate) VerificationMethod); + +#[wasm_bindgen(js_class = CoreVerificationMethod)] +impl WasmCoreVerificationMethod { + /// Creates a new `CoreVerificationMethod` from the given `did` and public key. + #[allow(non_snake_case)] + #[wasm_bindgen(constructor)] + pub fn new( + did: &WasmCoreDID, + keyType: WasmKeyType, + publicKey: Vec, + fragment: String, + ) -> Result { + let public_key: PublicKey = PublicKey::from(publicKey); + VerificationMethod::new(did.0.clone(), keyType.into(), &public_key, &fragment) + .map(Self) + .wasm_result() + } + + /// Returns a copy of the `CoreDIDUrl` of the `CoreVerificationMethod`'s `id`. + #[wasm_bindgen] + pub fn id(&self) -> WasmCoreDIDUrl { + WasmCoreDIDUrl::from(self.0.id().clone()) + } + + /// Sets the id of the `CoreVerificationMethod`. + #[wasm_bindgen(js_name = setId)] + pub fn set_id(&mut self, id: &WasmCoreDIDUrl) -> Result<()> { + self.0.set_id(id.0.clone()).wasm_result()?; + Ok(()) + } + + /// Returns a copy of the `controller` `DID` of the `CoreVerificationMethod`. + #[wasm_bindgen] + pub fn controller(&self) -> WasmCoreDID { + WasmCoreDID::from(self.0.controller().clone()) + } + + /// Sets the `controller` `DID` of the `CoreVerificationMethod` object. + #[wasm_bindgen(js_name = setController)] + pub fn set_controller(&mut self, did: &WasmCoreDID) { + *self.0.controller_mut() = did.0.clone(); + } + + /// Returns a copy of the `CoreVerificationMethod` type. + #[wasm_bindgen(js_name = type)] + pub fn type_(&self) -> WasmMethodType { + WasmMethodType::from(self.0.type_()) + } + + /// Sets the `CoreVerificationMethod` type. + #[wasm_bindgen(js_name = setType)] + pub fn set_type(&mut self, type_: &WasmMethodType) { + *self.0.type_mut() = type_.0; + } + + /// Returns a copy of the `CoreVerificationMethod` public key data. + #[wasm_bindgen] + pub fn data(&self) -> WasmMethodData { + WasmMethodData::from(self.0.data().clone()) + } + + /// Sets `CoreVerificationMethod` public key data. + #[wasm_bindgen(js_name = setData)] + pub fn set_data(&mut self, data: &WasmMethodData) { + *self.0.data_mut() = data.0.clone(); + } + + /// Get custom properties of the Verification Method. + #[wasm_bindgen] + pub fn properties(&self) -> Result { + MapStringAny::try_from(self.0.properties()) + } + + /// Adds a custom property to the Verification Method. + /// If the value is set to `null`, the custom property will be removed. + /// + /// ### WARNING + /// This method can overwrite existing properties like `id` and result + /// in an invalid Verification Method. + #[wasm_bindgen(js_name = setPropertyUnchecked)] + pub fn set_property_unchecked(&mut self, key: String, value: &JsValue) -> Result<()> { + let value: Option = value.into_serde().wasm_result()?; + match value { + Some(value) => { + self.0.properties_mut().insert(key, value); + } + None => { + self.0.properties_mut().remove(&key); + } + } + Ok(()) + } +} + +impl_wasm_json!(WasmCoreVerificationMethod, CoreVerificationMethod); +impl_wasm_clone!(WasmCoreVerificationMethod, CoreVerificationMethod); + +impl From for WasmCoreVerificationMethod { + fn from(method: VerificationMethod) -> Self { + Self(method) + } +} diff --git a/bindings/wasm/src/did/wasm_verification_method.rs b/bindings/wasm/src/did/wasm_verification_method.rs index 30b94a276e..7079c631ba 100644 --- a/bindings/wasm/src/did/wasm_verification_method.rs +++ b/bindings/wasm/src/did/wasm_verification_method.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use identity_iota::crypto::PublicKey; + use identity_iota::iota_core::IotaVerificationMethod; use wasm_bindgen::prelude::*; diff --git a/bindings/wasm/src/stardust/stardust_document.rs b/bindings/wasm/src/stardust/stardust_document.rs index a342ac9d4d..9d5cf625a5 100644 --- a/bindings/wasm/src/stardust/stardust_document.rs +++ b/bindings/wasm/src/stardust/stardust_document.rs @@ -175,7 +175,7 @@ impl WasmStardustDocument { self.0.insert_service(service.0.clone()) } - /// Remove a {@link StardustService} identified by the given {@link DIDUrl} from the document. + /// Remove a {@link StardustService} identified by the given {@link StardustDIDUrl} from the document. /// /// Returns `true` if a service was removed. #[wasm_bindgen(js_name = removeService)] diff --git a/bindings/wasm/src/stardust/stardust_verification_method.rs b/bindings/wasm/src/stardust/stardust_verification_method.rs index 47c3d7b9d9..0bff7e189f 100644 --- a/bindings/wasm/src/stardust/stardust_verification_method.rs +++ b/bindings/wasm/src/stardust/stardust_verification_method.rs @@ -1,6 +1,7 @@ // Copyright 2020-2022 Stiftung // SPDX-License-Identifier: Apache-2.0 +use crate::common::MapStringAny; use identity_iota::crypto::PublicKey; use identity_stardust::StardustVerificationMethod; use wasm_bindgen::prelude::*; @@ -33,12 +34,19 @@ impl WasmStardustVerificationMethod { .wasm_result() } - /// Returns a reference to the `StardustVerificationMethod` id. + /// Returns a copy of the `StardustVerificationMethod` id. #[wasm_bindgen] pub fn id(&self) -> WasmStardustDIDUrl { WasmStardustDIDUrl::from(self.0.id().clone()) } + /// Sets the id of the `StardustVerificationMethod`. + #[wasm_bindgen(js_name = setId)] + pub fn set_id(&mut self, id: &WasmStardustDIDUrl) -> Result<()> { + self.0.set_id(id.0.clone()).wasm_result()?; + Ok(()) + } + /// Returns a copy of the `controller` `DID` of the `StardustVerificationMethod`. #[wasm_bindgen] pub fn controller(&self) -> WasmStardustDID { @@ -57,11 +65,49 @@ impl WasmStardustVerificationMethod { WasmMethodType::from(self.0.type_()) } + /// Sets the `StardustVerificationMethod` type. + #[wasm_bindgen(js_name = setType)] + pub fn set_type(&mut self, type_: &WasmMethodType) { + *self.0.type_mut() = type_.0; + } + /// Returns a copy of the `StardustVerificationMethod` public key data. #[wasm_bindgen] pub fn data(&self) -> WasmMethodData { WasmMethodData::from(self.0.data().clone()) } + + /// Sets `StardustVerificationMethod` public key data. + #[wasm_bindgen(js_name = setData)] + pub fn set_data(&mut self, data: &WasmMethodData) { + *self.0.data_mut() = data.0.clone(); + } + + /// Get custom properties of the Verification Method. + #[wasm_bindgen] + pub fn properties(&self) -> Result { + MapStringAny::try_from(self.0.properties()) + } + + /// Adds a custom property to the Verification Method. + /// If the value is set to `null`, the custom property will be removed. + /// + /// ### WARNING + /// This method can overwrite existing properties like `id` and result + /// in an invalid Verification Method. + #[wasm_bindgen(js_name = setPropertyUnchecked)] + pub fn set_property_unchecked(&mut self, key: String, value: &JsValue) -> Result<()> { + let value: Option = value.into_serde().wasm_result()?; + match value { + Some(value) => { + self.0.properties_mut().insert(key, value); + } + None => { + self.0.properties_mut().remove(&key); + } + } + Ok(()) + } } impl_wasm_json!(WasmStardustVerificationMethod, StardustVerificationMethod); diff --git a/bindings/wasm/tests/core.ts b/bindings/wasm/tests/core.ts new file mode 100644 index 0000000000..1937ba2021 --- /dev/null +++ b/bindings/wasm/tests/core.ts @@ -0,0 +1,279 @@ +export {}; + +const assert = require('assert'); +const { + CoreDID, + CoreDocument, + CoreService, + CoreVerificationMethod, + KeyType, + MethodRelationship, + MethodScope, + MethodType, +} = require("../node"); + +const VALID_DID_KEY = "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"; +const VALID_DID_EXAMPLE = "did:example:123"; +const KEY_BYTES = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32]); + +describe('CoreDID', function () { + describe('#parse', function () { + it('iota', () => { + let tag = "0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"; + const didStr = "did:iota:smr:" + tag; + const did = CoreDID.parse(didStr); + assert.deepStrictEqual(did.toString(), didStr); + assert.deepStrictEqual(did.method(), "iota"); + assert.deepStrictEqual(did.authority(), "iota:smr:" + tag); + assert.deepStrictEqual(did.methodId(), "smr:" + tag); + assert.deepStrictEqual(did.scheme(), "did"); + }); + it('key', () => { + const tag = "z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"; + const didStr = "did:key:" + tag; + const did = CoreDID.parse(didStr); + assert.deepStrictEqual(did.toString(), didStr); + assert.deepStrictEqual(did.method(), "key"); + assert.deepStrictEqual(did.authority(), "key:" + tag); + assert.deepStrictEqual(did.methodId(), tag); + assert.deepStrictEqual(did.scheme(), "did"); + }); + it('example', () => { + const tag = "123"; + const didStr = VALID_DID_EXAMPLE; + const did = CoreDID.parse(didStr); + assert.deepStrictEqual(did.toString(), didStr); + assert.deepStrictEqual(did.method(), "example"); + assert.deepStrictEqual(did.authority(), "example:" + tag); + assert.deepStrictEqual(did.methodId(), tag); + assert.deepStrictEqual(did.scheme(), "did"); + }); + }); + describe('#setMethodId', function () { + it('should work', () => { + let didStr = "did:example:network:123"; + const did = CoreDID.parse(didStr); + did.setMethodId("abc"); + assert.deepStrictEqual(did.toString(), "did:example:abc"); + assert.deepStrictEqual(did.method(), "example"); + assert.deepStrictEqual(did.authority(), "example:abc"); + assert.deepStrictEqual(did.methodId(), "abc"); + }); + }); + describe('#validMethodId', function () { + it('should work', () => { + // Valid + assert.deepStrictEqual(CoreDID.validMethodId("abc"), true); + assert.deepStrictEqual(CoreDID.validMethodId("network:123"), true); + assert.deepStrictEqual(CoreDID.validMethodId("network:shard:123"), true); + // Invalid + assert.deepStrictEqual(CoreDID.validMethodId(" "), false); + assert.deepStrictEqual(CoreDID.validMethodId("abc[brackets]"), false); + }); + }); + describe('#setMethodName', function () { + it('should work', () => { + let didStr = "did:example:network:123"; + const did = CoreDID.parse(didStr); + did.setMethodName("other"); + assert.deepStrictEqual(did.toString(), "did:other:network:123"); + assert.deepStrictEqual(did.method(), "other"); + assert.deepStrictEqual(did.authority(), "other:network:123"); + assert.deepStrictEqual(did.methodId(), "network:123"); + }); + }); + describe('#validMethodName', function () { + it('should work', () => { + // Valid + assert.deepStrictEqual(CoreDID.validMethodId("abc"), true); + assert.deepStrictEqual(CoreDID.validMethodId("example"), true); + assert.deepStrictEqual(CoreDID.validMethodId("method123"), true); + // Invalid + assert.deepStrictEqual(CoreDID.validMethodId(" "), false); + assert.deepStrictEqual(CoreDID.validMethodId("method[brackets]"), false); + }); + }); +}); + +describe('CoreDocument', function () { + describe('#new', function () { + it('minimal should work', () => { + const doc = new CoreDocument({ + id: VALID_DID_EXAMPLE, + }); + assert.deepStrictEqual(doc.id().toString(), VALID_DID_EXAMPLE); + assert.deepStrictEqual(doc.controller(), []); + assert.deepStrictEqual(doc.alsoKnownAs(), []); + assert.deepStrictEqual(doc.verificatonMethod(), []); + assert.deepStrictEqual(doc.assertionMethod(), []); + assert.deepStrictEqual(doc.authentication(), []); + assert.deepStrictEqual(doc.capabilityDelegation(), []); + assert.deepStrictEqual(doc.capabilityInvocation(), []); + assert.deepStrictEqual(doc.keyAgreement(), []); + assert.deepStrictEqual(doc.methods(), []); + assert.deepStrictEqual(doc.service(), []); + assert.deepStrictEqual(doc.properties(), new Map()); + }); + it('full should work', () => { + const did = CoreDID.parse(VALID_DID_EXAMPLE); + const method0 = new CoreVerificationMethod(did, KeyType.Ed25519, KEY_BYTES, "key-0"); + const method1 = new CoreVerificationMethod(did, KeyType.Ed25519, KEY_BYTES, "key-1"); + const method2 = new CoreVerificationMethod(CoreDID.parse(VALID_DID_EXAMPLE), KeyType.Ed25519, KEY_BYTES, "key-2"); + const service = new CoreService({ + id: did.join('#service-1'), + type: "LinkedDomains", + serviceEndpoint: "https://example.com/", + }); + + const doc = new CoreDocument({ + id: VALID_DID_EXAMPLE, + controller: [VALID_DID_KEY, VALID_DID_EXAMPLE], + alsoKnownAs: [VALID_DID_KEY], + verificationMethod: [method0, method1], + assertionMethod: [method0.id()], + authentication: [method2, method0.id()], + keyAgreement: [method1.id()], + capabilityDelegation: [method0.id(), method1.id()], + capabilityInvocation: [method1.id(), method0.id()], + service: [service], + custom1: "asdf", + custom2: 1234, + }); + assert.deepStrictEqual(doc.id().toString(), did.toString()); + assert.deepStrictEqual(doc.controller().map((item: any) => item.toString()), [VALID_DID_KEY, VALID_DID_EXAMPLE]); + assert.deepStrictEqual(doc.alsoKnownAs(), [VALID_DID_KEY]); + assert.deepStrictEqual(doc.verificatonMethod().length, 2); + assert.deepStrictEqual(doc.verificatonMethod()[0].toJSON(), method0.toJSON()); + assert.deepStrictEqual(doc.verificatonMethod()[1].toJSON(), method1.toJSON()); + assert.deepStrictEqual(doc.assertionMethod().length, 1); + assert.deepStrictEqual(doc.assertionMethod()[0].toString(), method0.id().toString()); + assert.deepStrictEqual(doc.authentication().length, 2); + assert.deepStrictEqual(doc.authentication()[0].toJSON(), method2.toJSON()); + assert.deepStrictEqual(doc.authentication()[1].toString(), method0.id().toString()); + assert.deepStrictEqual(doc.capabilityDelegation().length, 2); + assert.deepStrictEqual(doc.capabilityDelegation()[0].toString(), method0.id().toString()); + assert.deepStrictEqual(doc.capabilityDelegation()[1].toString(), method1.id().toString()); + assert.deepStrictEqual(doc.capabilityInvocation().length, 2); + assert.deepStrictEqual(doc.capabilityInvocation()[0].toString(), method1.id().toString()); + assert.deepStrictEqual(doc.capabilityInvocation()[1].toString(), method0.id().toString()); + assert.deepStrictEqual(doc.keyAgreement().length, 1); + assert.deepStrictEqual(doc.keyAgreement()[0].toString(), method1.id().toString()); + assert.deepStrictEqual(doc.methods().length, 3); + assert.deepStrictEqual(doc.methods()[0].toJSON(), method0.toJSON()); + assert.deepStrictEqual(doc.methods()[1].toJSON(), method1.toJSON()); + assert.deepStrictEqual(doc.methods()[2].toJSON(), method2.toJSON()); + assert.deepStrictEqual(doc.service().length, 1); + assert.deepStrictEqual(doc.service()[0].toJSON(), service.toJSON()); + const properties = new Map() + properties.set("custom1", "asdf"); + properties.set("custom2", 1234); + assert.deepStrictEqual(doc.properties(), properties); + }); + }); + describe('#insert/resolve/removeMethod', function () { + it('should work', async () => { + const doc = new CoreDocument({ + id: VALID_DID_EXAMPLE, + }); + const fragment = "new-method-1"; + const scope = MethodScope.AssertionMethod(); + const method = new CoreVerificationMethod(doc.id(), KeyType.Ed25519, KEY_BYTES, fragment); + + // Add. + doc.insertMethod(method, scope); + // Resolve. + const resolved = doc.resolveMethod(fragment, scope); + assert.deepStrictEqual(resolved.id().fragment(), fragment); + assert.deepStrictEqual(resolved.type().toString(), MethodType.Ed25519VerificationKey2018().toString()); + assert.deepStrictEqual(resolved.controller().toString(), doc.id().toString()); + assert.deepStrictEqual(resolved.data().tryDecode(), KEY_BYTES); + assert.deepStrictEqual(resolved.toJSON(), method.toJSON()); + assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.VerificationMethod()), undefined); + // List. + const list = doc.methods(); + assert.deepStrictEqual(list.length, 1); + assert.deepStrictEqual(list[0].toJSON(), resolved.toJSON()); + // Remove. + doc.removeMethod(resolved.id()); + assert.deepStrictEqual(doc.resolveMethod(fragment), undefined); + assert.deepStrictEqual(doc.resolveMethod(fragment, scope), undefined); + assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.VerificationMethod()), undefined); + assert.deepStrictEqual(doc.methods().length, 0); + }); + }); + describe('#attach/detachMethodRelationship', function () { + it('should work', async () => { + const doc = new CoreDocument({ + id: VALID_DID_EXAMPLE, + }); + const fragment = "new-method-1"; + const method = new CoreVerificationMethod(doc.id(), KeyType.Ed25519, KEY_BYTES, fragment); + doc.insertMethod(method, MethodScope.VerificationMethod()); + assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.VerificationMethod()).toJSON(), method.toJSON()); + + // Attach. + doc.attachMethodRelationship(method.id(), MethodRelationship.Authentication); + assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.VerificationMethod()).toJSON(), method.toJSON()); + assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.Authentication()).toJSON(), method.toJSON()); + assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.AssertionMethod()), undefined); + assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.CapabilityInvocation()), undefined); + assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.CapabilityDelegation()), undefined); + assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.KeyAgreement()), undefined); + + // Detach. + doc.detachMethodRelationship(method.id(), MethodRelationship.Authentication); + assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.VerificationMethod()).toJSON(), method.toJSON()); + assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.Authentication()), undefined); + assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.AssertionMethod()), undefined); + assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.CapabilityInvocation()), undefined); + assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.CapabilityDelegation()), undefined); + assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.KeyAgreement()), undefined); + }); + }); + describe('#insert/resolve/removeService', function () { + it('should work', async () => { + const doc = new CoreDocument({ + id: VALID_DID_EXAMPLE, + }); + + // Add. + const fragment1 = "new-service-1"; + const service = new CoreService({ + id: doc.id().toUrl().join('#' + fragment1), + type: ["LinkedDomains", "ExampleType"], + serviceEndpoint: ["https://example.com/", "https://iota.org/"], + }); + doc.insertService(service); + // Resolve. + const resolved = doc.resolveService(fragment1); + assert.deepStrictEqual(resolved.id().fragment(), fragment1); + assert.deepStrictEqual(resolved.type(), ["LinkedDomains", "ExampleType"]); + assert.deepStrictEqual(resolved.serviceEndpoint(), ["https://example.com/", "https://iota.org/"]); + assert.deepStrictEqual(resolved.toJSON(), service.toJSON()); + // List. + const list = doc.service(); + assert.deepStrictEqual(list.length, 1); + assert.deepStrictEqual(list[0].toJSON(), resolved.toJSON()); + // Remove + const remove = doc.removeService(resolved.id()); + assert.deepStrictEqual(remove, true); + assert.deepStrictEqual(doc.resolveService(fragment1), undefined); + assert.deepStrictEqual(doc.service().length, 0); + }); + }); + describe('#properties', function () { + it('should work', () => { + const doc = new CoreDocument({ + id: VALID_DID_EXAMPLE, + }); + assert.deepStrictEqual(doc.properties(), new Map()); + + const properties = new Map() + properties.set("custom1", "asdf"); + properties.set("custom2", 1234); + doc.setPropertyUnchecked("custom1", "asdf"); + doc.setPropertyUnchecked("custom2", 1234); + assert.deepStrictEqual(doc.properties(), properties); + }); + }); +}); From 6991e029d5fc0c1ded1ec88108daa389f6653839 Mon Sep 17 00:00:00 2001 From: cycraig Date: Wed, 7 Sep 2022 16:41:14 +0200 Subject: [PATCH 48/89] Remove advanced concepts wiki section (#1002) * Remove outdated advanced concepts wiki section * Remove introduction references --- .../docs/concepts/advanced/did_messages.mdx | 37 ----- .../docs/concepts/advanced/overview.md | 11 -- .../concepts/advanced/storage_interface.mdx | 132 ------------------ documentation/docs/introduction.md | 4 - documentation/sidebars.js | 6 - 5 files changed, 190 deletions(-) delete mode 100644 documentation/docs/concepts/advanced/did_messages.mdx delete mode 100644 documentation/docs/concepts/advanced/overview.md delete mode 100644 documentation/docs/concepts/advanced/storage_interface.mdx diff --git a/documentation/docs/concepts/advanced/did_messages.mdx b/documentation/docs/concepts/advanced/did_messages.mdx deleted file mode 100644 index 0b00ebd3a7..0000000000 --- a/documentation/docs/concepts/advanced/did_messages.mdx +++ /dev/null @@ -1,37 +0,0 @@ ---- -title: DID Messages -sidebar_label: DID Messages -description: Learn how IOTA Identity recreates and validates the state from its origin to the current version using Integration and Differentiation Chains. -image: /img/Identity_icon.png -keywords: -- Diff Chain -- Differentiation Chain -- Integration Chain -- smart contracts -- Chronicle -- state -- stateless ---- -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -import CodeBlock from '@theme/CodeBlock'; - -TODO: Explain the concept of DID Messages and how they can be used to optimize DID updates. - -### Valid DID Documents - -Most DID methods are implemented on a Distributed Ledger Technology (DLT), such as Bitcoin, Ethereum or IOTA. Most common DID implementation on DLTs are based on fit-for-purpose Blockchains that store the state of a DID Document in the ledger, or a general purpose Blockchain that utilize smart contracts to store the state. Updating a DID Document where the state is understood by the network is straightforward. The network can determine if an action is legal and if a cryptographic signature is provided correctly, as it understands the underlying data structure, and can update the state accordingly. The individual state updates, or transactions, can be forgotten. - -The IOTA Tangle is unable to track, state, or understand the data structure. Storing the state is neither possible in the ledger, nor via a Smart contract (yet). Therefore, IOTA Identity has to recreate and validate the state from the origin of the Identity to the current version. The process involves querying all the relevant transactions from the Tangle, ordering them, filtering out the transactions that perform illegal actions or have an incorrect signature and then recreate state. As this requires the full history of the Identity, we recommend utilizing [Chronicle](https://github.com/iotaledger/chronicle.rs), an IOTA permanode, which stores the entire history of the Tangle. Further research will be performed to reduce storage requirements for IOTA Identity based applications. - -### DID Messages - -Due to this constant need for state recreating, unique performance improvements have been design and implemented for IOTA Identity. Most DID Documents will need few to no updates, however identities that sign a lot of Verifiable Credentials might update more frequently, as will be explained in the Verifiable Credentials section. To support higher frequency identity updates, we have introduced a unique solution called the “Integration Chain” and the “Differentiation Chain” (Diff Chain). - -The Integration Chain is a chain of transactions that contain full DID Documents. They are unrestricted in what they can add or remove from the DID Document. Every Integration Chain transaction points to a separate new Diff Chain. These Diff Chain transactions only list the changes to a DID Document and are therefore more compact. It is, however, restricted in rotating the signing key, making it fast and easy to validate the transaction. - -Once a new Integration chain transaction is created, it will take all Diff Chain updates and compress them into a new DID Document, essentially combining them all into a single transaction. This reduces the amount of updates that need to be queried and validated tremendously. For example, lets assume every Diff chain contains 100 updates. Then validating a DID that has done 1050 updates, only requires the validation of 10 Integration Chain updates and 40 Diff Chain updates (The latest Diff Chain). We skipped out on 10 Diff Chains each containing 100 updates, and only validated the 10 Integration Chain updates and the last Diff Chain containing 40 updates. If we estimate every update to be on average 1 Kb, we only have to download 50 kb of information and validate it, which is significantly less than the otherwise 1.025 Mb of information. - -The improved performance and ability to handle frequently updated DID Documents is especially beneficial for Verifiable Credential Revocation. - -TODO: mention future revocation scheme replacement for MerkleKeyCollection. diff --git a/documentation/docs/concepts/advanced/overview.md b/documentation/docs/concepts/advanced/overview.md deleted file mode 100644 index 3ca0522125..0000000000 --- a/documentation/docs/concepts/advanced/overview.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -title: Advanced Concepts Overview -sidebar_label: Overview -description: Provide overview of the advanced concepts -image: /img/Identity_icon.png -keywords: -- advanced -- concepts ---- - -TODO: Provide overview of the advanced concepts \ No newline at end of file diff --git a/documentation/docs/concepts/advanced/storage_interface.mdx b/documentation/docs/concepts/advanced/storage_interface.mdx deleted file mode 100644 index 26846128f7..0000000000 --- a/documentation/docs/concepts/advanced/storage_interface.mdx +++ /dev/null @@ -1,132 +0,0 @@ ---- -title: Storage Interface -sidebar_label: Storage Interface -description: Explain the need for the storage interface and how it can be implemented -image: /img/Identity_icon.png -keywords: - - storage - - storage interface - - storage adapter - - account - - signing - - encryption ---- - -import memstore_ts from "!!raw-loader!../../../../bindings/wasm/examples-account/src/custom_storage.ts"; -import memstore_rs from "!!raw-loader!../../../../identity_account_storage/src/storage/memstore.rs"; -import Tabs from "@theme/Tabs"; -import TabItem from "@theme/TabItem"; -import CodeBlock from "@theme/CodeBlock"; - -## Introduction - -The high-level account API takes care of publishing updates to an identity and storing secrets securely. It does the latter by using an implementation of the `Storage` interface. In this section, we will go into more depth of the interface, and how to implement that interface. - -The key idea behind the interface is strongly inspired by the architecture of key management systems (KMS) or secure enclaves: once private keys are entered into the system, they can never be retrieved again. Instead, all operations using the key will have to go through that system. This approach is what allows `Storage` implementations to be architected more securely than simply storing and loading private keys from a regular database. Of course, the security is directly dependent on the concrete implementation, which is why we provide one such implementation using [Stronghold](https://github.com/iotaledger/stronghold.rs/), and strongly recommend using it. However, there are cases where one cannot use `Stronghold` or may want to integrate key management of identities into their own KMS or similar, which is why the `Storage` interface is an abstraction over such systems. Any implementation of that interface can then be used by the `Account`. - -The storage interface has three major categories of functions. A brief overview of those functions: - -- DID Operations: Management of identities. - - `did_create`: Based on a private key, or a generated one, creates a new DID. - - `did_list`: List all DIDs in this `Storage`. - - `did_exists`: Returns whether the given DID exists in this `Storage`. - - `did_purge`: Wipes all data related to the given DID. -- Key Operations: Various functionality to managing cryptographic keys. - - `key_generate`: Generates a new key for the given DID. - - `key_insert`: Inserts a pre-existing private key for the given DID `location`. - - `key_public`: Calculates and returns the public key for the given location to a private key. - - `key_delete`: Removes the key at the given location. - - `key_sign`: Signs the given data with the key at the given location. - - `key_exists`: Returns whether the key at the given location exists. -- Data Operations: Used for keeping state persistent. Storages only need to serialize and store the data. - - `chain_state_get`: Returns the `ChainState` data structure for the given `DID`. - - `chain_state_set`: Sets the `ChainState` data structure for the given `DID`. - - `document_get`: Returns the DID document for the given `DID`. - - `document_set`: Sets the DID document for the given `DID`. - -## Storage Layout - -### Identifiers - -There are two types of identifiers in the interface, DIDs and key locations. A DID identifies an identity, while a key location identifies a key. An implementation recommendation is to use the DID as a partition key. Everything related to a DID can be stored in a partition identified by that DID. Importantly, the location of a key is only guaranteed to be unique within the DID partition it belongs to. If no partitioning is used, then DID and key location should be combined (e.g. concatenated) to produce a single, globally unique (i.e. across all identities) identifier for a key in storage. - -### Representations - -A `KeyLocation` is a compound identifier based on the fragment of a verification method and the hash of a public key. The motivation for this design is that a `KeyLocation` can be derived given a DID document and one of its verification methods. Thus, no additional state is necessary. - -Canonical string representations of the `IotaDID` and `KeyLocation` type can be obtained using the string representation of a DID and the `canonical` method on `KeyLocation` respectively. These representations are intended to be kept stable as much as possible. - -### Example layout - -This illustrates the recommended approach for partitioning the storage layout (where `location -> key` is a mapping from `location` to `key`): - -- `did:iota:Ft3wA8Tv2nF25hij3aegR54Wvqju7t5zqW9xnCB5L3Wu` - - `sign-0:16843234495045965331 -> 0xc6f0dbacd56156ff4c383d549ac61ada87f8aa69454f3bfae99f5fa9e093a5c3` - - `kex-0:7560300328640998700 -> 0xe494e36164e0a760140f3a9ab7dfdad38edac698f93d5239655dbd7499194760` -- `did:iota:DSvXWs7FUch9MQcaUKmrRFZyHYcHwt3t3pbjvKsQBfep` - - `sign-0:16843234495045965331 -> 0xc6f0dbacd56156ff4c383d549ac61ada87f8aa69454f3bfae99f5fa9e093a5c3` - - `kex-0:16546298247591944074 -> 0x8e1d037cd343f84276ab737b638da9095bcb6052f7fd9628d21d20f434f9959a` - - `key:8559754420653090937 -> 0x4ef484a54aa16503878aa1ecaa6d73cb8254aefa3f80a569ed33ca685289d01e` - -Note how fragments (such as `kex-0`) can appear more than once, but the hash of the public key - calculated from the stored private key - makes the location unique in general. - -:::caution - -Although unlikely in practice, even the same private key can be used across different DIDs, which produces the same key location (here `sign-0:16843234495045965331`) and it's important that these are stored independently, so that deleting one does not accidentally delete the other. Hence why a key's full identifier in storage needs to be based on the DID _and_ the key location. - -::: - -That said, the following flattened structure also satisfies the requirements: - -- `did:iota:Ft3wA8Tv2nF25hij3aegR54Wvqju7t5zqW9xnCB5L3Wu:sign-0:16843234495045965331 -> 0xc6f0dbacd56156ff4c383d549ac61ada87f8aa69454f3bfae99f5fa9e093a5c3` -- `did:iota:Ft3wA8Tv2nF25hij3aegR54Wvqju7t5zqW9xnCB5L3Wu:kex-0:7560300328640998700 -> 0xe494e36164e0a760140f3a9ab7dfdad38edac698f93d5239655dbd7499194760` -- `did:iota:DSvXWs7FUch9MQcaUKmrRFZyHYcHwt3t3pbjvKsQBfep:sign-0:16843234495045965331 -> 0xc6f0dbacd56156ff4c383d549ac61ada87f8aa69454f3bfae99f5fa9e093a5c3` -- `did:iota:DSvXWs7FUch9MQcaUKmrRFZyHYcHwt3t3pbjvKsQBfep:kex-0:16546298247591944074 -> 0x8e1d037cd343f84276ab737b638da9095bcb6052f7fd9628d21d20f434f9959a` -- `did:iota:DSvXWs7FUch9MQcaUKmrRFZyHYcHwt3t3pbjvKsQBfep:key:8559754420653090937 -> 0x4ef484a54aa16503878aa1ecaa6d73cb8254aefa3f80a569ed33ca685289d01e` - -The primary advantage of the partitioning is that it simplifies the implementation of the `did_purge` operation, which wipes all data belonging to a given DID. With partitioning, this operation can simply wipe the partition whereas a storage with a flattened layout will have to do more work. - -## Indexing - -The interface has two methods called `did_list` and `did_exists`. These return the list of stored DIDs, and whether a DID exists in storage, respectively. Implementations are thus expected to maintain a list or index of stored DIDs. An identity created with `did_create` is added to the index, while an identity deleted through `did_purge` is removed from the index. - -If the storage implementation can be accessed concurrently, then access to the index needs to be synchronized, since it is unique per storage instance. - -## Implementation - -The IOTA Identity framework ships two implementations of `Storage`. The `MemStore` is an insecure in-memory implementation intended as an example implementation and for testing. The secure and recommended `Storage` is `Stronghold`. `Stronghold` may be interesting for implementers to look at, as it needs to deal with some challenges the in-memory version does not have. - -This section will detail some common challenges and embeds the `MemStore` implementations in Rust and TypeScript. - -### Challenges - -The `did_create` method takes the fragment of the initial verification method, the name of a network in which the DID will eventually exist, and an optional private key. From these inputs, it either generates a key or uses the passed private key to calculate the public key and from that derive the DID. In case a key needs to be generated, the challenge is to obtain the location for the key to be stored at. Since the key location depends on the public key, but key generation likely needs a location for the key to be stored at, there is a circular dependency that needs to be resolved. This can be resolved in at least two ways. - -1. Generate the key at a random location, then derive the actual location and move the key there -2. If moving a key is not possible, then an additional mapping from key location to some storage-internal location identifier can be maintained. Then it's possible to generate the key at some storage-internal location, calculate the key location and store the mapping. - -Since this also needs to happen before the DID can be derived from the public key, similar approaches can be used to work around the not-yet available DID partition key. Storages may choose to have one statically identified partition where keys are generated initially, and then moved from there. Storages whose restrictions do not allow for this, may want to use the flattened storage layout described in [example layout](#example-layout) and use the mapping approach. - -### Storage Test Suite - -The `StorageTestSuite` can be used to test the basic functionality of storage implementations. See its documentation ([Rust docs](https://docs.rs/identity_iota/latest/identity_iota/account_storage/struct.StorageTestSuite.html), [Wasm docs](../../libraries/wasm/api_reference#StorageTestSuite)) for more details. - -### Examples - -This section shows the Rust and TypeScript `MemStore` implementations, which are thoroughly commented. - - - - {memstore_ts} - - - {memstore_rs} - - diff --git a/documentation/docs/introduction.md b/documentation/docs/introduction.md index 64c10d6de3..53c0e6e5ec 100644 --- a/documentation/docs/introduction.md +++ b/documentation/docs/introduction.md @@ -38,10 +38,6 @@ Explains the DID standard from W3C and how to manipulate DID Documents. Explains the VC standard from W3C, how to create and revoke VCs, and how to use Verifiable Presentations. -### [Chapter 3.3: Advanced Concepts](./concepts/advanced/overview) - -This chapter is meant for those that want to push the IOTA Identity framework to its limits, utilizing the more complex, yet more flexible lower-level libraries, allowing developers to optimize their implementation, take control over storage/security, and add features to the framework. - ### [Chapter 4: Programming Languages](./libraries/overview) diff --git a/documentation/sidebars.js b/documentation/sidebars.js index 2e8abca683..1eb75668ba 100644 --- a/documentation/sidebars.js +++ b/documentation/sidebars.js @@ -55,12 +55,6 @@ module.exports = { 'concepts/verifiable_credentials/create', 'concepts/verifiable_credentials/revocation', 'concepts/verifiable_credentials/verifiable_presentations', - ], - 'Advanced Concepts': [ - 'concepts/advanced/overview', - 'concepts/advanced/did_messages', - 'concepts/advanced/storage_interface', - ] }, ], From 4b43b801ac8bf637c5ba4a57052d4ca26a9b09e8 Mon Sep 17 00:00:00 2001 From: cycraig Date: Thu, 8 Sep 2022 09:57:16 +0200 Subject: [PATCH 49/89] Add deletion wiki article (#997) * Add delete wiki page * Update CodeSnippetComponent, finish Delete page * Remove whitespace * Clarify destruction * Apply suggestions from code review * Add link to Revocation Bitmap Service * Apply suggestions from code reviews * Update documentation/docs/specs/did/iota_did_method_spec.md Co-authored-by: Abdulrahim Al Methiab <31316147+abdulmth@users.noreply.github.com> --- .../decentralized_identifiers/delete.mdx | 74 ++++++++++++++++++ .../docs/specs/did/iota_did_method_spec.md | 47 ++++++------ documentation/sidebars.js | 3 +- .../components/CodeSnippetComponent/index.js | 76 +++++++++++-------- .../CodeSnippetComponent/styles.css | 8 +- 5 files changed, 146 insertions(+), 62 deletions(-) create mode 100644 documentation/docs/concepts/decentralized_identifiers/delete.mdx diff --git a/documentation/docs/concepts/decentralized_identifiers/delete.mdx b/documentation/docs/concepts/decentralized_identifiers/delete.mdx new file mode 100644 index 0000000000..8ad2aeed02 --- /dev/null +++ b/documentation/docs/concepts/decentralized_identifiers/delete.mdx @@ -0,0 +1,74 @@ +--- +title: Delete an IOTA Identity +sidebar_label: Delete +description: How to deactivate or destroy an IOTA Identity +image: /img/Identity_icon.png +keywords: +- Delete +- Deactivate +- Destroy +--- + +import deactivate_did_rs from '!!raw-loader!../../../../examples/0_basic/3_deactivate_did.rs'; +import deactivate_did_js from '!!raw-loader!../../../../bindings/wasm/examples-stardust/src/ex3_deactivate_did.ts'; +import delete_did_rs from '!!raw-loader!../../../../examples/0_basic/4_delete_did.rs'; +import delete_did_js from '!!raw-loader!../../../../bindings/wasm/examples-stardust/src/ex4_delete_did.ts'; +import CodeSnippet from '../../../src/components/CodeSnippetComponent' + + +There are two approaches to delete an IOTA Identity, with different implications: + +- [Deactivate](#deactivate) +- [Destroy](#destroy) + + +## Deactivate + +As detailed in the [IOTA DID Method Specification](../../specs/did/iota_did_method_spec.md#deactivate), the state controller of an IOTA Identity may [deactivate](https://www.w3.org/TR/did-core/#did-document-metadata) it by publishing an update that either: + +- deletes the contents of the DID Document entirely, leaving the state metadata empty, OR +- sets the `deactivated` field in the DID Document metadata to `true`. + +In both cases, the DID Document will be marked as `deactivated` when resolved. + +This operation is reversible: the identity can subsequently be reactivated at any time, by publishing an update restoring the DID Document's contents, or unsetting the `deactivated` field in the metadata respectively, depending on how it was initially deactivated. + +Note that the governor (if different from the state controller) cannot deactivate an identity directly because it is disallowed from updating the DID Document, but it may [destroy](#destroy) it. + +### Example + +The following example demonstrates deactivating and reactivating an IOTA DID Document, and optionally reclaiming the storage deposit. + + + +## Destroy + +Alternatively, an IOTA Identity can be permanently [destroyed](../../specs/did/iota_did_method_spec.md#destroy). + +This is achieved by the governor of a DID publishing a transaction consuming the [Alias Output](../../specs/did/iota_did_method_spec.md#alias-output) containing the IOTA DID Document, without a corresponding Alias Output on the output side. + +Any coins and tokens in the Alias Output are reclaimed and can be sent to another address. + +:::warning + +Destroying an IOTA Identity is permanent and irreversible. + +::: + +Note that historical versions may still be stored off-ledger or on a permanode, so sensitive or Personal Identifiable Information (PII) should NEVER be stored in a DID Document. Even with a previous version available, a destroyed DID can never be restored. + +### Example + +The following example demonstrates a governor destroying an IOTA Identity and sending the storage deposit back to itself. + + diff --git a/documentation/docs/specs/did/iota_did_method_spec.md b/documentation/docs/specs/did/iota_did_method_spec.md index 5886729fc2..fa35c3c8fe 100644 --- a/documentation/docs/specs/did/iota_did_method_spec.md +++ b/documentation/docs/specs/did/iota_did_method_spec.md @@ -28,7 +28,7 @@ Data types and subschemas used throughout this TIP are defined in [draft TIP-21] ### UTXO Ledger -The unspent transaction output ([UTXO](https://wiki.iota.org/IOTA-2.0-Research-Specifications/5.1UTXO)) model defines a ledger state where outputs are created by a transaction consuming outputs of previous transactions as inputs. IOTA and Shimmer have several output types, the relevant ones for the IOTA DID Method are: Basic Outputs for value transactions, and Alias Outputs for storage of DID documents. +The unspent transaction output ([UTXO](https://wiki.iota.org/IOTA-2.0-Research-Specifications/5.1UTXO)) model defines a ledger state where outputs are created by a transaction consuming outputs of previous transactions as inputs. IOTA and Shimmer have several output types, the relevant ones for the IOTA DID Method are: Basic Outputs for value transactions, and Alias Outputs for storage of DID Documents. All outputs must hold a minimum amount of coins to be stored on the ledger. For output types that can hold arbitrary data, for instance the Alias Output, the amount of coins held by the output must cover the byte cost of the data stored. This helps control the ledger size from growing uncontrollably while guaranteeing that the data is not pruned from the nodes, which is important for resolving DID Documents. This deposit is fully refundable and can be reclaimed when the output is destroyed. @@ -116,7 +116,7 @@ In the `State Metadata` of the Alias Output must be a byte packed payload with h | Document Type | ByteArray[3] | Set to value **DID** to denote a DID Document. | | Version | uint8 | Set value **1** to denote the version number of this method | | Encoding | uint8 | Set to value to **0** to denote JSON encoding without compression. | -| Payload | ByteArray | A DID document and its metadata, where every occurrence of the DID in the document is replaced by `did:0:0`. It must be encoded according to `Encoding`. | +| Payload | ByteArray | A DID Document and its metadata, where every occurrence of the DID in the document is replaced by `did:0:0`. It must be encoded according to `Encoding`. | Next to [TIP-21](#data-types--subschema-notation), we use the following type definitions: @@ -129,9 +129,9 @@ Next to [TIP-21](#data-types--subschema-notation), we use the following type def The payload must contain the following fields: -* `metadata`: contains metadata about the DID document. For example, `created` to indicate the time of +* `metadata`: contains metadata about the DID Document. For example, `created` to indicate the time of creation, and `updated` to indicate the time of the last update to the document. It can also include other properties. -* `document`: which contains the DID document. In the example below, the document only contains one verification method. The `id` and `controller` is specified by `did:0:0` which references the DID of the document itself, since the DID is unknown at the time of publishing. It also deduplicates the DID of the document to reduce the size of the state metadata, in turn reducing the required storage deposit. +* `document`: which contains the DID Document. In the example below, the document only contains one verification method. The `id` and `controller` is specified by `did:0:0` which references the DID of the document itself, since the DID is unknown at the time of publishing. It also deduplicates the DID of the document to reduce the size of the state metadata, in turn reducing the required storage deposit. Example State Metadata Document: @@ -185,43 +185,40 @@ Once the transaction is confirmed, the DID is published and can be formatted by ### Read -The following steps can be used to read the latest DID document associated with a DID. +The following steps can be used to read the latest DID Document associated with a DID. 1. Obtain the `Alias ID` from the DID by extracting the `iota-tag` from the DID, see [DID Format](#did-format). 2. Obtain the network of the DID by extracting the `iota-network` from the DID, see [DID Format](#did-format). 3. Query the Alias Output corresponding to the `Alias ID` using a node running the [inx indexer](https://github.com/iotaledger/inx-indexer). Nodes usually include this indexer by default. 4. Assert that the extracted network matches the one returned from the node. Return an error otherwise. 5. Assert that the `Alias ID` of the returned output matches the `Alias ID` extracted from the DID. Return an error otherwise. 6. Retrieve the value of the `State Metadata` field from the returned output. -7. Check if its content matches the [Anatomy of the State Metadata](#anatomy-of-the-state-metadata). Return an error otherwise. -8. Decode the DID document from the `State Metadata`. +7. Validate the contents match the structure described in [Anatomy of the State Metadata](#anatomy-of-the-state-metadata). Return an error otherwise. +8. Decode the DID Document from the `State Metadata`. 9. Replace the placeholder `did:0:0` with the DID given as input. ### Update -Updating a DID Document can be achieved by a state transition of the Alias Output. A state controller can update the -`State Metadata` in the Alias Output which reflects an update to the DID Document. +Updating a DID Document can be achieved by the state controller performing a state transition of the Alias Output with the updated content: -1. Create the content of the updated DID Document. -2. Create the payload and the headers as described in the [Anatomy of the State Metadata](#anatomy-of-the-state-metadata). -3. Create a new Alias Output with the same `Alias ID` as the current output. -4. Increment the `State Index`. -5. Leave the unlock conditions unchanged. -6. Set enough coins in the output to cover the byte cost. -7. Publish a new transaction that includes the current Alias Output as input and the newly created one as output. If the Alias Output to be updated is state-controlled by other Alias or NFT Outputs, those outputs will have to be unlocked in the same transaction. +1. Create a copy of the Alias Output with the `Alias ID` set explicitly. +2. Pack the updated DID Document, as described in the [Anatomy of the State Metadata](#anatomy-of-the-state-metadata), into the `State Metadata` of the output. +3. Increment the `State Index`. +4. Set the `amount` of coins sufficient to cover the byte cost. +5. Publish a new transaction that includes the current Alias Output as input (along with any required Basic Outputs to consume to cover the `amount`, if increased) and the updated one as output. If the state controller unlock of the Alias Output references other Alias or NFT Outputs, those outputs must be unlocked in the same transaction, recursively. ### Delete #### Deactivate -Temporarily deactivating a DID can be done by deleting the content of the `State Meadata` in the Alias Output. -1. Set the Alias Output's `State Metadata` field to an empty byte array. -2. Increment the `State Index`. -3. Set the state controller and the governor. -4. Publish a new transaction with the current Alias Output as input and the newly created one as output. -Another option is to update the DID Document and set the `deactivated` property in `metadata` to true. In this case the deactivated DID Document will be deactivated, but it can still be resolved, see [Update](#update). +Temporarily deactivating a DID can be done by deleting the contents of the `State Meadata` in the Alias Output, setting it to an empty byte array, and publishing an [update](#update). + +Another option is to [update](#update) the DID Document and set the `deactivated` property in its `metadata` to true. In both cases, the deactivated DID Document will be marked as `deactivated` when resolved. #### Destroy -In order to permanently destroy a DID, a new transaction can be published that consumes the Alias Output without having an Alias Output on the output side with a corresponding explicit `Alias ID`. This results in destroying the Alias Output and the DID. Note that this operation is irreversible resulting in permanently deleting the DID. + +In order to permanently destroy a DID, a new transaction can be published by the governor that consumes the Alias Output without having a corresponding Alias Output on the output side with the same explicit `Alias ID`. This results in destroying the Alias Output and the DID. + +Note that this operation irreversibly and irrecoverably deletes the DID. This is because the `Alias ID` from which an IOTA DID is derived (see [IOTA-Tag](#iota-tag)) is generated from the hash of the input transaction that created it, which cannot generally be replicated. ## IOTA Identity standards @@ -244,7 +241,7 @@ The IOTA Identity framework also standardized certain `services` that are embedd Currently standardized `services`: -* Nothing yet. +* [Revocation Bitmap Service](../revocation_bitmap_2022.md#revocation-bitmap-service) ## Security Considerations The `did:iota` method is implemented on the [IOTA](https://iota.org), a public permissionless and feeless Distributed Ledger Technology (DLT), making it resistant against almost all censorship attack vectors. Up until the `Coordicide` update for the IOTA network, a reliability on the coordinator exists for resolving ordering conflicts. This has a minor censorship possibility, that, in the wrost case, can prevent transactions from getting confirmed. @@ -264,4 +261,4 @@ That directly conflicts with certain privacy laws such as GDPR, which have a 'ri ### Correlation Risks -As with any DID method, identities can be linked if they are used too often and their usage somehow becomes public. See [DID Correlation Risks](DID Correlation Risks). Additionally, a DID can be correlated with funds if the Alias Output used to store the DID Document or any of its controllers is used for holding, transferring or controlling coins or NFTs. \ No newline at end of file +As with any DID method, identities can be linked if they are used too often and their usage somehow becomes public. See [DID Correlation Risks](https://www.w3.org/TR/did-core/#did-correlation-risks). Additionally, a DID can be correlated with funds if the Alias Output used to store the DID Document or any of its controllers is used for holding, transferring or controlling coins or NFTs. diff --git a/documentation/sidebars.js b/documentation/sidebars.js index 1eb75668ba..ad6eb80068 100644 --- a/documentation/sidebars.js +++ b/documentation/sidebars.js @@ -48,6 +48,7 @@ module.exports = { 'concepts/decentralized_identifiers/create', 'concepts/decentralized_identifiers/update', 'concepts/decentralized_identifiers/resolve', + 'concepts/decentralized_identifiers/delete', 'concepts/decentralized_identifiers/private_tangle', ], 'Verifiable Credentials': [ @@ -95,7 +96,6 @@ module.exports = { collapsed: true, items: [ 'specs/overview', - 'specs/revocation_bitmap_2022', { type: 'category', label: 'IOTA DID', @@ -139,6 +139,7 @@ module.exports = { 'specs/didcomm/CHANGELOG', ] }, + 'specs/revocation_bitmap_2022', ], }, 'glossary', diff --git a/documentation/src/components/CodeSnippetComponent/index.js b/documentation/src/components/CodeSnippetComponent/index.js index 04d5a8aa54..8ea16821a4 100644 --- a/documentation/src/components/CodeSnippetComponent/index.js +++ b/documentation/src/components/CodeSnippetComponent/index.js @@ -3,7 +3,7 @@ import React, {useEffect} from "react"; import "./styles.css"; import CodeBlock from "@theme/CodeBlock"; -export default function CodeSnippet({nodeReplitLink, rustContent, nodeGithubLink, rustGithubLink}) { +export default function CodeSnippet({nodeReplitLink, nodeContent, rustContent, nodeGithubLink, rustGithubLink}) { const [lang, setLang] = React.useState("node"); const ARROW_OUT_OF_BOX_ICON = ( @@ -31,8 +31,8 @@ export default function CodeSnippet({nodeReplitLink, rustContent, nodeGithubLink let langFromStorage = localStorage.getItem("lang"); let lang = langFromStorage ? langFromStorage : "node"; - //If Replit doesn't exist default to next option - if (lang === "node" && !nodeReplitLink) { + //If Node doesn't exist, default to next option + if (lang === "node" && !nodeReplitLink && !nodeContent) { lang = "rust"; } if (lang === "rust" && !rustContent) { @@ -46,7 +46,19 @@ export default function CodeSnippet({nodeReplitLink, rustContent, nodeGithubLink {/***** Tabs *****/}
- {nodeReplitLink && ( + {rustContent && ( + + )} + {(nodeReplitLink || nodeContent) && ( - )} - {rustContent && ( - + >Node.js )}
{/***** Code Snippet *****/}
- {lang === "node" ? ( - <> - - - ) : ( -
- - {rustContent} - -
- )} + { + (() => { + if(lang === "node" && nodeReplitLink) { + return ( + <> + + + ) + } else if (lang === "node" && nodeContent) { + return ( +
+ + {nodeContent} + +
+ ) + } else { + return ( +
+ + {rustContent} + +
+ ) + } + })() + }
{/***** Github Link *****/} diff --git a/documentation/src/components/CodeSnippetComponent/styles.css b/documentation/src/components/CodeSnippetComponent/styles.css index 68d83becff..c24706da3a 100644 --- a/documentation/src/components/CodeSnippetComponent/styles.css +++ b/documentation/src/components/CodeSnippetComponent/styles.css @@ -1,4 +1,4 @@ - + .languageButton:hover { color: var(--ifm-color-emphasis-1000); } @@ -23,7 +23,7 @@ } .codeSnippetContainer { - height: 700px; + max-height: 700px; width: 100%; background-color: #292D3E; overflow: hidden; @@ -47,7 +47,7 @@ margin-bottom: 20px; } -.rustContainer { - height: 100%; +.rustContainer,.nodeContainer { + max-height: 700px; overflow-y: scroll; } From f0329c02782890b7cbc23175bb38e8d33ef9489a Mon Sep 17 00:00:00 2001 From: cycraig Date: Thu, 8 Sep 2022 10:16:34 +0200 Subject: [PATCH 50/89] Update resolution wiki page (#992) * Update resolution documentation for Stardust * Update resolver wiki page * Update documentation yarn.lock to use latest Docusaurus * Fix yarn.lock checksum * Add Resolver example * Remove linebreaks * Apply suggestions from code review --- .../decentralized_identifiers/resolve.mdx | 232 +- .../docs/specs/did/iota_did_method_spec.md | 4 +- documentation/yarn.lock | 3471 ++++++++--------- 3 files changed, 1651 insertions(+), 2056 deletions(-) diff --git a/documentation/docs/concepts/decentralized_identifiers/resolve.mdx b/documentation/docs/concepts/decentralized_identifiers/resolve.mdx index 78405873b6..0a920d7077 100644 --- a/documentation/docs/concepts/decentralized_identifiers/resolve.mdx +++ b/documentation/docs/concepts/decentralized_identifiers/resolve.mdx @@ -6,79 +6,51 @@ image: /img/Identity_icon.png keywords: - Resolve --- -import resolve_did_rs from '!!raw-loader!../../../../examples_legacy/low-level-api/resolve_did.rs'; -import resolve_history_rs from '!!raw-loader!../../../../examples_legacy/low-level-api/resolve_history.rs'; -import resolve_did_js from '!!raw-loader!../../../../bindings/wasm/examples/src/resolve_did.js'; -import resolve_history_js from '!!raw-loader!../../../../bindings/wasm/examples/src/resolve_history.js'; -import CodeSnippet from '../../../src/components/CodeSnippetComponent' +import resolve_did_rs from '!!raw-loader!../../../../examples/0_basic/2_resolve_did.rs'; +import resolve_did_js from '!!raw-loader!../../../../bindings/wasm/examples-stardust/src/ex2_resolve_did.ts'; +import CodeSnippet from '../../../src/components/CodeSnippetComponent'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -import CodeBlock from '@theme/CodeBlock'; +DID resolution is the process of fetching and decoding a [DID Document](https://www.w3.org/TR/did-core/#dfn-did-documents) corresponding to a given [DID](https://www.w3.org/TR/did-core/#dfn-decentralized-identifiers). The [IOTA Identity framework](https://github.com/iotaledger/identity.rs) supports resolving DID Documents that are stored on the IOTA and Shimmer networks. -DID resolution is the process of fetching a [DID Document](https://www.w3.org/TR/did-core/#dfn-did-documents) corresponding to a given [DID](https://www.w3.org/TR/did-core/#dfn-decentralized-identifiers). -The [IOTA Identity Framework](https://github.com/iotaledger/identity.rs) supports resolving DID Documents that are stored on an IOTA Tangle (public or private). The main tool supplied -by the IOTA Identity Framework to handle DID Document resolution in a type safe manner is the `Resolver`. A DID Resolver as defined in the [W3C Decentralized Identifiers specification](https://www.w3.org/TR/did-core/#dfn-did-resolvers) -enforces the signature of the resolution function in a manner that is more centered around Web/API resolution rather than a strongly typed framework. This is the reason why the `Resolver` provided by the IOTA Identity Framework deviates somewhat from -the W3C specification. +This is similar to, but not to be confused with, the [W3C DID Resolution specification](https://w3c-ccg.github.io/did-resolution/), which defines function signatures for resolution in the context of web or REST APIs, whereas the IOTA Identity framework provides strongly-typed resolution for a better developer experience. +This functionality is primarily provided by the `Resolver`, which can: +- [Resolve DID Documents across multiple networks](#resolving-a-did). +- [Resolve the DID Documents referenced in a verifiable presentation or credential](#resolution-for-verifiable-presentations). -## Resolving a DID from the main network -The following example demonstrates how to resolve the DID: "did:iota:H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV" from the `main` network. - - - -```rust -use identity_iota::client::Resolver; -use identity_iota::iota_core::IotaDID; -use identity_iota::client::ResolvedIotaDocument; - -let resolver: Resolver = Resolver::new().await?; -let did: IotaDID = IotaDID::parse("did:iota:H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV")?; - -let doc: ResolvedIotaDocument = resolver.resolve(&did).await?; - -``` - - - +## Resolving a DID -```js -const { - DID, - Resolver, - ResolvedDocument, -} = require('@iota/identity-wasm/node'); +The following examples demonstrate how to resolve an IOTA DID Document from its DID. - const resolver = new Resolver(); - const did = DID.parse("did:iota:H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV"); - const doc = await resolver.resolve(did); +### Resolver -``` - - -What happens in this example can be explained on a high level as follows: The Resolver queries the Tangle for the history of the DID Document and utilizes it to recreate and validate the latest state of the DID Document. +The `Resolver` follows the read procedure defined in the [IOTA DID Method Specification](../../specs/did/iota_did_method_spec.md#read). It fetches the latest [Alias Output](../../specs/did/iota_did_method_spec#alias-output) from the network specified in the DID (see [DID Format](../../specs/did/iota_did_method_spec#did-format)), then extracts and validates the DID Document from it. -## Resolving from a private tangle -Resolving a DID from a private tangle is similar to resolving a DID from the main net. The only difference is that -the resolver needs to be configured to have a client capable of operating on said private tangle. Building a `Client` configured for a specified Tangle is explained in [this example in Rust](https://github.com/iotaledger/identity.rs/blob/dev/examples_legacy/low-level-api/private_tangle.rs) and [this example in Javascript](https://github.com/iotaledger/identity.rs/blob/dev/bindings/wasm/examples/src/private_tangle.js). +Multiple networks can be supported by a `Resolver` simply by adding multiple clients to it, each using a node endpoint for a different network. -The following example demonstrates how one can setup a `Resolver` with a given `client` and then attempt resolving a specified `did` which may be on any Tangle (public or private). - + ```rust -use identity_iota::client::Resolver; -use identity_iota::client::ResolverBuilder; -use identity_iota::iota_core::IotaDID; -use identity_iota::client::Client; -use identity_iota::client::Result; - - -async fn build_and_resolve(client: Client, did: IotaDID) -> Result { - let resolver_builder: ResolverBuilder = ResolverBuilder::new().await?; - let resolver: Resolver = resolver_builder.client(client).build().await?; - resolver.resolve(did).await +use identity_resolver::Resolver; +use identity_stardust::{StardustDID, StardustDocument, StardustIdentityClientExt}; +use iota_client::Client; + +#[tokio::main] +async fn main() { + // Configure a client for the Shimmer testnet "rms". + let node_url = "https://api.testnet.shimmer.network/"; + let client = Client::builder().with_primary_node(node_url, None).unwrap().finish().unwrap(); + + // Construct a resolver using the client. + let mut resolver = Resolver::::new(); + resolver.attach_iota_handler(client); + + // Parse the DID and resolve its DID Document. + let did = StardustDID::parse("did:iota:rms:0x7b48b06232b8a1e7a31c314cab1ceedb84e2e9dd2b1fae79b67eaa4595f15e47").unwrap(); + let document: StardustDocument = resolver.resolve(&did).await.unwrap(); } ``` @@ -86,56 +58,50 @@ async fn build_and_resolve(client: Client, did: IotaDID) -> Result ```js -const { - DID, - Resolver, - ResolvedDocument, - Client, -} = require('@iota/identity-wasm/node'); - - -async function buildAndResolve(client, did) { - const resolver = await Resolver.builder().client(client).build(); - const resolvedDocument = await resolver.resolve(did); - return resolvedDocument; -} - +const { MixedResolver, StardustDID, StardustIdentityClient } = require('@iota/identity-wasm/node'); +const { Client } = require('@cycraig/iota-client-wasm/node'); + +// Configure a client for the Shimmer testnet "rms". +const nodeUrl = "https://api.testnet.shimmer.network/"; +const client = new Client({ + primaryNode: nodeUrl, + localPow: true, +}); +const didClient = new StardustIdentityClient(client); + +// Construct a resolver using the client. +const resolver = new MixedResolver({ + client: didClient, +}); + +// Parse the DID and resolve its DID Document. +const did = StardustDID.parse("did:iota:rms:0x7b48b06232b8a1e7a31c314cab1ceedb84e2e9dd2b1fae79b67eaa4595f15e47"); +const document = await resolver.resolve(did); ``` + -In the example above the resolver will automatically try to resolve the DID from the network specified in the `did` (See [DID Format](../../specs/did/iota_did_method_spec#did-format)). -If the resolver was not built with a client configured for the given network name then an error will be thrown. Note that the `ResolverBuilder` can configure the `Resolver` to use -multiple networks as long as they have distinct valid names (max six characters). - -Note that in the context of an identity managed by an `Account` the DID document can also be resolved by simply calling the `resolve` method on the `Account` directly. - -## Resolution in the context of Verifiable Presentations -As explained in [Verifiable Presentations](./../verifiable_credentials/verifiable_presentations) one resolves the DID Documents of the credential issuers and presentation holder -during verification of a verifiable presentation. Resolving the necessary DID Documents is done automatically when verifying presentations via the `Resolver`, but there are certain -advanced use cases where more control is desired. To accommodate for such situations the `Resolver` also comes equipped with additional stand alone methods that enable: -- resolving a presentation holder's DID Document -- resolving all DID Documents of the distinct issuers of the credentials contained in the presentation -- resolving the issuer's DID Document for a given verifiable credential +### Client - -## Resolving the history of a DID Document. -The fact that a DID Document [can be updated](./update.mdx) implies that the state of the DID Document can change over time, or in other words the result of resolving a DID -also depends on when this operation was carried out. The `Resolver` provides a way to view the entire history of a DID Document (up to the time when the method is called). +The `Client` can also be used directly, to resolve individual DIDs from its configured network. - + ```rust -use identity_iota::client::Resolver; -use identity_iota::iota_core::IotaDID; -use identity_iota::client::DocumentHistory; -use identity_iota::client::Result; - - -async fn call_resolve_history(did: IotaDID) -> Result { - let resolver: Resolver = Resolver::new().await?; - resolver.resolve_history(did).await? +use identity_stardust::{StardustDID, StardustDocument, StardustIdentityClientExt}; +use iota_client::Client; + +#[tokio::main] +async fn main() { + // Configure a client for the Shimmer testnet "rms". + let node_url = "https://api.testnet.shimmer.network/"; + let client = Client::builder().with_primary_node(node_url, None).unwrap().finish().unwrap(); + + // Parse the DID and resolve its DID Document. + let did = StardustDID::parse("did:iota:rms:0x7b48b06232b8a1e7a31c314cab1ceedb84e2e9dd2b1fae79b67eaa4595f15e47").unwrap(); + let document: StardustDocument = client.resolve_did(&did).await.unwrap(); } ``` @@ -143,50 +109,40 @@ async fn call_resolve_history(did: IotaDID) -> Result { ```js -const { - DID, - Resolver, - DocumentHistory, -} = require('@iota/identity-wasm/node'); - - -async function callResolveHistory(did) { - const resolver = new Resolver(); - const documentHistory = await resolver.resolveHistory(did); - return documentHistory; -} - +const { StardustDID, StardustIdentityClient } = require('@iota/identity-wasm/node'); +const { Client } = require('@cycraig/iota-client-wasm/node'); + +// Configure a client for the Shimmer testnet "rms". +const nodeUrl = "https://api.testnet.shimmer.network/"; +const client = new Client({ + primaryNode: nodeUrl, + localPow: true, +}); +const didClient = new StardustIdentityClient(client); + +// Parse the DID and resolve its DID Document. +const did = StardustDID.parse("did:iota:rms:0x7b48b06232b8a1e7a31c314cab1ceedb84e2e9dd2b1fae79b67eaa4595f15e47"); +const document = await didClient.resolveDid(did); ``` + +## Resolution for Verifiable Presentations + +When validating [verifiable presentations](../verifiable_credentials/verifiable_presentations.mdx), it is necessary to resolve the DID Documents of the [verifiable credential](../verifiable_credentials/overview.md) issuers and presentation holder to verify their signatures. Resolving the necessary DID Documents is performed automatically when verifying presentations via the `Resolver` (see this [example](../verifiable_credentials/verifiable_presentations.mdx#example)). + +When direct access to these DID Documents is desired, the `Resolver` also provides standalone methods to: +- Resolve a presentation holder's DID Document. +- Resolve the DID Documents of the issuers of the credentials in a verifiable presentation. +- Resolve the issuer's DID Document for a given verifiable credential. -## Complete examples -This section shows complete examples from the Iota Identity Framework code base. The first example creates a DID Document, publishes it to the Tangle and then resolves it. +## Example +The following example creates a new IOTA DID, publishes it and then resolves its DID Document and Alias Output. - - -This second example demonstrates creating, publishing changes and then resolving the history of a DID Document. - - - {resolve_history_js} - - - {resolve_history_rs} - - - -Note that this example used the `Client` to resolve the history of the DID Document, but one could also use the `Resolver` for this task. diff --git a/documentation/docs/specs/did/iota_did_method_spec.md b/documentation/docs/specs/did/iota_did_method_spec.md index fa35c3c8fe..e45dc24a67 100644 --- a/documentation/docs/specs/did/iota_did_method_spec.md +++ b/documentation/docs/specs/did/iota_did_method_spec.md @@ -255,9 +255,9 @@ All private keys or seeds used for the `did:iota` method should be equally well ### Personal Identifiable Information -The public IOTA Tangle networks are immutable networks. This means that once something is included, it can never be completely removed. For example, destroying an Alias Output will remove it from the ledger state, but it can still be stored in permanodes or by any party that records historical ledger states. +The public IOTA and Shimmer networks are immutable. This means that once something is included, it can never be completely removed. For example, destroying an Alias Output will remove it from the ledger state, but it can still be stored in permanodes or by any party that records historical ledger states. -That directly conflicts with certain privacy laws such as GDPR, which have a 'right-to-be-forgotten' for Personal Identifiable Information (PII). As such, users should NEVER upload any PII to the Tangle, including inside DID Documents. The IOTA Identity framework allows Verifiable Credentials to be published to the Tangle directly, however this feature should only be utilized by Identity for Organisations and Identity for Things. +That directly conflicts with certain privacy laws such as GDPR, which have a 'right-to-be-forgotten' for Personal Identifiable Information (PII). As such, users should NEVER upload any PII, including inside DID Documents. While Verifiable Credentials can be made public, this should only be utilized by Identity for Organisations and Identity for Things. ### Correlation Risks diff --git a/documentation/yarn.lock b/documentation/yarn.lock index 2c6cfdc825..3ec0967052 100644 --- a/documentation/yarn.lock +++ b/documentation/yarn.lock @@ -15,19 +15,19 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.16.0, @babel/code-frame@npm:^7.16.7, @babel/code-frame@npm:^7.8.3": - version: 7.16.7 - resolution: "@babel/code-frame@npm:7.16.7" +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.16.0, @babel/code-frame@npm:^7.18.6, @babel/code-frame@npm:^7.8.3": + version: 7.18.6 + resolution: "@babel/code-frame@npm:7.18.6" dependencies: - "@babel/highlight": ^7.16.7 - checksum: db2f7faa31bc2c9cf63197b481b30ea57147a5fc1a6fab60e5d6c02cdfbf6de8e17b5121f99917b3dabb5eeb572da078312e70697415940383efc140d4e0808b + "@babel/highlight": ^7.18.6 + checksum: 195e2be3172d7684bf95cff69ae3b7a15a9841ea9d27d3c843662d50cdd7d6470fd9c8e64be84d031117e4a4083486effba39f9aef6bbb2c89f7f21bcfba33ba languageName: node linkType: hard -"@babel/compat-data@npm:^7.13.11, @babel/compat-data@npm:^7.17.10": - version: 7.17.10 - resolution: "@babel/compat-data@npm:7.17.10" - checksum: e85051087cd4690de5061909a2dd2d7f8b6434a3c2e30be6c119758db2027ae1845bcd75a81127423dd568b706ac6994a1a3d7d701069a23bf5cfe900728290b +"@babel/compat-data@npm:^7.17.7, @babel/compat-data@npm:^7.18.8": + version: 7.18.13 + resolution: "@babel/compat-data@npm:7.18.13" + checksum: 869a730dc3ec40d4d5141b832d50b16702a2ea7bf5b87dc2761e7dfaa8deeafa03b8809fc42ff713ac1d450748dcdb07e1eb21f4633e10b87fd47be0065573e6 languageName: node linkType: hard @@ -55,195 +55,193 @@ __metadata: languageName: node linkType: hard -"@babel/core@npm:^7.15.5, @babel/core@npm:^7.17.10": - version: 7.18.2 - resolution: "@babel/core@npm:7.18.2" +"@babel/core@npm:^7.18.5, @babel/core@npm:^7.18.6": + version: 7.18.13 + resolution: "@babel/core@npm:7.18.13" dependencies: "@ampproject/remapping": ^2.1.0 - "@babel/code-frame": ^7.16.7 - "@babel/generator": ^7.18.2 - "@babel/helper-compilation-targets": ^7.18.2 - "@babel/helper-module-transforms": ^7.18.0 - "@babel/helpers": ^7.18.2 - "@babel/parser": ^7.18.0 - "@babel/template": ^7.16.7 - "@babel/traverse": ^7.18.2 - "@babel/types": ^7.18.2 + "@babel/code-frame": ^7.18.6 + "@babel/generator": ^7.18.13 + "@babel/helper-compilation-targets": ^7.18.9 + "@babel/helper-module-transforms": ^7.18.9 + "@babel/helpers": ^7.18.9 + "@babel/parser": ^7.18.13 + "@babel/template": ^7.18.10 + "@babel/traverse": ^7.18.13 + "@babel/types": ^7.18.13 convert-source-map: ^1.7.0 debug: ^4.1.0 gensync: ^1.0.0-beta.2 json5: ^2.2.1 semver: ^6.3.0 - checksum: 14a4142c12e004cd2477b7610408d5788ee5dd821ee9e4de204cbb72d9c399d858d9deabc3d49914d5d7c2927548160c19bdc7524b1a9f6acc1ec96a8d9848dd + checksum: c7ee5b2c10bc5b0325e31fb5da4cb4bc03f9d5f5c00ec3481a018917bcc6b7b040de0690c606a424f57e5fc26d218d64e7718d0e5d7d8614d39c8cd898fab9b3 languageName: node linkType: hard -"@babel/generator@npm:^7.12.5, @babel/generator@npm:^7.17.10, @babel/generator@npm:^7.17.9, @babel/generator@npm:^7.18.2": - version: 7.18.2 - resolution: "@babel/generator@npm:7.18.2" +"@babel/generator@npm:^7.12.5, @babel/generator@npm:^7.17.9, @babel/generator@npm:^7.18.13, @babel/generator@npm:^7.18.7": + version: 7.18.13 + resolution: "@babel/generator@npm:7.18.13" dependencies: - "@babel/types": ^7.18.2 - "@jridgewell/gen-mapping": ^0.3.0 + "@babel/types": ^7.18.13 + "@jridgewell/gen-mapping": ^0.3.2 jsesc: ^2.5.1 - checksum: d0661e95532ddd97566d41fec26355a7b28d1cbc4df95fe80cc084c413342935911b48db20910708db39714844ddd614f61c2ec4cca3fb10181418bdcaa2e7a3 + checksum: 27f5e7eb774e4d76ee75dc96e3e1bd26cc0ee7cea13a8f7342b716319c0a4d4e26fc49aa8f19316f7c99383da55eeb2a866c6e034e9deacad58a9de9ed6004fb languageName: node linkType: hard -"@babel/helper-annotate-as-pure@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/helper-annotate-as-pure@npm:7.16.7" +"@babel/helper-annotate-as-pure@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/helper-annotate-as-pure@npm:7.18.6" dependencies: - "@babel/types": ^7.16.7 - checksum: d235be963fed5d48a8a4cfabc41c3f03fad6a947810dbcab9cebed7f819811457e10d99b4b2e942ad71baa7ee8e3cd3f5f38a4e4685639ddfddb7528d9a07179 + "@babel/types": ^7.18.6 + checksum: 88ccd15ced475ef2243fdd3b2916a29ea54c5db3cd0cfabf9d1d29ff6e63b7f7cd1c27264137d7a40ac2e978b9b9a542c332e78f40eb72abe737a7400788fc1b languageName: node linkType: hard -"@babel/helper-builder-binary-assignment-operator-visitor@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/helper-builder-binary-assignment-operator-visitor@npm:7.16.7" +"@babel/helper-builder-binary-assignment-operator-visitor@npm:^7.18.6": + version: 7.18.9 + resolution: "@babel/helper-builder-binary-assignment-operator-visitor@npm:7.18.9" dependencies: - "@babel/helper-explode-assignable-expression": ^7.16.7 - "@babel/types": ^7.16.7 - checksum: 1784f19a57ecfafca8e5c2e0f3eac53451cb13a857cbe0ca0cd9670922228d099ef8c3dd8cd318e2d7bce316fdb2ece3e527c30f3ecd83706e37ab6beb0c60eb + "@babel/helper-explode-assignable-expression": ^7.18.6 + "@babel/types": ^7.18.9 + checksum: b4bc214cb56329daff6cc18a7f7a26aeafb55a1242e5362f3d47fe3808421f8c7cd91fff95d6b9b7ccb67e14e5a67d944e49dbe026942bfcbfda19b1c72a8e72 languageName: node linkType: hard -"@babel/helper-compilation-targets@npm:^7.13.0, @babel/helper-compilation-targets@npm:^7.16.7, @babel/helper-compilation-targets@npm:^7.17.10, @babel/helper-compilation-targets@npm:^7.18.2": - version: 7.18.2 - resolution: "@babel/helper-compilation-targets@npm:7.18.2" +"@babel/helper-compilation-targets@npm:^7.17.7, @babel/helper-compilation-targets@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/helper-compilation-targets@npm:7.18.9" dependencies: - "@babel/compat-data": ^7.17.10 - "@babel/helper-validator-option": ^7.16.7 + "@babel/compat-data": ^7.18.8 + "@babel/helper-validator-option": ^7.18.6 browserslist: ^4.20.2 semver: ^6.3.0 peerDependencies: "@babel/core": ^7.0.0 - checksum: 4f02e79f20c0b3f8db5049ba8c35027c41ccb3fc7884835d04e49886538e0f55702959db1bb75213c94a5708fec2dc81a443047559a4f184abb884c72c0059b4 + checksum: 2a9d71e124e098a9f45de4527ddd1982349d231827d341e00da9dfb967e260ecc7662c8b62abee4a010fb34d5f07a8d2155c974e0bc1928144cee5644910621d languageName: node linkType: hard -"@babel/helper-create-class-features-plugin@npm:^7.17.12, @babel/helper-create-class-features-plugin@npm:^7.18.0": - version: 7.18.0 - resolution: "@babel/helper-create-class-features-plugin@npm:7.18.0" +"@babel/helper-create-class-features-plugin@npm:^7.18.6, @babel/helper-create-class-features-plugin@npm:^7.18.9": + version: 7.18.13 + resolution: "@babel/helper-create-class-features-plugin@npm:7.18.13" dependencies: - "@babel/helper-annotate-as-pure": ^7.16.7 - "@babel/helper-environment-visitor": ^7.16.7 - "@babel/helper-function-name": ^7.17.9 - "@babel/helper-member-expression-to-functions": ^7.17.7 - "@babel/helper-optimise-call-expression": ^7.16.7 - "@babel/helper-replace-supers": ^7.16.7 - "@babel/helper-split-export-declaration": ^7.16.7 + "@babel/helper-annotate-as-pure": ^7.18.6 + "@babel/helper-environment-visitor": ^7.18.9 + "@babel/helper-function-name": ^7.18.9 + "@babel/helper-member-expression-to-functions": ^7.18.9 + "@babel/helper-optimise-call-expression": ^7.18.6 + "@babel/helper-replace-supers": ^7.18.9 + "@babel/helper-split-export-declaration": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0 - checksum: 9a6ef175350f1cf87abe7a738e8c9b603da7fcdb153c74e49af509183f8705278020baddb62a12c7f9ca059487fef97d75a4adea6a1446598ad9901d010e4296 + checksum: 3d2da92a54b885b211a747a8c553bb0190895d140cd1daab5d9945b2b271817d9d288a512364085e9fad19bd503921cd85fcc12f98df67b39b27f5655eb9d058 languageName: node linkType: hard -"@babel/helper-create-regexp-features-plugin@npm:^7.16.7, @babel/helper-create-regexp-features-plugin@npm:^7.17.12": - version: 7.17.12 - resolution: "@babel/helper-create-regexp-features-plugin@npm:7.17.12" +"@babel/helper-create-regexp-features-plugin@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/helper-create-regexp-features-plugin@npm:7.18.6" dependencies: - "@babel/helper-annotate-as-pure": ^7.16.7 - regexpu-core: ^5.0.1 + "@babel/helper-annotate-as-pure": ^7.18.6 + regexpu-core: ^5.1.0 peerDependencies: "@babel/core": ^7.0.0 - checksum: fe49d26b0f6c58d4c1748a4d0e98b343882b428e6db43c4ba5e0aa7ff2296b3a557f0a88de9f000599bb95640a6c47c0b0c9a952b58c11f61aabb06bcc304329 + checksum: 2d76e660cbfd0bfcb01ca9f177f0e9091c871a6b99f68ece6bcf4ab4a9df073485bdc2d87ecdfbde44b7f3723b26d13085d0f92082adb3ae80d31b246099f10a languageName: node linkType: hard -"@babel/helper-define-polyfill-provider@npm:^0.3.1": - version: 0.3.1 - resolution: "@babel/helper-define-polyfill-provider@npm:0.3.1" +"@babel/helper-define-polyfill-provider@npm:^0.3.2": + version: 0.3.2 + resolution: "@babel/helper-define-polyfill-provider@npm:0.3.2" dependencies: - "@babel/helper-compilation-targets": ^7.13.0 - "@babel/helper-module-imports": ^7.12.13 - "@babel/helper-plugin-utils": ^7.13.0 - "@babel/traverse": ^7.13.0 + "@babel/helper-compilation-targets": ^7.17.7 + "@babel/helper-plugin-utils": ^7.16.7 debug: ^4.1.1 lodash.debounce: ^4.0.8 resolve: ^1.14.2 semver: ^6.1.2 peerDependencies: "@babel/core": ^7.4.0-0 - checksum: e3e93cb22febfc0449a210cdafb278e5e1a038af2ca2b02f5dee71c7a49e8ba26e469d631ee11a4243885961a62bb2e5b0a4deb3ec1d7918a33c953d05c3e584 + checksum: 8f693ab8e9d73873c2e547c7764c7d32d73c14f8dcefdd67fd3a038eb75527e2222aa53412ea673b9bfc01c32a8779a60e77a7381bbdd83452f05c9b7ef69c2c languageName: node linkType: hard -"@babel/helper-environment-visitor@npm:^7.16.7, @babel/helper-environment-visitor@npm:^7.18.2": - version: 7.18.2 - resolution: "@babel/helper-environment-visitor@npm:7.18.2" - checksum: 1a9c8726fad454a082d077952a90f17188e92eabb3de236cb4782c49b39e3f69c327e272b965e9a20ff8abf37d30d03ffa6fd7974625a6c23946f70f7527f5e9 +"@babel/helper-environment-visitor@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/helper-environment-visitor@npm:7.18.9" + checksum: b25101f6162ddca2d12da73942c08ad203d7668e06663df685634a8fde54a98bc015f6f62938e8554457a592a024108d45b8f3e651fd6dcdb877275b73cc4420 languageName: node linkType: hard -"@babel/helper-explode-assignable-expression@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/helper-explode-assignable-expression@npm:7.16.7" +"@babel/helper-explode-assignable-expression@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/helper-explode-assignable-expression@npm:7.18.6" dependencies: - "@babel/types": ^7.16.7 - checksum: ea2135ba36da6a2be059ebc8f10fbbb291eb0e312da54c55c6f50f9cbd8601e2406ec497c5e985f7c07a97f31b3bef9b2be8df53f1d53b974043eaf74fe54bbc + "@babel/types": ^7.18.6 + checksum: 225cfcc3376a8799023d15dc95000609e9d4e7547b29528c7f7111a0e05493ffb12c15d70d379a0bb32d42752f340233c4115bded6d299bc0c3ab7a12be3d30f languageName: node linkType: hard -"@babel/helper-function-name@npm:^7.16.7, @babel/helper-function-name@npm:^7.17.9": - version: 7.17.9 - resolution: "@babel/helper-function-name@npm:7.17.9" +"@babel/helper-function-name@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/helper-function-name@npm:7.18.9" dependencies: - "@babel/template": ^7.16.7 - "@babel/types": ^7.17.0 - checksum: a59b2e5af56d8f43b9b0019939a43774754beb7cb01a211809ca8031c71890999d07739e955343135ec566c4d8ff725435f1f60fb0af3bb546837c1f9f84f496 + "@babel/template": ^7.18.6 + "@babel/types": ^7.18.9 + checksum: d04c44e0272f887c0c868651be7fc3c5690531bea10936f00d4cca3f6d5db65e76dfb49e8d553c42ae1fe1eba61ccce9f3d93ba2df50a66408c8d4c3cc61cf0c languageName: node linkType: hard -"@babel/helper-hoist-variables@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/helper-hoist-variables@npm:7.16.7" +"@babel/helper-hoist-variables@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/helper-hoist-variables@npm:7.18.6" dependencies: - "@babel/types": ^7.16.7 - checksum: 6ae1641f4a751cd9045346e3f61c3d9ec1312fd779ab6d6fecfe2a96e59a481ad5d7e40d2a840894c13b3fd6114345b157f9e3062fc5f1580f284636e722de60 + "@babel/types": ^7.18.6 + checksum: fd9c35bb435fda802bf9ff7b6f2df06308a21277c6dec2120a35b09f9de68f68a33972e2c15505c1a1a04b36ec64c9ace97d4a9e26d6097b76b4396b7c5fa20f languageName: node linkType: hard -"@babel/helper-member-expression-to-functions@npm:^7.17.7": - version: 7.17.7 - resolution: "@babel/helper-member-expression-to-functions@npm:7.17.7" +"@babel/helper-member-expression-to-functions@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/helper-member-expression-to-functions@npm:7.18.9" dependencies: - "@babel/types": ^7.17.0 - checksum: 70f361bab627396c714c3938e94a569cb0da522179328477cdbc4318e4003c2666387ad4931d6bd5de103338c667c9e4bbe3e917fc8c527b3f3eb6175b888b7d + "@babel/types": ^7.18.9 + checksum: fcf8184e3b55051c4286b2cbedf0eccc781d0f3c9b5cbaba582eca19bf0e8d87806cdb7efc8554fcb969ceaf2b187d5ea748d40022d06ec7739fbb18c1b19a7a languageName: node linkType: hard -"@babel/helper-module-imports@npm:^7.12.13, @babel/helper-module-imports@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/helper-module-imports@npm:7.16.7" +"@babel/helper-module-imports@npm:^7.16.7, @babel/helper-module-imports@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/helper-module-imports@npm:7.18.6" dependencies: - "@babel/types": ^7.16.7 - checksum: ddd2c4a600a2e9a4fee192ab92bf35a627c5461dbab4af31b903d9ba4d6b6e59e0ff3499fde4e2e9a0eebe24906f00b636f8b4d9bd72ff24d50e6618215c3212 + "@babel/types": ^7.18.6 + checksum: f393f8a3b3304b1b7a288a38c10989de754f01d29caf62ce7c4e5835daf0a27b81f3ac687d9d2780d39685aae7b55267324b512150e7b2be967b0c493b6a1def languageName: node linkType: hard -"@babel/helper-module-transforms@npm:^7.12.1, @babel/helper-module-transforms@npm:^7.18.0": - version: 7.18.0 - resolution: "@babel/helper-module-transforms@npm:7.18.0" +"@babel/helper-module-transforms@npm:^7.12.1, @babel/helper-module-transforms@npm:^7.18.6, @babel/helper-module-transforms@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/helper-module-transforms@npm:7.18.9" dependencies: - "@babel/helper-environment-visitor": ^7.16.7 - "@babel/helper-module-imports": ^7.16.7 - "@babel/helper-simple-access": ^7.17.7 - "@babel/helper-split-export-declaration": ^7.16.7 - "@babel/helper-validator-identifier": ^7.16.7 - "@babel/template": ^7.16.7 - "@babel/traverse": ^7.18.0 - "@babel/types": ^7.18.0 - checksum: 824c3967c08d75bb36adc18c31dcafebcd495b75b723e2e17c6185e88daf5c6db62a6a75d9f791b5f38618a349e7cb32503e715a1b9a4e8bad4d0f43e3e6b523 + "@babel/helper-environment-visitor": ^7.18.9 + "@babel/helper-module-imports": ^7.18.6 + "@babel/helper-simple-access": ^7.18.6 + "@babel/helper-split-export-declaration": ^7.18.6 + "@babel/helper-validator-identifier": ^7.18.6 + "@babel/template": ^7.18.6 + "@babel/traverse": ^7.18.9 + "@babel/types": ^7.18.9 + checksum: af08c60ea239ff3d40eda542fceaab69de17e713f131e80ead08c975ba7a47dd55d439cb48cfb14ae7ec96704a10c989ff5a5240e52a39101cb44a49467ce058 languageName: node linkType: hard -"@babel/helper-optimise-call-expression@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/helper-optimise-call-expression@npm:7.16.7" +"@babel/helper-optimise-call-expression@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/helper-optimise-call-expression@npm:7.18.6" dependencies: - "@babel/types": ^7.16.7 - checksum: 925feb877d5a30a71db56e2be498b3abbd513831311c0188850896c4c1ada865eea795dce5251a1539b0f883ef82493f057f84286dd01abccc4736acfafe15ea + "@babel/types": ^7.18.6 + checksum: e518fe8418571405e21644cfb39cf694f30b6c47b10b006609a92469ae8b8775cbff56f0b19732343e2ea910641091c5a2dc73b56ceba04e116a33b0f8bd2fbd languageName: node linkType: hard @@ -254,252 +252,263 @@ __metadata: languageName: node linkType: hard -"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.13.0, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.16.7, @babel/helper-plugin-utils@npm:^7.17.12, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3": - version: 7.17.12 - resolution: "@babel/helper-plugin-utils@npm:7.17.12" - checksum: 4813cf0ddb0f143de032cb88d4207024a2334951db330f8216d6fa253ea320c02c9b2667429ef1a34b5e95d4cfbd085f6cb72d418999751c31d0baf2422cc61d +"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.16.7, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.18.9, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3": + version: 7.18.9 + resolution: "@babel/helper-plugin-utils@npm:7.18.9" + checksum: ebae876cd60f1fe238c7210986093845fa5c4cad5feeda843ea4d780bf068256717650376d3af2a5e760f2ed6a35c065ae144f99c47da3e54aa6cba99d8804e0 languageName: node linkType: hard -"@babel/helper-remap-async-to-generator@npm:^7.16.8": - version: 7.16.8 - resolution: "@babel/helper-remap-async-to-generator@npm:7.16.8" +"@babel/helper-remap-async-to-generator@npm:^7.18.6, @babel/helper-remap-async-to-generator@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/helper-remap-async-to-generator@npm:7.18.9" dependencies: - "@babel/helper-annotate-as-pure": ^7.16.7 - "@babel/helper-wrap-function": ^7.16.8 - "@babel/types": ^7.16.8 - checksum: 29282ee36872130085ca111539725abbf20210c2a1d674bee77f338a57c093c3154108d03a275f602e471f583bd2c7ae10d05534f87cbc22b95524fe2b569488 + "@babel/helper-annotate-as-pure": ^7.18.6 + "@babel/helper-environment-visitor": ^7.18.9 + "@babel/helper-wrap-function": ^7.18.9 + "@babel/types": ^7.18.9 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 4be6076192308671b046245899b703ba090dbe7ad03e0bea897bb2944ae5b88e5e85853c9d1f83f643474b54c578d8ac0800b80341a86e8538264a725fbbefec languageName: node linkType: hard -"@babel/helper-replace-supers@npm:^7.16.7": - version: 7.18.2 - resolution: "@babel/helper-replace-supers@npm:7.18.2" +"@babel/helper-replace-supers@npm:^7.18.6, @babel/helper-replace-supers@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/helper-replace-supers@npm:7.18.9" dependencies: - "@babel/helper-environment-visitor": ^7.18.2 - "@babel/helper-member-expression-to-functions": ^7.17.7 - "@babel/helper-optimise-call-expression": ^7.16.7 - "@babel/traverse": ^7.18.2 - "@babel/types": ^7.18.2 - checksum: c0083b7933672dd2aed50b79021c46401c83f41bc2132def19c5414cf8f944251f6d91dd959b2bedada9a7436a80fab629adb486e008566290c82293e89fec05 + "@babel/helper-environment-visitor": ^7.18.9 + "@babel/helper-member-expression-to-functions": ^7.18.9 + "@babel/helper-optimise-call-expression": ^7.18.6 + "@babel/traverse": ^7.18.9 + "@babel/types": ^7.18.9 + checksum: 2de8b29cc4bfa4e241da2de16abd5571709f6eb394206dc16e3a7816976d1691635dd4bc930881e9d798f44b48a5f1849dc7f51a62946f3e8270452be1ec5352 languageName: node linkType: hard -"@babel/helper-simple-access@npm:^7.17.7, @babel/helper-simple-access@npm:^7.18.2": - version: 7.18.2 - resolution: "@babel/helper-simple-access@npm:7.18.2" +"@babel/helper-simple-access@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/helper-simple-access@npm:7.18.6" dependencies: - "@babel/types": ^7.18.2 - checksum: c0862b56db7e120754d89273a039b128c27517389f6a4425ff24e49779791e8fe10061579171fb986be81fa076778acb847c709f6f5e396278d9c5e01360c375 + "@babel/types": ^7.18.6 + checksum: 37cd36eef199e0517845763c1e6ff6ea5e7876d6d707a6f59c9267c547a50aa0e84260ba9285d49acfaf2cfa0a74a772d92967f32ac1024c961517d40b6c16a5 languageName: node linkType: hard -"@babel/helper-skip-transparent-expression-wrappers@npm:^7.16.0": - version: 7.16.0 - resolution: "@babel/helper-skip-transparent-expression-wrappers@npm:7.16.0" +"@babel/helper-skip-transparent-expression-wrappers@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/helper-skip-transparent-expression-wrappers@npm:7.18.9" dependencies: - "@babel/types": ^7.16.0 - checksum: b9ed2896eb253e6a85f472b0d4098ed80403758ad1a4e34b02b11e8276e3083297526758b1a3e6886e292987266f10622d7dbced3508cc22b296a74903b41cfb + "@babel/types": ^7.18.9 + checksum: 6e93ccd10248293082606a4b3e30eed32c6f796d378f6b662796c88f462f348aa368aadeb48eb410cfcc8250db93b2d6627c2e55662530f08fc25397e588d68a languageName: node linkType: hard -"@babel/helper-split-export-declaration@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/helper-split-export-declaration@npm:7.16.7" +"@babel/helper-split-export-declaration@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/helper-split-export-declaration@npm:7.18.6" dependencies: - "@babel/types": ^7.16.7 - checksum: e10aaf135465c55114627951b79115f24bc7af72ecbb58d541d66daf1edaee5dde7cae3ec8c3639afaf74526c03ae3ce723444e3b5b3dc77140c456cd84bcaa1 + "@babel/types": ^7.18.6 + checksum: c6d3dede53878f6be1d869e03e9ffbbb36f4897c7cc1527dc96c56d127d834ffe4520a6f7e467f5b6f3c2843ea0e81a7819d66ae02f707f6ac057f3d57943a2b languageName: node linkType: hard -"@babel/helper-validator-identifier@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/helper-validator-identifier@npm:7.16.7" - checksum: dbb3db9d184343152520a209b5684f5e0ed416109cde82b428ca9c759c29b10c7450657785a8b5c5256aa74acc6da491c1f0cf6b784939f7931ef82982051b69 +"@babel/helper-string-parser@npm:^7.18.10": + version: 7.18.10 + resolution: "@babel/helper-string-parser@npm:7.18.10" + checksum: d554a4393365b624916b5c00a4cc21c990c6617e7f3fe30be7d9731f107f12c33229a7a3db9d829bfa110d2eb9f04790745d421640e3bd245bb412dc0ea123c1 languageName: node linkType: hard -"@babel/helper-validator-option@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/helper-validator-option@npm:7.16.7" - checksum: c5ccc451911883cc9f12125d47be69434f28094475c1b9d2ada7c3452e6ac98a1ee8ddd364ca9e3f9855fcdee96cdeafa32543ebd9d17fee7a1062c202e80570 +"@babel/helper-validator-identifier@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/helper-validator-identifier@npm:7.18.6" + checksum: e295254d616bbe26e48c196a198476ab4d42a73b90478c9842536cf910ead887f5af6b5c4df544d3052a25ccb3614866fa808dc1e3a5a4291acd444e243c0648 languageName: node linkType: hard -"@babel/helper-wrap-function@npm:^7.16.8": - version: 7.16.8 - resolution: "@babel/helper-wrap-function@npm:7.16.8" +"@babel/helper-validator-option@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/helper-validator-option@npm:7.18.6" + checksum: f9cc6eb7cc5d759c5abf006402180f8d5e4251e9198197428a97e05d65eb2f8ae5a0ce73b1dfd2d35af41d0eb780627a64edf98a4e71f064eeeacef8de58f2cf + languageName: node + linkType: hard + +"@babel/helper-wrap-function@npm:^7.18.9": + version: 7.18.11 + resolution: "@babel/helper-wrap-function@npm:7.18.11" dependencies: - "@babel/helper-function-name": ^7.16.7 - "@babel/template": ^7.16.7 - "@babel/traverse": ^7.16.8 - "@babel/types": ^7.16.8 - checksum: d8aae4bacaf138d47dca1421ba82b41eac954cbb0ad17ab1c782825c6f2afe20076fbed926ab265967758336de5112d193a363128cd1c6967c66e0151174f797 + "@babel/helper-function-name": ^7.18.9 + "@babel/template": ^7.18.10 + "@babel/traverse": ^7.18.11 + "@babel/types": ^7.18.10 + checksum: e2fb909cdeb5c8688513261202cdeab7c6a8ac1f30daa5a1e0111631f270c26118c2e6b27014fc9f5d2c0ee1182fc40a3db2d30e45425587067f49dcae737dc9 languageName: node linkType: hard -"@babel/helpers@npm:^7.12.5, @babel/helpers@npm:^7.18.2": - version: 7.18.2 - resolution: "@babel/helpers@npm:7.18.2" +"@babel/helpers@npm:^7.12.5, @babel/helpers@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/helpers@npm:7.18.9" dependencies: - "@babel/template": ^7.16.7 - "@babel/traverse": ^7.18.2 - "@babel/types": ^7.18.2 - checksum: 94620242f23f6d5f9b83a02b1aa1632ffb05b0815e1bb53d3b46d64aa8e771066bba1db8bd267d9091fb00134cfaeda6a8d69d1d4cc2c89658631adfa077ae70 + "@babel/template": ^7.18.6 + "@babel/traverse": ^7.18.9 + "@babel/types": ^7.18.9 + checksum: d0bd8255d36bfc65dc52ce75f7fea778c70287da2d64981db4c84fbdf9581409ecbd6433deff1c81da3a5acf26d7e4c364b3a4445efacf88f4f48e77c5b34d8d languageName: node linkType: hard -"@babel/highlight@npm:^7.16.7": - version: 7.17.12 - resolution: "@babel/highlight@npm:7.17.12" +"@babel/highlight@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/highlight@npm:7.18.6" dependencies: - "@babel/helper-validator-identifier": ^7.16.7 + "@babel/helper-validator-identifier": ^7.18.6 chalk: ^2.0.0 js-tokens: ^4.0.0 - checksum: 841a11aa353113bcce662b47085085a379251bf8b09054e37e1e082da1bf0d59355a556192a6b5e9ee98e8ee6f1f2831ac42510633c5e7043e3744dda2d6b9d6 + checksum: 92d8ee61549de5ff5120e945e774728e5ccd57fd3b2ed6eace020ec744823d4a98e242be1453d21764a30a14769ecd62170fba28539b211799bbaf232bbb2789 languageName: node linkType: hard -"@babel/parser@npm:^7.12.7, @babel/parser@npm:^7.16.7, @babel/parser@npm:^7.17.10, @babel/parser@npm:^7.17.9, @babel/parser@npm:^7.18.0": - version: 7.18.3 - resolution: "@babel/parser@npm:7.18.3" +"@babel/parser@npm:^7.12.7, @babel/parser@npm:^7.17.9, @babel/parser@npm:^7.18.10, @babel/parser@npm:^7.18.13, @babel/parser@npm:^7.18.8": + version: 7.18.13 + resolution: "@babel/parser@npm:7.18.13" bin: parser: ./bin/babel-parser.js - checksum: 6894b3266f84b6c6b52bf09e7f61526efc35d8afa72ff0ad9aecb27a4b6de02d1ebc7f61fc3ae7c0fd8ecb5ac17083d1f27c1b3176e5eac41131d7160a9a7d88 + checksum: 8b41c35607668495d67d9a7c5f61768aaab26acf887efdadc2781aed54046981480ef40aeda0b84d61aed02cf0c4ff4b028d5f83ab85e17e2ddff318f9243b8b languageName: node linkType: hard -"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.17.12": - version: 7.17.12 - resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.17.12" +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.18.6" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0 - checksum: 6ef739b3a2b0ac0b22b60ff472c118163ceb8d414dd08c8186cc563fddc2be62ad4d8681e02074a1c7f0056a72e7146493a85d12ded02e50904b0009ed85d8bf + checksum: 845bd280c55a6a91d232cfa54eaf9708ec71e594676fe705794f494bb8b711d833b752b59d1a5c154695225880c23dbc9cab0e53af16fd57807976cd3ff41b8d languageName: node linkType: hard -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.17.12": - version: 7.17.12 - resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.17.12" +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.18.9" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 - "@babel/helper-skip-transparent-expression-wrappers": ^7.16.0 - "@babel/plugin-proposal-optional-chaining": ^7.17.12 + "@babel/helper-plugin-utils": ^7.18.9 + "@babel/helper-skip-transparent-expression-wrappers": ^7.18.9 + "@babel/plugin-proposal-optional-chaining": ^7.18.9 peerDependencies: "@babel/core": ^7.13.0 - checksum: 68520a8f26e56bc8d90c22133537a9819e82598e3c82007f30bdaf8898b0e12a7bfa0cd3044aca35a7f362fd6bc04e4cd8052a571fc2eb40ad8f1cf24e0fc45f + checksum: 93abb5cb179a13db171bfc2cdf79489598f43c50cc174f97a2b7bb1d44d24ade7109665a20cf4e317ad6c1c730f036f06478f7c7e789b4240be1abdb60d6452f languageName: node linkType: hard -"@babel/plugin-proposal-async-generator-functions@npm:^7.17.12": - version: 7.17.12 - resolution: "@babel/plugin-proposal-async-generator-functions@npm:7.17.12" +"@babel/plugin-proposal-async-generator-functions@npm:^7.18.10": + version: 7.18.10 + resolution: "@babel/plugin-proposal-async-generator-functions@npm:7.18.10" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 - "@babel/helper-remap-async-to-generator": ^7.16.8 + "@babel/helper-environment-visitor": ^7.18.9 + "@babel/helper-plugin-utils": ^7.18.9 + "@babel/helper-remap-async-to-generator": ^7.18.9 "@babel/plugin-syntax-async-generators": ^7.8.4 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 16a3c7f68a27031b4973b7c64ca009873c91b91afd7b3a4694ec7f1c6d8e91a6ee142eafd950113810fae122faa1031de71140333b2b1bd03d5367b1a05b1d91 + checksum: 3a6c25085021053830f6c57780118d3337935ac3309eef7f09b11e413d189eed8119d50cbddeb4c8c02f42f8cc01e62a4667b869be6e158f40030bafb92a0629 languageName: node linkType: hard -"@babel/plugin-proposal-class-properties@npm:^7.17.12": - version: 7.17.12 - resolution: "@babel/plugin-proposal-class-properties@npm:7.17.12" +"@babel/plugin-proposal-class-properties@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-proposal-class-properties@npm:7.18.6" dependencies: - "@babel/helper-create-class-features-plugin": ^7.17.12 - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-create-class-features-plugin": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 884df6a4617a18cdc2a630096b2a10954bcc94757c893bb01abd6702fdc73343ca5c611f4884c4634e0608f5e86c3093ea6b973ce00bf21b248ba54de92c837d + checksum: 49a78a2773ec0db56e915d9797e44fd079ab8a9b2e1716e0df07c92532f2c65d76aeda9543883916b8e0ff13606afeffa67c5b93d05b607bc87653ad18a91422 languageName: node linkType: hard -"@babel/plugin-proposal-class-static-block@npm:^7.18.0": - version: 7.18.0 - resolution: "@babel/plugin-proposal-class-static-block@npm:7.18.0" +"@babel/plugin-proposal-class-static-block@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-proposal-class-static-block@npm:7.18.6" dependencies: - "@babel/helper-create-class-features-plugin": ^7.18.0 - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-create-class-features-plugin": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.6 "@babel/plugin-syntax-class-static-block": ^7.14.5 peerDependencies: "@babel/core": ^7.12.0 - checksum: 70fd622fd7c62cca2aa99c70532766340a5c30105e35cb3f1187b450580d43adc78b3fcb1142ed339bcfccf84be95ea03407adf467331b318ce6874432736c89 + checksum: b8d7ae99ed5ad784f39e7820e3ac03841f91d6ed60ab4a98c61d6112253da36013e12807bae4ffed0ef3cb318e47debac112ed614e03b403fb8b075b09a828ee languageName: node linkType: hard -"@babel/plugin-proposal-dynamic-import@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-proposal-dynamic-import@npm:7.16.7" +"@babel/plugin-proposal-dynamic-import@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-proposal-dynamic-import@npm:7.18.6" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.18.6 "@babel/plugin-syntax-dynamic-import": ^7.8.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 5992012484fb8bda1451369350e475091954ed414dd9ef8654a3c4daa2db0205d4f29c94f5d3dedfbc5a434996375c8304586904337d6af938ac0f27a0033e23 + checksum: 96b1c8a8ad8171d39e9ab106be33bde37ae09b22fb2c449afee9a5edf3c537933d79d963dcdc2694d10677cb96da739cdf1b53454e6a5deab9801f28a818bb2f languageName: node linkType: hard -"@babel/plugin-proposal-export-namespace-from@npm:^7.17.12": - version: 7.17.12 - resolution: "@babel/plugin-proposal-export-namespace-from@npm:7.17.12" +"@babel/plugin-proposal-export-namespace-from@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/plugin-proposal-export-namespace-from@npm:7.18.9" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.18.9 "@babel/plugin-syntax-export-namespace-from": ^7.8.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 41c9cd4c0a5629b65725d2554867c15b199f534cea5538bd1ae379c0d13e7206d8590e23b23cb05a8b243e33e6eb88c1de3fd03a55cdbc6d4cf8634a6bebe43d + checksum: 84ff22bacc5d30918a849bfb7e0e90ae4c5b8d8b65f2ac881803d1cf9068dffbe53bd657b0e4bc4c20b4db301b1c85f1e74183cf29a0dd31e964bd4e97c363ef languageName: node linkType: hard -"@babel/plugin-proposal-json-strings@npm:^7.17.12": - version: 7.17.12 - resolution: "@babel/plugin-proposal-json-strings@npm:7.17.12" +"@babel/plugin-proposal-json-strings@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-proposal-json-strings@npm:7.18.6" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.18.6 "@babel/plugin-syntax-json-strings": ^7.8.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 8ed4ee3fbc28e44fac17c48bd95b5b8c3ffc852053a9fffd36ab498ec0b0ba069b8b2f5658edc18332748948433b9d3e1e376f564a1d65cb54592ba9943be09b + checksum: 25ba0e6b9d6115174f51f7c6787e96214c90dd4026e266976b248a2ed417fe50fddae72843ffb3cbe324014a18632ce5648dfac77f089da858022b49fd608cb3 languageName: node linkType: hard -"@babel/plugin-proposal-logical-assignment-operators@npm:^7.17.12": - version: 7.17.12 - resolution: "@babel/plugin-proposal-logical-assignment-operators@npm:7.17.12" +"@babel/plugin-proposal-logical-assignment-operators@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/plugin-proposal-logical-assignment-operators@npm:7.18.9" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.18.9 "@babel/plugin-syntax-logical-assignment-operators": ^7.10.4 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 0d48451836219b7beeca4be22a8aeb4a177a4944be4727afb94a4a11f201dde8b0b186dd2ad65b537d61e9af3fa1afda734f7096bec8602debd76d07aa342e21 + checksum: dd87fa4a48c6408c5e85dbd6405a65cc8fe909e3090030df46df90df64cdf3e74007381a58ed87608778ee597eff7395d215274009bb3f5d8964b2db5557754f languageName: node linkType: hard -"@babel/plugin-proposal-nullish-coalescing-operator@npm:^7.17.12": - version: 7.17.12 - resolution: "@babel/plugin-proposal-nullish-coalescing-operator@npm:7.17.12" +"@babel/plugin-proposal-nullish-coalescing-operator@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-proposal-nullish-coalescing-operator@npm:7.18.6" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.18.6 "@babel/plugin-syntax-nullish-coalescing-operator": ^7.8.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 7881d8005d0d4e17a94f3bfbfa4a0d8af016d2f62ed90912fabb8c5f8f0cc0a15fd412f09c230984c40b5c893086987d403c73198ef388ffcb3726ff72efc009 + checksum: 949c9ddcdecdaec766ee610ef98f965f928ccc0361dd87cf9f88cf4896a6ccd62fce063d4494778e50da99dea63d270a1be574a62d6ab81cbe9d85884bf55a7d languageName: node linkType: hard -"@babel/plugin-proposal-numeric-separator@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-proposal-numeric-separator@npm:7.16.7" +"@babel/plugin-proposal-numeric-separator@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-proposal-numeric-separator@npm:7.18.6" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.18.6 "@babel/plugin-syntax-numeric-separator": ^7.10.4 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 8e2fb0b32845908c67f80bc637a0968e28a66727d7ffb22b9c801dc355d88e865dc24aec586b00c922c23833ae5d26301b443b53609ea73d8344733cd48a1eca + checksum: f370ea584c55bf4040e1f78c80b4eeb1ce2e6aaa74f87d1a48266493c33931d0b6222d8cee3a082383d6bb648ab8d6b7147a06f974d3296ef3bc39c7851683ec languageName: node linkType: hard @@ -516,81 +525,81 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-object-rest-spread@npm:^7.18.0": - version: 7.18.0 - resolution: "@babel/plugin-proposal-object-rest-spread@npm:7.18.0" +"@babel/plugin-proposal-object-rest-spread@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/plugin-proposal-object-rest-spread@npm:7.18.9" dependencies: - "@babel/compat-data": ^7.17.10 - "@babel/helper-compilation-targets": ^7.17.10 - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/compat-data": ^7.18.8 + "@babel/helper-compilation-targets": ^7.18.9 + "@babel/helper-plugin-utils": ^7.18.9 "@babel/plugin-syntax-object-rest-spread": ^7.8.3 - "@babel/plugin-transform-parameters": ^7.17.12 + "@babel/plugin-transform-parameters": ^7.18.8 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 2b49bcf9a6b11fd8b6a1d4962a64f3c846a63f8340eca9824c907f75bfcff7422ca35b135607fc3ef2d4e7e77ce6b6d955b772dc3c1c39f7ed24a0d8a560ec78 + checksum: 66b9bae741d46edf1c96776d26dfe5d335981e57164ec2450583e3d20dfaa08a5137ffebb897e443913207789f9816bfec4ae845f38762c0196a60949eaffdba languageName: node linkType: hard -"@babel/plugin-proposal-optional-catch-binding@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-proposal-optional-catch-binding@npm:7.16.7" +"@babel/plugin-proposal-optional-catch-binding@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-proposal-optional-catch-binding@npm:7.18.6" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.18.6 "@babel/plugin-syntax-optional-catch-binding": ^7.8.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 4a422bb19a23cf80a245c60bea7adbe5dac8ff3bc1a62f05d7155e1eb68d401b13339c94dfd1f3d272972feeb45746f30d52ca0f8d5c63edf6891340878403df + checksum: 7b5b39fb5d8d6d14faad6cb68ece5eeb2fd550fb66b5af7d7582402f974f5bc3684641f7c192a5a57e0f59acfae4aada6786be1eba030881ddc590666eff4d1e languageName: node linkType: hard -"@babel/plugin-proposal-optional-chaining@npm:^7.17.12": - version: 7.17.12 - resolution: "@babel/plugin-proposal-optional-chaining@npm:7.17.12" +"@babel/plugin-proposal-optional-chaining@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/plugin-proposal-optional-chaining@npm:7.18.9" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 - "@babel/helper-skip-transparent-expression-wrappers": ^7.16.0 + "@babel/helper-plugin-utils": ^7.18.9 + "@babel/helper-skip-transparent-expression-wrappers": ^7.18.9 "@babel/plugin-syntax-optional-chaining": ^7.8.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: a27b220573441a0ad3eecf8ddcb249556a64de45add236791d76cfa164a8fd34181857528fa7d21d03d6b004e7c043bd929cce068e611ee1ac72aaf4d397aa12 + checksum: f2db40e26172f07c50b635cb61e1f36165de3ba868fcf608d967642f0d044b7c6beb0e7ecf17cbd421144b99e1eae7ad6031ded92925343bb0ed1d08707b514f languageName: node linkType: hard -"@babel/plugin-proposal-private-methods@npm:^7.17.12": - version: 7.17.12 - resolution: "@babel/plugin-proposal-private-methods@npm:7.17.12" +"@babel/plugin-proposal-private-methods@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-proposal-private-methods@npm:7.18.6" dependencies: - "@babel/helper-create-class-features-plugin": ^7.17.12 - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-create-class-features-plugin": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: a1e5bd6a0a541af55d133d7bcf51ff8eb4ac7417a30f518c2f38107d7d033a3d5b7128ea5b3a910b458d7ceb296179b6ff9d972be60d1c686113d25fede8bed3 + checksum: 22d8502ee96bca99ad2c8393e8493e2b8d4507576dd054490fd8201a36824373440106f5b098b6d821b026c7e72b0424ff4aeca69ed5f42e48f029d3a156d5ad languageName: node linkType: hard -"@babel/plugin-proposal-private-property-in-object@npm:^7.17.12": - version: 7.17.12 - resolution: "@babel/plugin-proposal-private-property-in-object@npm:7.17.12" +"@babel/plugin-proposal-private-property-in-object@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-proposal-private-property-in-object@npm:7.18.6" dependencies: - "@babel/helper-annotate-as-pure": ^7.16.7 - "@babel/helper-create-class-features-plugin": ^7.17.12 - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-annotate-as-pure": ^7.18.6 + "@babel/helper-create-class-features-plugin": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.6 "@babel/plugin-syntax-private-property-in-object": ^7.14.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 056cb77994b2ee367301cdf8c5b7ed71faf26d60859bbba1368b342977481b0884712a1b97fbd9b091750162923d0265bf901119d46002775aa66e4a9f30f411 + checksum: c8e56a972930730345f39f2384916fd8e711b3f4b4eae2ca9740e99958980118120d5cc9b6ac150f0965a5a35f825910e2c3013d90be3e9993ab6111df444569 languageName: node linkType: hard -"@babel/plugin-proposal-unicode-property-regex@npm:^7.17.12, @babel/plugin-proposal-unicode-property-regex@npm:^7.4.4": - version: 7.17.12 - resolution: "@babel/plugin-proposal-unicode-property-regex@npm:7.17.12" +"@babel/plugin-proposal-unicode-property-regex@npm:^7.18.6, @babel/plugin-proposal-unicode-property-regex@npm:^7.4.4": + version: 7.18.6 + resolution: "@babel/plugin-proposal-unicode-property-regex@npm:7.18.6" dependencies: - "@babel/helper-create-regexp-features-plugin": ^7.17.12 - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-create-regexp-features-plugin": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 0e4194510415ed11849f1617fcb32d996df746ba93cd05ebbabecb63cfc02c0e97b585c97da3dcf68acdd3c8b71cfae964abe5d5baba6bd3977a475d9225ad9e + checksum: a8575ecb7ff24bf6c6e94808d5c84bb5a0c6dd7892b54f09f4646711ba0ee1e1668032b3c43e3e1dfec2c5716c302e851ac756c1645e15882d73df6ad21ae951 languageName: node linkType: hard @@ -649,14 +658,14 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-import-assertions@npm:^7.17.12": - version: 7.17.12 - resolution: "@babel/plugin-syntax-import-assertions@npm:7.17.12" +"@babel/plugin-syntax-import-assertions@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-syntax-import-assertions@npm:7.18.6" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: fef25c3247d18dc7b8e432ed07f4afb92d70113fcfc3db0ca52388f8083b4bd60f88fe9ec0085e8a5a6daf18a619042376e76e2b4bd9470cddb7362cd268bea5 + checksum: 54918a05375325ba0c60bc81abfb261e6f118bed2de94e4c17dca9a2006fc25e13b1a8b5504b9a881238ea394fd2f098f60b2eb3a392585d6348874565445e7b languageName: node linkType: hard @@ -682,14 +691,14 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-jsx@npm:^7.12.13, @babel/plugin-syntax-jsx@npm:^7.17.12": - version: 7.17.12 - resolution: "@babel/plugin-syntax-jsx@npm:7.17.12" +"@babel/plugin-syntax-jsx@npm:^7.17.12, @babel/plugin-syntax-jsx@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-syntax-jsx@npm:7.18.6" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 6acd0bbca8c3e0100ad61f3b7d0b0111cd241a0710b120b298c4aa0e07be02eccbcca61ede1e7678ade1783a0979f20305b62263df6767fa3fbf658670d82af5 + checksum: 6d37ea972970195f1ffe1a54745ce2ae456e0ac6145fae9aa1480f297248b262ea6ebb93010eddb86ebfacb94f57c05a1fc5d232b9a67325b09060299d515c67 languageName: node linkType: hard @@ -781,517 +790,517 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-typescript@npm:^7.17.12": - version: 7.17.12 - resolution: "@babel/plugin-syntax-typescript@npm:7.17.12" +"@babel/plugin-syntax-typescript@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-syntax-typescript@npm:7.18.6" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 50ab09f1953a2b0586cff9e29bf7cea3d886b48c1361a861687c2aef46356c6d73778c3341b0c051dc82a34417f19e9d759ae918353c5a98d25e85f2f6d24181 + checksum: 2cde73725ec51118ebf410bf02d78781c03fa4d3185993fcc9d253b97443381b621c44810084c5dd68b92eb8bdfae0e5b163e91b32bebbb33852383d1815c05d languageName: node linkType: hard -"@babel/plugin-transform-arrow-functions@npm:^7.17.12": - version: 7.17.12 - resolution: "@babel/plugin-transform-arrow-functions@npm:7.17.12" +"@babel/plugin-transform-arrow-functions@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-arrow-functions@npm:7.18.6" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 48f99e74f523641696d5d9fb3f5f02497eca2e97bc0e9b8230a47f388e37dc5fd84b8b29e9f5a0c82d63403f7ba5f085a28e26939678f6e917d5c01afd884b50 + checksum: 900f5c695755062b91eec74da6f9092f40b8fada099058b92576f1e23c55e9813ec437051893a9b3c05cefe39e8ac06303d4a91b384e1c03dd8dc1581ea11602 languageName: node linkType: hard -"@babel/plugin-transform-async-to-generator@npm:^7.17.12": - version: 7.17.12 - resolution: "@babel/plugin-transform-async-to-generator@npm:7.17.12" +"@babel/plugin-transform-async-to-generator@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-async-to-generator@npm:7.18.6" dependencies: - "@babel/helper-module-imports": ^7.16.7 - "@babel/helper-plugin-utils": ^7.17.12 - "@babel/helper-remap-async-to-generator": ^7.16.8 + "@babel/helper-module-imports": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.6 + "@babel/helper-remap-async-to-generator": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 052dd56eb3b10bc31f5aaced0f75fc7307713f74049ccfb91cd087bebfc890a6d462b59445c5299faaca9030814172cac290c941c76b731a38dcb267377c9187 + checksum: c2cca47468cf1aeefdc7ec35d670e195c86cee4de28a1970648c46a88ce6bd1806ef0bab27251b9e7fb791bb28a64dcd543770efd899f28ee5f7854e64e873d3 languageName: node linkType: hard -"@babel/plugin-transform-block-scoped-functions@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.16.7" +"@babel/plugin-transform-block-scoped-functions@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.18.6" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 591e9f75437bb32ebf9506d28d5c9659c66c0e8e0c19b12924d808d898e68309050aadb783ccd70bb4956555067326ecfa17a402bc77eb3ece3c6863d40b9016 + checksum: 0a0df61f94601e3666bf39f2cc26f5f7b22a94450fb93081edbed967bd752ce3f81d1227fefd3799f5ee2722171b5e28db61379234d1bb85b6ec689589f99d7e languageName: node linkType: hard -"@babel/plugin-transform-block-scoping@npm:^7.17.12": - version: 7.17.12 - resolution: "@babel/plugin-transform-block-scoping@npm:7.17.12" +"@babel/plugin-transform-block-scoping@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/plugin-transform-block-scoping@npm:7.18.9" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.18.9 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: ea3d4d88e38367d62a1029d204c5cc0ac410b00779179c8507448001c64784bf8e34c6fa57f23d8b95a835541a2fc67d1076650b1efc99c78f699de354472e49 + checksum: f8064ea431eb7aa349dc5b6be87a650f912b48cd65afde917e8644f6f840d7f9d2ce4795f2aa3955aa5b23a73d4ad38abd03386ae109b4b8702b746c6d35bda3 languageName: node linkType: hard -"@babel/plugin-transform-classes@npm:^7.17.12": - version: 7.17.12 - resolution: "@babel/plugin-transform-classes@npm:7.17.12" +"@babel/plugin-transform-classes@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/plugin-transform-classes@npm:7.18.9" dependencies: - "@babel/helper-annotate-as-pure": ^7.16.7 - "@babel/helper-environment-visitor": ^7.16.7 - "@babel/helper-function-name": ^7.17.9 - "@babel/helper-optimise-call-expression": ^7.16.7 - "@babel/helper-plugin-utils": ^7.17.12 - "@babel/helper-replace-supers": ^7.16.7 - "@babel/helper-split-export-declaration": ^7.16.7 + "@babel/helper-annotate-as-pure": ^7.18.6 + "@babel/helper-environment-visitor": ^7.18.9 + "@babel/helper-function-name": ^7.18.9 + "@babel/helper-optimise-call-expression": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.9 + "@babel/helper-replace-supers": ^7.18.9 + "@babel/helper-split-export-declaration": ^7.18.6 globals: ^11.1.0 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 0127b1cc432373965edf28cbfd9e85df5bc77e974ceb80ba32691e050e8fb6792f207d1941529c81d1b9e7a6e82da26ecc445f6f547f0ad5076cd2b27adc18ac + checksum: d7e953c0cf32af64e75db1277d2556c04635f32691ef462436897840be6f8021d4f85ee96134cb796a12dda549cf53346fedf96b671885f881bc4037c9d120ad languageName: node linkType: hard -"@babel/plugin-transform-computed-properties@npm:^7.17.12": - version: 7.17.12 - resolution: "@babel/plugin-transform-computed-properties@npm:7.17.12" +"@babel/plugin-transform-computed-properties@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/plugin-transform-computed-properties@npm:7.18.9" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.18.9 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 5d05418617e0967bec4818556b7febb6f8c40813e32035f0bd6b7dbd7b9d63e9ab7c7c8fd7bd05bab2a599dad58e7b69957d9559b41079d112c219bbc3649aa1 + checksum: a6bfbea207827d77592628973c0e8cc3319db636506bdc6e81e21582de2e767890e6975b382d0511e9ec3773b9f43691185df90832883bbf9251f688d27fbc1d languageName: node linkType: hard -"@babel/plugin-transform-destructuring@npm:^7.18.0": - version: 7.18.0 - resolution: "@babel/plugin-transform-destructuring@npm:7.18.0" +"@babel/plugin-transform-destructuring@npm:^7.18.9": + version: 7.18.13 + resolution: "@babel/plugin-transform-destructuring@npm:7.18.13" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.18.9 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: d85d60737c3b05c4db71bc94270e952122d360bd6ebf91b5f98cf16fb8564558b615d115354fe0ef41e2aae9c4540e6e16144284d881ecaef687693736cd2a79 + checksum: 83e44ec93a4cfbf69376db8836d00ec803820081bf0f8b6cea73a9b3cd320b8285768d5b385744af4a27edda4b6502245c52d3ed026ea61356faf57bfe78effb languageName: node linkType: hard -"@babel/plugin-transform-dotall-regex@npm:^7.16.7, @babel/plugin-transform-dotall-regex@npm:^7.4.4": - version: 7.16.7 - resolution: "@babel/plugin-transform-dotall-regex@npm:7.16.7" +"@babel/plugin-transform-dotall-regex@npm:^7.18.6, @babel/plugin-transform-dotall-regex@npm:^7.4.4": + version: 7.18.6 + resolution: "@babel/plugin-transform-dotall-regex@npm:7.18.6" dependencies: - "@babel/helper-create-regexp-features-plugin": ^7.16.7 - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-create-regexp-features-plugin": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 554570dddfd5bfd87ab307be520f69a3d4ed2d2db677c165971b400d4c96656d0c165b318e69f1735612dcd12e04c0ee257697dc26800e8a572ca73bc05fa0f4 + checksum: cbe5d7063eb8f8cca24cd4827bc97f5641166509e58781a5f8aa47fb3d2d786ce4506a30fca2e01f61f18792783a5cb5d96bf5434c3dd1ad0de8c9cc625a53da languageName: node linkType: hard -"@babel/plugin-transform-duplicate-keys@npm:^7.17.12": - version: 7.17.12 - resolution: "@babel/plugin-transform-duplicate-keys@npm:7.17.12" +"@babel/plugin-transform-duplicate-keys@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/plugin-transform-duplicate-keys@npm:7.18.9" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.18.9 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: fb6ad550538830b0dc5b1b547734359f2d782209570e9d61fe9b84a6929af570fcc38ab579a67ee7cd6a832147db91a527f4cceb1248974f006fe815980816bb + checksum: 220bf4a9fec5c4d4a7b1de38810350260e8ea08481bf78332a464a21256a95f0df8cd56025f346238f09b04f8e86d4158fafc9f4af57abaef31637e3b58bd4fe languageName: node linkType: hard -"@babel/plugin-transform-exponentiation-operator@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-exponentiation-operator@npm:7.16.7" +"@babel/plugin-transform-exponentiation-operator@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-exponentiation-operator@npm:7.18.6" dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor": ^7.16.7 - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-builder-binary-assignment-operator-visitor": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 8082c79268f5b1552292bd3abbfed838a1131747e62000146e70670707b518602e907bbe3aef0fda824a2eebe995a9d897bd2336a039c5391743df01608673b0 + checksum: 7f70222f6829c82a36005508d34ddbe6fd0974ae190683a8670dd6ff08669aaf51fef2209d7403f9bd543cb2d12b18458016c99a6ed0332ccedb3ea127b01229 languageName: node linkType: hard -"@babel/plugin-transform-for-of@npm:^7.18.1": - version: 7.18.1 - resolution: "@babel/plugin-transform-for-of@npm:7.18.1" +"@babel/plugin-transform-for-of@npm:^7.18.8": + version: 7.18.8 + resolution: "@babel/plugin-transform-for-of@npm:7.18.8" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: cdc6e1f1170218cc6ac5b26b4b8f011ec5c36666101e00e0061aaa5772969b093bad5b2af8ce908c184126d5bb0c26b89dd4debb96b2375aba2e20e427a623a8 + checksum: ca64c623cf0c7a80ab6f07ebd3e6e4ade95e2ae806696f70b43eafe6394fa8ce21f2b1ffdd15df2067f7363d2ecfe26472a97c6c774403d2163fa05f50c98f17 languageName: node linkType: hard -"@babel/plugin-transform-function-name@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-function-name@npm:7.16.7" +"@babel/plugin-transform-function-name@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/plugin-transform-function-name@npm:7.18.9" dependencies: - "@babel/helper-compilation-targets": ^7.16.7 - "@babel/helper-function-name": ^7.16.7 - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-compilation-targets": ^7.18.9 + "@babel/helper-function-name": ^7.18.9 + "@babel/helper-plugin-utils": ^7.18.9 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 4d97d0b84461cdd5d5aa2d010cdaf30f1f83a92a0dedd3686cbc7e90dc1249a70246f5bac0c1f3cd3f1dbfb03f7aac437776525a0c90cafd459776ea4fcc6bde + checksum: 62dd9c6cdc9714704efe15545e782ee52d74dc73916bf954b4d3bee088fb0ec9e3c8f52e751252433656c09f744b27b757fc06ed99bcde28e8a21600a1d8e597 languageName: node linkType: hard -"@babel/plugin-transform-literals@npm:^7.17.12": - version: 7.17.12 - resolution: "@babel/plugin-transform-literals@npm:7.17.12" +"@babel/plugin-transform-literals@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/plugin-transform-literals@npm:7.18.9" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.18.9 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 09280fc1ed23b81deafd4fcd7a35d6c0944668de2317f14c1b8b78c5c201f71a063bb8d174d2fc97d86df480ff23104c8919d3aacf19f33c2b5ada584203bf1c + checksum: 3458dd2f1a47ac51d9d607aa18f3d321cbfa8560a985199185bed5a906bb0c61ba85575d386460bac9aed43fdd98940041fae5a67dff286f6f967707cff489f8 languageName: node linkType: hard -"@babel/plugin-transform-member-expression-literals@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-member-expression-literals@npm:7.16.7" +"@babel/plugin-transform-member-expression-literals@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-member-expression-literals@npm:7.18.6" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: fdf5b22abab2b770e69348ce7f99796c3e0e1e7ce266afdbe995924284704930fa989323bdbda7070db8adb45a72f39eaa1dbebf18b67fc44035ec00c6ae3300 + checksum: 35a3d04f6693bc6b298c05453d85ee6e41cc806538acb6928427e0e97ae06059f97d2f07d21495fcf5f70d3c13a242e2ecbd09d5c1fcb1b1a73ff528dcb0b695 languageName: node linkType: hard -"@babel/plugin-transform-modules-amd@npm:^7.18.0": - version: 7.18.0 - resolution: "@babel/plugin-transform-modules-amd@npm:7.18.0" +"@babel/plugin-transform-modules-amd@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-modules-amd@npm:7.18.6" dependencies: - "@babel/helper-module-transforms": ^7.18.0 - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-module-transforms": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.6 babel-plugin-dynamic-import-node: ^2.3.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: bed3ff5cd81f236981360fc4a6fd2262685c1202772c657ce3ab95b7930437f8fa22361021b481c977b6f47988dfcc07c7782a1c91b90d3a5552c91401f4631a + checksum: f60c4c4e0eaec41e42c003cbab44305da7a8e05b2c9bdfc2b3fe0f9e1d7441c959ff5248aa03e350abe530e354028cbf3aa20bf07067b11510997dad8dd39be0 languageName: node linkType: hard -"@babel/plugin-transform-modules-commonjs@npm:^7.18.2": - version: 7.18.2 - resolution: "@babel/plugin-transform-modules-commonjs@npm:7.18.2" +"@babel/plugin-transform-modules-commonjs@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-modules-commonjs@npm:7.18.6" dependencies: - "@babel/helper-module-transforms": ^7.18.0 - "@babel/helper-plugin-utils": ^7.17.12 - "@babel/helper-simple-access": ^7.18.2 + "@babel/helper-module-transforms": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.6 + "@babel/helper-simple-access": ^7.18.6 babel-plugin-dynamic-import-node: ^2.3.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 99c1c5ce9c353e29eb680ebb5bdf27c076c6403e133a066999298de642423cc7f38cfbac02372d33ed73278da13be23c4be7d60169c3e27bd900a373e61a599a + checksum: 7e356e3df8a6a8542cced7491ec5b1cc1093a88d216a59e63a5d2b9fe9d193cbea864f680a41429e41a4f9ecec930aa5b0b8f57e2b17b3b4d27923bb12ba5d14 languageName: node linkType: hard -"@babel/plugin-transform-modules-systemjs@npm:^7.18.0": - version: 7.18.0 - resolution: "@babel/plugin-transform-modules-systemjs@npm:7.18.0" +"@babel/plugin-transform-modules-systemjs@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/plugin-transform-modules-systemjs@npm:7.18.9" dependencies: - "@babel/helper-hoist-variables": ^7.16.7 - "@babel/helper-module-transforms": ^7.18.0 - "@babel/helper-plugin-utils": ^7.17.12 - "@babel/helper-validator-identifier": ^7.16.7 + "@babel/helper-hoist-variables": ^7.18.6 + "@babel/helper-module-transforms": ^7.18.9 + "@babel/helper-plugin-utils": ^7.18.9 + "@babel/helper-validator-identifier": ^7.18.6 babel-plugin-dynamic-import-node: ^2.3.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 80fccfc546aab76238d3f4aeb454f61ed885670578f1ab6dc063bba5b5d4cbdf821439ac6ca8bc24449eed752359600b47be717196103d2eabba06de1bf3f732 + checksum: 6122d9901ed5dc56d9db843efc9249fe20d769a11989bbbf5a806ed4f086def949185198aa767888481babf70fc52b6b3e297a991e2b02b4f34ffb03d998d1e3 languageName: node linkType: hard -"@babel/plugin-transform-modules-umd@npm:^7.18.0": - version: 7.18.0 - resolution: "@babel/plugin-transform-modules-umd@npm:7.18.0" +"@babel/plugin-transform-modules-umd@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-modules-umd@npm:7.18.6" dependencies: - "@babel/helper-module-transforms": ^7.18.0 - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-module-transforms": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 4081a79cfd4c6fda785c2137f9f2721e35c06a9d2f23c304172838d12e9317a24d3cb5b652a9db61e58319b370c57b1b44991429efe709679f98e114d98597fb + checksum: c3b6796c6f4579f1ba5ab0cdcc73910c1e9c8e1e773c507c8bb4da33072b3ae5df73c6d68f9126dab6e99c24ea8571e1563f8710d7c421fac1cde1e434c20153 languageName: node linkType: hard -"@babel/plugin-transform-named-capturing-groups-regex@npm:^7.17.12": - version: 7.17.12 - resolution: "@babel/plugin-transform-named-capturing-groups-regex@npm:7.17.12" +"@babel/plugin-transform-named-capturing-groups-regex@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-named-capturing-groups-regex@npm:7.18.6" dependencies: - "@babel/helper-create-regexp-features-plugin": ^7.17.12 - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-create-regexp-features-plugin": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0 - checksum: cff9d91d0abd87871da6574583e79093ed75d5faecea45b6a13350ba243b1a595d349a6e7d906f5dfdf6c69c643cba9df662c3d01eaa187c5b1a01cb5838e848 + checksum: 6ef64aa3dad68df139eeaa7b6e9bb626be8f738ed5ed4db765d516944b1456d513b6bad3bb60fff22babe73de26436fd814a4228705b2d3d2fdb272c31da35e2 languageName: node linkType: hard -"@babel/plugin-transform-new-target@npm:^7.17.12": - version: 7.17.12 - resolution: "@babel/plugin-transform-new-target@npm:7.17.12" +"@babel/plugin-transform-new-target@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-new-target@npm:7.18.6" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: bec26350fa49c9a9431d23b4ff234f8eb60554b8cdffca432a94038406aae5701014f343568c0e0cc8afae6f95d492f6bae0d0e2c101c1a484fb20eec75b2c07 + checksum: bd780e14f46af55d0ae8503b3cb81ca86dcc73ed782f177e74f498fff934754f9e9911df1f8f3bd123777eed7c1c1af4d66abab87c8daae5403e7719a6b845d1 languageName: node linkType: hard -"@babel/plugin-transform-object-super@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-object-super@npm:7.16.7" +"@babel/plugin-transform-object-super@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-object-super@npm:7.18.6" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 - "@babel/helper-replace-supers": ^7.16.7 + "@babel/helper-plugin-utils": ^7.18.6 + "@babel/helper-replace-supers": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 46e3c879f4a93e904f2ecf83233d40c48c832bdbd82a67cab1f432db9aa51702e40d9e51e5800613e12299974f90f4ed3869e1273dbca8642984266320c5f341 + checksum: 0fcb04e15deea96ae047c21cb403607d49f06b23b4589055993365ebd7a7d7541334f06bf9642e90075e66efce6ebaf1eb0ef066fbbab802d21d714f1aac3aef languageName: node linkType: hard -"@babel/plugin-transform-parameters@npm:^7.12.1, @babel/plugin-transform-parameters@npm:^7.17.12": - version: 7.17.12 - resolution: "@babel/plugin-transform-parameters@npm:7.17.12" +"@babel/plugin-transform-parameters@npm:^7.12.1, @babel/plugin-transform-parameters@npm:^7.18.8": + version: 7.18.8 + resolution: "@babel/plugin-transform-parameters@npm:7.18.8" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: d9ed5ec61dc460835bade8fa710b42ec9f207bd448ead7e8abd46b87db0afedbb3f51284700fd2a6892fdf6544ec9b949c505c6542c5ba0a41ca4e8749af00f0 + checksum: 2b5863300da60face8a250d91da16294333bd5626e9721b13a3ba2078bd2a5a190e32c6e7a1323d5f547f579aeb2804ff49a62a55fcad2b1d099e55a55b788ea languageName: node linkType: hard -"@babel/plugin-transform-property-literals@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-property-literals@npm:7.16.7" +"@babel/plugin-transform-property-literals@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-property-literals@npm:7.18.6" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: b5674458991a9b0e8738989d70faa88c7f98ed3df923c119f1225069eed72fe5e0ce947b1adc91e378f5822fbdeb7a672f496fd1c75c4babcc88169e3a7c3229 + checksum: 1c16e64de554703f4b547541de2edda6c01346dd3031d4d29e881aa7733785cd26d53611a4ccf5353f4d3e69097bb0111c0a93ace9e683edd94fea28c4484144 languageName: node linkType: hard -"@babel/plugin-transform-react-constant-elements@npm:^7.14.5": - version: 7.17.12 - resolution: "@babel/plugin-transform-react-constant-elements@npm:7.17.12" +"@babel/plugin-transform-react-constant-elements@npm:^7.17.12": + version: 7.18.12 + resolution: "@babel/plugin-transform-react-constant-elements@npm:7.18.12" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.18.9 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: e39e256e492b82346a4e6c3f2a7802bbe7332b132b42d6c62aa16bee2b905e0669d941fff87aadedfd3afa0aa8d52b9ee624d15d7eb5f06f20567a4b8b531d2c + checksum: d83fbc65e8eb32b64fc83c64436d85dba44e2c358b906e5eb3709d22b05bdeada2f92af1e85e26fda88bb8d688b06546b9a98fee17c82563ae00f19827ba0c79 languageName: node linkType: hard -"@babel/plugin-transform-react-display-name@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-react-display-name@npm:7.16.7" +"@babel/plugin-transform-react-display-name@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-react-display-name@npm:7.18.6" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 483154413671ab0a25ae37520b7cf5bfab0958c484a3707c6799b1f1436d1e51481bcc03fbfcdbf90bf6b46818d931ae35e515141d8354c3287351b4467376ba + checksum: 51c087ab9e41ef71a29335587da28417536c6f816c292e092ffc0e0985d2f032656801d4dd502213ce32481f4ba6c69402993ffa67f0818a07606ff811e4be49 languageName: node linkType: hard -"@babel/plugin-transform-react-jsx-development@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-react-jsx-development@npm:7.16.7" +"@babel/plugin-transform-react-jsx-development@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-react-jsx-development@npm:7.18.6" dependencies: - "@babel/plugin-transform-react-jsx": ^7.16.7 + "@babel/plugin-transform-react-jsx": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 697c71cb0ac9647a9b8c6f1aca99767cf06197f6c0b5d1f2e0c01f641e0706a380779f06836fdb941d3aa171f868091270fbe9fcfbfbcc2a24df5e60e04545e8 + checksum: ec9fa65db66f938b75c45e99584367779ac3e0af8afc589187262e1337c7c4205ea312877813ae4df9fb93d766627b8968d74ac2ba702e4883b1dbbe4953ecee languageName: node linkType: hard -"@babel/plugin-transform-react-jsx@npm:^7.16.7, @babel/plugin-transform-react-jsx@npm:^7.17.12": - version: 7.17.12 - resolution: "@babel/plugin-transform-react-jsx@npm:7.17.12" +"@babel/plugin-transform-react-jsx@npm:^7.18.6": + version: 7.18.10 + resolution: "@babel/plugin-transform-react-jsx@npm:7.18.10" dependencies: - "@babel/helper-annotate-as-pure": ^7.16.7 - "@babel/helper-module-imports": ^7.16.7 - "@babel/helper-plugin-utils": ^7.17.12 - "@babel/plugin-syntax-jsx": ^7.17.12 - "@babel/types": ^7.17.12 + "@babel/helper-annotate-as-pure": ^7.18.6 + "@babel/helper-module-imports": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.9 + "@babel/plugin-syntax-jsx": ^7.18.6 + "@babel/types": ^7.18.10 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 02e9974d14821173bb8e84db4bdfccd546bfdbf445d91d6345f953591f16306cf5741861d72e0d0910f3ffa7d4084fafed99cedf736e7ba8bed0cf64320c2ea6 + checksum: 1aacfb0286d5b95c45bbda6cf026f9e81a261298b5921cd55b357581c9b3681fe70ba56846fae86cf63908ea8e07d0e3dd8192d663d6bddd75a7fe4c091cd724 languageName: node linkType: hard -"@babel/plugin-transform-react-pure-annotations@npm:^7.16.7": - version: 7.18.0 - resolution: "@babel/plugin-transform-react-pure-annotations@npm:7.18.0" +"@babel/plugin-transform-react-pure-annotations@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-react-pure-annotations@npm:7.18.6" dependencies: - "@babel/helper-annotate-as-pure": ^7.16.7 - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-annotate-as-pure": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 908b2ee74a13eb16455f77c14ad7ffb1c2c0c44f5e34b05541e82634c56b405d2589b574fbb734edb2012e3dd1b16edbe9d7e80626886108088b4f07f27a231b + checksum: 97c4873d409088f437f9084d084615948198dd87fc6723ada0e7e29c5a03623c2f3e03df3f52e7e7d4d23be32a08ea00818bff302812e48713c706713bd06219 languageName: node linkType: hard -"@babel/plugin-transform-regenerator@npm:^7.18.0": - version: 7.18.0 - resolution: "@babel/plugin-transform-regenerator@npm:7.18.0" +"@babel/plugin-transform-regenerator@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-regenerator@npm:7.18.6" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.18.6 regenerator-transform: ^0.15.0 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: ebacf2bbe9e2fb6f2bd7996e19b41bfc9848628950ae06a1a832802a0b8e32a32003c6b89318da6ca521f79045c91324dcb4c97247ed56f86fa58d7401a7316f + checksum: 60bd482cb0343c714f85c3e19a13b3b5fa05ee336c079974091c0b35e263307f4e661f4555dff90707a87d5efe19b1d51835db44455405444ac1813e268ad750 languageName: node linkType: hard -"@babel/plugin-transform-reserved-words@npm:^7.17.12": - version: 7.17.12 - resolution: "@babel/plugin-transform-reserved-words@npm:7.17.12" +"@babel/plugin-transform-reserved-words@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-reserved-words@npm:7.18.6" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: d8a617cb79ca5852ac2736a9f81c15a3b0760919720c3b9069a864e2288006ebcaab557dbb36a3eba936defd6699f82e3bf894915925aa9185f5d9bcbf3b29fd + checksum: 0738cdc30abdae07c8ec4b233b30c31f68b3ff0eaa40eddb45ae607c066127f5fa99ddad3c0177d8e2832e3a7d3ad115775c62b431ebd6189c40a951b867a80c languageName: node linkType: hard -"@babel/plugin-transform-runtime@npm:^7.17.10": - version: 7.18.2 - resolution: "@babel/plugin-transform-runtime@npm:7.18.2" +"@babel/plugin-transform-runtime@npm:^7.18.6": + version: 7.18.10 + resolution: "@babel/plugin-transform-runtime@npm:7.18.10" dependencies: - "@babel/helper-module-imports": ^7.16.7 - "@babel/helper-plugin-utils": ^7.17.12 - babel-plugin-polyfill-corejs2: ^0.3.0 - babel-plugin-polyfill-corejs3: ^0.5.0 - babel-plugin-polyfill-regenerator: ^0.3.0 + "@babel/helper-module-imports": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.9 + babel-plugin-polyfill-corejs2: ^0.3.2 + babel-plugin-polyfill-corejs3: ^0.5.3 + babel-plugin-polyfill-regenerator: ^0.4.0 semver: ^6.3.0 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 2f41b3a9213ddb795de0f7598e9e6dfe20781bab4346e34585f2ceab187dac03860db6e41c4819fb3ddb51111ee910c8b45971d6e886cd715e06ba0f3ea19481 + checksum: 98c18680b4258b8bd3f04926b73c72ae77037d5ea5b50761ca35de15896bf0d04bedabde39a81be56dbd4859c96ffaa7103fbefb5d5b58a36e0a80381e4a146c languageName: node linkType: hard -"@babel/plugin-transform-shorthand-properties@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-shorthand-properties@npm:7.16.7" +"@babel/plugin-transform-shorthand-properties@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-shorthand-properties@npm:7.18.6" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: ca381ecf8f48696512172deca40af46b1f64e3497186fdc2c9009286d8f06b468c4d61cdc392dc8b0c165298117dda67be9e2ff0e99d7691b0503f1240d4c62b + checksum: b8e4e8acc2700d1e0d7d5dbfd4fdfb935651913de6be36e6afb7e739d8f9ca539a5150075a0f9b79c88be25ddf45abb912fe7abf525f0b80f5b9d9860de685d7 languageName: node linkType: hard -"@babel/plugin-transform-spread@npm:^7.17.12": - version: 7.17.12 - resolution: "@babel/plugin-transform-spread@npm:7.17.12" +"@babel/plugin-transform-spread@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/plugin-transform-spread@npm:7.18.9" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 - "@babel/helper-skip-transparent-expression-wrappers": ^7.16.0 + "@babel/helper-plugin-utils": ^7.18.9 + "@babel/helper-skip-transparent-expression-wrappers": ^7.18.9 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 3a95e4f163d598c0efc9d983e5ce3e8716998dd2af62af8102b11cb8d6383c71b74c7106adbce73cda6e48d3d3e927627847d36d76c2eb688cd0e2e07f67fb51 + checksum: 59489dd6212bd21debdf77746d9fa02dfe36f7062dc08742b8841d04312a26ea37bc0d71c71a6e37c3ab81dce744faa7f23fa94b0915593458f6adc35c087766 languageName: node linkType: hard -"@babel/plugin-transform-sticky-regex@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-sticky-regex@npm:7.16.7" +"@babel/plugin-transform-sticky-regex@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-sticky-regex@npm:7.18.6" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: d59e20121ff0a483e29364eff8bb42cd8a0b7a3158141eea5b6f219227e5b873ea70f317f65037c0f557887a692ac993b72f99641a37ea6ec0ae8000bfab1343 + checksum: 68ea18884ae9723443ffa975eb736c8c0d751265859cd3955691253f7fee37d7a0f7efea96c8a062876af49a257a18ea0ed5fea0d95a7b3611ce40f7ee23aee3 languageName: node linkType: hard -"@babel/plugin-transform-template-literals@npm:^7.18.2": - version: 7.18.2 - resolution: "@babel/plugin-transform-template-literals@npm:7.18.2" +"@babel/plugin-transform-template-literals@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/plugin-transform-template-literals@npm:7.18.9" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.18.9 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: bc0102ed8c789e5bc01053088e2de85b82cebcd4d57af9fdc32ca62f559d3dd19c33e9d26caa71c5fd8e94152e5ce4fc4da19badc2d537620e6dea83bce7eb05 + checksum: 3d2fcd79b7c345917f69b92a85bdc3ddd68ce2c87dc70c7d61a8373546ccd1f5cb8adc8540b49dfba08e1b82bb7b3bbe23a19efdb2b9c994db2db42906ca9fb2 languageName: node linkType: hard -"@babel/plugin-transform-typeof-symbol@npm:^7.17.12": - version: 7.17.12 - resolution: "@babel/plugin-transform-typeof-symbol@npm:7.17.12" +"@babel/plugin-transform-typeof-symbol@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/plugin-transform-typeof-symbol@npm:7.18.9" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.18.9 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: e30bd03c8abc1b095f8b2a10289df6850e3bc3cd0aea1cbc29050aa3b421cbb77d0428b0cd012333632a7a930dc8301cd888e762b2dd601e7dc5dac50f4140c9 + checksum: e754e0d8b8a028c52e10c148088606e3f7a9942c57bd648fc0438e5b4868db73c386a5ed47ab6d6f0594aae29ee5ffc2ffc0f7ebee7fae560a066d6dea811cd4 languageName: node linkType: hard -"@babel/plugin-transform-typescript@npm:^7.17.12": - version: 7.18.1 - resolution: "@babel/plugin-transform-typescript@npm:7.18.1" +"@babel/plugin-transform-typescript@npm:^7.18.6": + version: 7.18.12 + resolution: "@babel/plugin-transform-typescript@npm:7.18.12" dependencies: - "@babel/helper-create-class-features-plugin": ^7.18.0 - "@babel/helper-plugin-utils": ^7.17.12 - "@babel/plugin-syntax-typescript": ^7.17.12 + "@babel/helper-create-class-features-plugin": ^7.18.9 + "@babel/helper-plugin-utils": ^7.18.9 + "@babel/plugin-syntax-typescript": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 3edc35769662bdff85da8cdfca65c79a03e856834bb0884e13740bb2d723781b7a6dae083496e64330f28d331b266961c558316ac7d92acc9c589fcc7b12df11 + checksum: 87e9b783ef712697a9d3bd72d0345ea4ea71b4676f9b88da0a30fe4b8a81f453a5badee788bb4dc849616af84d674d728a6ec4248f14a75bfb0b4de5bcce7431 languageName: node linkType: hard -"@babel/plugin-transform-unicode-escapes@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-unicode-escapes@npm:7.16.7" +"@babel/plugin-transform-unicode-escapes@npm:^7.18.10": + version: 7.18.10 + resolution: "@babel/plugin-transform-unicode-escapes@npm:7.18.10" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.18.9 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: d10c3b5baa697ca2d9ecce2fd7705014d7e1ddd86ed684ccec378f7ad4d609ab970b5546d6cdbe242089ecfc7a79009d248cf4f8ee87d629485acfb20c0d9160 + checksum: f5baca55cb3c11bc08ec589f5f522d85c1ab509b4d11492437e45027d64ae0b22f0907bd1381e8d7f2a436384bb1f9ad89d19277314242c5c2671a0f91d0f9cd languageName: node linkType: hard -"@babel/plugin-transform-unicode-regex@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-unicode-regex@npm:7.16.7" +"@babel/plugin-transform-unicode-regex@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-unicode-regex@npm:7.18.6" dependencies: - "@babel/helper-create-regexp-features-plugin": ^7.16.7 - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-create-regexp-features-plugin": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: ef7721cfb11b269809555b1c392732566c49f6ced58e0e990c0e81e58a934bbab3072dcbe92d3a20d60e3e41036ecf987bcc63a7cde90711a350ad774667e5e6 - languageName: node - linkType: hard - -"@babel/preset-env@npm:^7.15.6, @babel/preset-env@npm:^7.17.10": - version: 7.18.2 - resolution: "@babel/preset-env@npm:7.18.2" - dependencies: - "@babel/compat-data": ^7.17.10 - "@babel/helper-compilation-targets": ^7.18.2 - "@babel/helper-plugin-utils": ^7.17.12 - "@babel/helper-validator-option": ^7.16.7 - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": ^7.17.12 - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": ^7.17.12 - "@babel/plugin-proposal-async-generator-functions": ^7.17.12 - "@babel/plugin-proposal-class-properties": ^7.17.12 - "@babel/plugin-proposal-class-static-block": ^7.18.0 - "@babel/plugin-proposal-dynamic-import": ^7.16.7 - "@babel/plugin-proposal-export-namespace-from": ^7.17.12 - "@babel/plugin-proposal-json-strings": ^7.17.12 - "@babel/plugin-proposal-logical-assignment-operators": ^7.17.12 - "@babel/plugin-proposal-nullish-coalescing-operator": ^7.17.12 - "@babel/plugin-proposal-numeric-separator": ^7.16.7 - "@babel/plugin-proposal-object-rest-spread": ^7.18.0 - "@babel/plugin-proposal-optional-catch-binding": ^7.16.7 - "@babel/plugin-proposal-optional-chaining": ^7.17.12 - "@babel/plugin-proposal-private-methods": ^7.17.12 - "@babel/plugin-proposal-private-property-in-object": ^7.17.12 - "@babel/plugin-proposal-unicode-property-regex": ^7.17.12 + checksum: d9e18d57536a2d317fb0b7c04f8f55347f3cfacb75e636b4c6fa2080ab13a3542771b5120e726b598b815891fc606d1472ac02b749c69fd527b03847f22dc25e + languageName: node + linkType: hard + +"@babel/preset-env@npm:^7.18.2, @babel/preset-env@npm:^7.18.6": + version: 7.18.10 + resolution: "@babel/preset-env@npm:7.18.10" + dependencies: + "@babel/compat-data": ^7.18.8 + "@babel/helper-compilation-targets": ^7.18.9 + "@babel/helper-plugin-utils": ^7.18.9 + "@babel/helper-validator-option": ^7.18.6 + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": ^7.18.6 + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": ^7.18.9 + "@babel/plugin-proposal-async-generator-functions": ^7.18.10 + "@babel/plugin-proposal-class-properties": ^7.18.6 + "@babel/plugin-proposal-class-static-block": ^7.18.6 + "@babel/plugin-proposal-dynamic-import": ^7.18.6 + "@babel/plugin-proposal-export-namespace-from": ^7.18.9 + "@babel/plugin-proposal-json-strings": ^7.18.6 + "@babel/plugin-proposal-logical-assignment-operators": ^7.18.9 + "@babel/plugin-proposal-nullish-coalescing-operator": ^7.18.6 + "@babel/plugin-proposal-numeric-separator": ^7.18.6 + "@babel/plugin-proposal-object-rest-spread": ^7.18.9 + "@babel/plugin-proposal-optional-catch-binding": ^7.18.6 + "@babel/plugin-proposal-optional-chaining": ^7.18.9 + "@babel/plugin-proposal-private-methods": ^7.18.6 + "@babel/plugin-proposal-private-property-in-object": ^7.18.6 + "@babel/plugin-proposal-unicode-property-regex": ^7.18.6 "@babel/plugin-syntax-async-generators": ^7.8.4 "@babel/plugin-syntax-class-properties": ^7.12.13 "@babel/plugin-syntax-class-static-block": ^7.14.5 "@babel/plugin-syntax-dynamic-import": ^7.8.3 "@babel/plugin-syntax-export-namespace-from": ^7.8.3 - "@babel/plugin-syntax-import-assertions": ^7.17.12 + "@babel/plugin-syntax-import-assertions": ^7.18.6 "@babel/plugin-syntax-json-strings": ^7.8.3 "@babel/plugin-syntax-logical-assignment-operators": ^7.10.4 "@babel/plugin-syntax-nullish-coalescing-operator": ^7.8.3 @@ -1301,48 +1310,48 @@ __metadata: "@babel/plugin-syntax-optional-chaining": ^7.8.3 "@babel/plugin-syntax-private-property-in-object": ^7.14.5 "@babel/plugin-syntax-top-level-await": ^7.14.5 - "@babel/plugin-transform-arrow-functions": ^7.17.12 - "@babel/plugin-transform-async-to-generator": ^7.17.12 - "@babel/plugin-transform-block-scoped-functions": ^7.16.7 - "@babel/plugin-transform-block-scoping": ^7.17.12 - "@babel/plugin-transform-classes": ^7.17.12 - "@babel/plugin-transform-computed-properties": ^7.17.12 - "@babel/plugin-transform-destructuring": ^7.18.0 - "@babel/plugin-transform-dotall-regex": ^7.16.7 - "@babel/plugin-transform-duplicate-keys": ^7.17.12 - "@babel/plugin-transform-exponentiation-operator": ^7.16.7 - "@babel/plugin-transform-for-of": ^7.18.1 - "@babel/plugin-transform-function-name": ^7.16.7 - "@babel/plugin-transform-literals": ^7.17.12 - "@babel/plugin-transform-member-expression-literals": ^7.16.7 - "@babel/plugin-transform-modules-amd": ^7.18.0 - "@babel/plugin-transform-modules-commonjs": ^7.18.2 - "@babel/plugin-transform-modules-systemjs": ^7.18.0 - "@babel/plugin-transform-modules-umd": ^7.18.0 - "@babel/plugin-transform-named-capturing-groups-regex": ^7.17.12 - "@babel/plugin-transform-new-target": ^7.17.12 - "@babel/plugin-transform-object-super": ^7.16.7 - "@babel/plugin-transform-parameters": ^7.17.12 - "@babel/plugin-transform-property-literals": ^7.16.7 - "@babel/plugin-transform-regenerator": ^7.18.0 - "@babel/plugin-transform-reserved-words": ^7.17.12 - "@babel/plugin-transform-shorthand-properties": ^7.16.7 - "@babel/plugin-transform-spread": ^7.17.12 - "@babel/plugin-transform-sticky-regex": ^7.16.7 - "@babel/plugin-transform-template-literals": ^7.18.2 - "@babel/plugin-transform-typeof-symbol": ^7.17.12 - "@babel/plugin-transform-unicode-escapes": ^7.16.7 - "@babel/plugin-transform-unicode-regex": ^7.16.7 + "@babel/plugin-transform-arrow-functions": ^7.18.6 + "@babel/plugin-transform-async-to-generator": ^7.18.6 + "@babel/plugin-transform-block-scoped-functions": ^7.18.6 + "@babel/plugin-transform-block-scoping": ^7.18.9 + "@babel/plugin-transform-classes": ^7.18.9 + "@babel/plugin-transform-computed-properties": ^7.18.9 + "@babel/plugin-transform-destructuring": ^7.18.9 + "@babel/plugin-transform-dotall-regex": ^7.18.6 + "@babel/plugin-transform-duplicate-keys": ^7.18.9 + "@babel/plugin-transform-exponentiation-operator": ^7.18.6 + "@babel/plugin-transform-for-of": ^7.18.8 + "@babel/plugin-transform-function-name": ^7.18.9 + "@babel/plugin-transform-literals": ^7.18.9 + "@babel/plugin-transform-member-expression-literals": ^7.18.6 + "@babel/plugin-transform-modules-amd": ^7.18.6 + "@babel/plugin-transform-modules-commonjs": ^7.18.6 + "@babel/plugin-transform-modules-systemjs": ^7.18.9 + "@babel/plugin-transform-modules-umd": ^7.18.6 + "@babel/plugin-transform-named-capturing-groups-regex": ^7.18.6 + "@babel/plugin-transform-new-target": ^7.18.6 + "@babel/plugin-transform-object-super": ^7.18.6 + "@babel/plugin-transform-parameters": ^7.18.8 + "@babel/plugin-transform-property-literals": ^7.18.6 + "@babel/plugin-transform-regenerator": ^7.18.6 + "@babel/plugin-transform-reserved-words": ^7.18.6 + "@babel/plugin-transform-shorthand-properties": ^7.18.6 + "@babel/plugin-transform-spread": ^7.18.9 + "@babel/plugin-transform-sticky-regex": ^7.18.6 + "@babel/plugin-transform-template-literals": ^7.18.9 + "@babel/plugin-transform-typeof-symbol": ^7.18.9 + "@babel/plugin-transform-unicode-escapes": ^7.18.10 + "@babel/plugin-transform-unicode-regex": ^7.18.6 "@babel/preset-modules": ^0.1.5 - "@babel/types": ^7.18.2 - babel-plugin-polyfill-corejs2: ^0.3.0 - babel-plugin-polyfill-corejs3: ^0.5.0 - babel-plugin-polyfill-regenerator: ^0.3.0 + "@babel/types": ^7.18.10 + babel-plugin-polyfill-corejs2: ^0.3.2 + babel-plugin-polyfill-corejs3: ^0.5.3 + babel-plugin-polyfill-regenerator: ^0.4.0 core-js-compat: ^3.22.1 semver: ^6.3.0 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: f81892a7970cb34643b93917cbbc9b581d5066d892639867521f4a85ec258e69362a37bbb7b899b351e71d26095a97cd2d6e35e5f9ee110715146e0ccc19e700 + checksum: 36eeb7157021091c8047703833b7a28e4963865d16968a5b9dbffe1eb05e44307a8d29ad45d81fd23817f68290b52921c42f513a93996c7083d23d5e2cea0c6b languageName: node linkType: hard @@ -1361,90 +1370,91 @@ __metadata: languageName: node linkType: hard -"@babel/preset-react@npm:^7.14.5, @babel/preset-react@npm:^7.16.7": - version: 7.17.12 - resolution: "@babel/preset-react@npm:7.17.12" +"@babel/preset-react@npm:^7.17.12, @babel/preset-react@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/preset-react@npm:7.18.6" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 - "@babel/helper-validator-option": ^7.16.7 - "@babel/plugin-transform-react-display-name": ^7.16.7 - "@babel/plugin-transform-react-jsx": ^7.17.12 - "@babel/plugin-transform-react-jsx-development": ^7.16.7 - "@babel/plugin-transform-react-pure-annotations": ^7.16.7 + "@babel/helper-plugin-utils": ^7.18.6 + "@babel/helper-validator-option": ^7.18.6 + "@babel/plugin-transform-react-display-name": ^7.18.6 + "@babel/plugin-transform-react-jsx": ^7.18.6 + "@babel/plugin-transform-react-jsx-development": ^7.18.6 + "@babel/plugin-transform-react-pure-annotations": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 369712150d6a152720069db8d024320f3d9d2a6611e9b0be4aa03dcab8502fa0e9efc0693c93ba2d818d5243c9d03b015163d76efe65df600f15b9b0a206f674 + checksum: 540d9cf0a0cc0bb07e6879994e6fb7152f87dafbac880b56b65e2f528134c7ba33e0cd140b58700c77b2ebf4c81fa6468fed0ba391462d75efc7f8c1699bb4c3 languageName: node linkType: hard -"@babel/preset-typescript@npm:^7.15.0, @babel/preset-typescript@npm:^7.16.7": - version: 7.17.12 - resolution: "@babel/preset-typescript@npm:7.17.12" +"@babel/preset-typescript@npm:^7.17.12, @babel/preset-typescript@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/preset-typescript@npm:7.18.6" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 - "@babel/helper-validator-option": ^7.16.7 - "@babel/plugin-transform-typescript": ^7.17.12 + "@babel/helper-plugin-utils": ^7.18.6 + "@babel/helper-validator-option": ^7.18.6 + "@babel/plugin-transform-typescript": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: f4ee9eeb0ef631a47d1c9bd7f6e365ae0bacefa3f47c702b03c51652ea764c267b26fdcf2814718b26c73accdd0fff7fcec1bb2d00625a967ecd7dac2f5fdce1 + checksum: 7fe0da5103eb72d3cf39cf3e138a794c8cdd19c0b38e3e101507eef519c46a87a0d6d0e8bc9e28a13ea2364001ebe7430b9d75758aab4c3c3a8db9a487b9dc7c languageName: node linkType: hard -"@babel/runtime-corejs3@npm:^7.17.9": - version: 7.18.3 - resolution: "@babel/runtime-corejs3@npm:7.18.3" +"@babel/runtime-corejs3@npm:^7.18.6": + version: 7.18.9 + resolution: "@babel/runtime-corejs3@npm:7.18.9" dependencies: core-js-pure: ^3.20.2 regenerator-runtime: ^0.13.4 - checksum: 50319e107e4c3dc6662404daf1079ab1ecd1cb232577bf50e645c5051fa8977f6ce48a44aa1d158ce2beaec76a43df4fc404999bf4f03c66719c3f8b8fe50a4e + checksum: 249158b660ac996fa4f4b0d1ab5810db060af40fac4d0bb5da23f55539a151313ae254aa64afc2ab7000d95167824e21a689f74bc24b36fd0f5ca030d522133d languageName: node linkType: hard -"@babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.3, @babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.17.9, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7": - version: 7.18.3 - resolution: "@babel/runtime@npm:7.18.3" +"@babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.3, @babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7": + version: 7.18.9 + resolution: "@babel/runtime@npm:7.18.9" dependencies: regenerator-runtime: ^0.13.4 - checksum: db8526226aa02cfa35a5a7ac1a34b5f303c62a1f000c7db48cb06c6290e616483e5036ab3c4e7a84d0f3be6d4e2148d5fe5cec9564bf955f505c3e764b83d7f1 + checksum: 36dd736baba7164e82b3cc9d43e081f0cb2d05ff867ad39cac515d99546cee75b7f782018b02a3dcf5f2ef3d27f319faa68965fdfec49d4912c60c6002353a2e languageName: node linkType: hard -"@babel/template@npm:^7.12.7, @babel/template@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/template@npm:7.16.7" +"@babel/template@npm:^7.12.7, @babel/template@npm:^7.18.10, @babel/template@npm:^7.18.6": + version: 7.18.10 + resolution: "@babel/template@npm:7.18.10" dependencies: - "@babel/code-frame": ^7.16.7 - "@babel/parser": ^7.16.7 - "@babel/types": ^7.16.7 - checksum: 10cd112e89276e00f8b11b55a51c8b2f1262c318283a980f4d6cdb0286dc05734b9aaeeb9f3ad3311900b09bc913e02343fcaa9d4a4f413964aaab04eb84ac4a + "@babel/code-frame": ^7.18.6 + "@babel/parser": ^7.18.10 + "@babel/types": ^7.18.10 + checksum: 93a6aa094af5f355a72bd55f67fa1828a046c70e46f01b1606e6118fa1802b6df535ca06be83cc5a5e834022be95c7b714f0a268b5f20af984465a71e28f1473 languageName: node linkType: hard -"@babel/traverse@npm:^7.12.9, @babel/traverse@npm:^7.13.0, @babel/traverse@npm:^7.16.8, @babel/traverse@npm:^7.17.10, @babel/traverse@npm:^7.18.0, @babel/traverse@npm:^7.18.2": - version: 7.18.2 - resolution: "@babel/traverse@npm:7.18.2" +"@babel/traverse@npm:^7.12.9, @babel/traverse@npm:^7.18.11, @babel/traverse@npm:^7.18.13, @babel/traverse@npm:^7.18.8, @babel/traverse@npm:^7.18.9": + version: 7.18.13 + resolution: "@babel/traverse@npm:7.18.13" dependencies: - "@babel/code-frame": ^7.16.7 - "@babel/generator": ^7.18.2 - "@babel/helper-environment-visitor": ^7.18.2 - "@babel/helper-function-name": ^7.17.9 - "@babel/helper-hoist-variables": ^7.16.7 - "@babel/helper-split-export-declaration": ^7.16.7 - "@babel/parser": ^7.18.0 - "@babel/types": ^7.18.2 + "@babel/code-frame": ^7.18.6 + "@babel/generator": ^7.18.13 + "@babel/helper-environment-visitor": ^7.18.9 + "@babel/helper-function-name": ^7.18.9 + "@babel/helper-hoist-variables": ^7.18.6 + "@babel/helper-split-export-declaration": ^7.18.6 + "@babel/parser": ^7.18.13 + "@babel/types": ^7.18.13 debug: ^4.1.0 globals: ^11.1.0 - checksum: e21c2d550bf610406cf21ef6fbec525cb1d80b9d6d71af67552478a24ee371203cb4025b23b110ae7288a62a874ad5898daad19ad23daa95dfc8ab47a47a092f + checksum: 1a2ef738fac4968feba6385787a3f8f7357d08e7739ecc5b37d8ff5668935253a03290f700f02a85ccfd369d5898625f0722d7733bff2ef91504f6cd8b836f19 languageName: node linkType: hard -"@babel/types@npm:^7.12.7, @babel/types@npm:^7.15.6, @babel/types@npm:^7.16.0, @babel/types@npm:^7.16.7, @babel/types@npm:^7.16.8, @babel/types@npm:^7.17.0, @babel/types@npm:^7.17.12, @babel/types@npm:^7.18.0, @babel/types@npm:^7.18.2, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": - version: 7.18.2 - resolution: "@babel/types@npm:7.18.2" +"@babel/types@npm:^7.12.7, @babel/types@npm:^7.17.0, @babel/types@npm:^7.18.10, @babel/types@npm:^7.18.13, @babel/types@npm:^7.18.4, @babel/types@npm:^7.18.6, @babel/types@npm:^7.18.9, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": + version: 7.18.13 + resolution: "@babel/types@npm:7.18.13" dependencies: - "@babel/helper-validator-identifier": ^7.16.7 + "@babel/helper-string-parser": ^7.18.10 + "@babel/helper-validator-identifier": ^7.18.6 to-fast-properties: ^2.0.0 - checksum: 3750bcb9ef6f36ecf0c1477cf6010cd23f2db5cb93f6771ba84c07c08aa005934532bc81e9067192f85214c43e16731e0e3c244773071879967fd1cd22ba2144 + checksum: abc3ad1f3b6864df0ea0e778bcdf7d2c5ee2293811192962d50e8a8c05c1aeec90a48275f53b2a45aad882ed8bef9477ae1f8e70ac1d44d039e14930d1388dcc languageName: node linkType: hard @@ -1455,44 +1465,45 @@ __metadata: languageName: node linkType: hard -"@docusaurus/core@npm:2.0.0-beta.20, @docusaurus/core@npm:^2.0.0-beta.20": - version: 2.0.0-beta.20 - resolution: "@docusaurus/core@npm:2.0.0-beta.20" +"@docusaurus/core@npm:2.1.0, @docusaurus/core@npm:^2.0.0-beta.20": + version: 2.1.0 + resolution: "@docusaurus/core@npm:2.1.0" dependencies: - "@babel/core": ^7.17.10 - "@babel/generator": ^7.17.10 + "@babel/core": ^7.18.6 + "@babel/generator": ^7.18.7 "@babel/plugin-syntax-dynamic-import": ^7.8.3 - "@babel/plugin-transform-runtime": ^7.17.10 - "@babel/preset-env": ^7.17.10 - "@babel/preset-react": ^7.16.7 - "@babel/preset-typescript": ^7.16.7 - "@babel/runtime": ^7.17.9 - "@babel/runtime-corejs3": ^7.17.9 - "@babel/traverse": ^7.17.10 - "@docusaurus/cssnano-preset": 2.0.0-beta.20 - "@docusaurus/logger": 2.0.0-beta.20 - "@docusaurus/mdx-loader": 2.0.0-beta.20 + "@babel/plugin-transform-runtime": ^7.18.6 + "@babel/preset-env": ^7.18.6 + "@babel/preset-react": ^7.18.6 + "@babel/preset-typescript": ^7.18.6 + "@babel/runtime": ^7.18.6 + "@babel/runtime-corejs3": ^7.18.6 + "@babel/traverse": ^7.18.8 + "@docusaurus/cssnano-preset": 2.1.0 + "@docusaurus/logger": 2.1.0 + "@docusaurus/mdx-loader": 2.1.0 "@docusaurus/react-loadable": 5.5.2 - "@docusaurus/utils": 2.0.0-beta.20 - "@docusaurus/utils-common": 2.0.0-beta.20 - "@docusaurus/utils-validation": 2.0.0-beta.20 - "@slorber/static-site-generator-webpack-plugin": ^4.0.4 + "@docusaurus/utils": 2.1.0 + "@docusaurus/utils-common": 2.1.0 + "@docusaurus/utils-validation": 2.1.0 + "@slorber/static-site-generator-webpack-plugin": ^4.0.7 "@svgr/webpack": ^6.2.1 - autoprefixer: ^10.4.5 + autoprefixer: ^10.4.7 babel-loader: ^8.2.5 - babel-plugin-dynamic-import-node: 2.3.0 + babel-plugin-dynamic-import-node: ^2.3.3 boxen: ^6.2.1 + chalk: ^4.1.2 chokidar: ^3.5.3 clean-css: ^5.3.0 cli-table3: ^0.6.2 combine-promises: ^1.1.0 commander: ^5.1.0 - copy-webpack-plugin: ^10.2.4 - core-js: ^3.22.3 + copy-webpack-plugin: ^11.0.0 + core-js: ^3.23.3 css-loader: ^6.7.1 - css-minimizer-webpack-plugin: ^3.4.1 - cssnano: ^5.1.7 - del: ^6.0.0 + css-minimizer-webpack-plugin: ^4.0.0 + cssnano: ^5.1.12 + del: ^6.1.1 detect-port: ^1.3.0 escape-html: ^1.0.3 eta: ^1.12.3 @@ -1504,30 +1515,29 @@ __metadata: import-fresh: ^3.3.0 leven: ^3.1.0 lodash: ^4.17.21 - mini-css-extract-plugin: ^2.6.0 - postcss: ^8.4.13 - postcss-loader: ^6.2.1 + mini-css-extract-plugin: ^2.6.1 + postcss: ^8.4.14 + postcss-loader: ^7.0.0 prompts: ^2.4.2 react-dev-utils: ^12.0.1 react-helmet-async: ^1.3.0 react-loadable: "npm:@docusaurus/react-loadable@5.5.2" react-loadable-ssr-addon-v5-slorber: ^1.0.1 - react-router: ^5.2.0 + react-router: ^5.3.3 react-router-config: ^5.1.1 - react-router-dom: ^5.2.0 - remark-admonitions: ^1.2.1 + react-router-dom: ^5.3.3 rtl-detect: ^1.0.4 semver: ^7.3.7 serve-handler: ^6.1.3 shelljs: ^0.8.5 - terser-webpack-plugin: ^5.3.1 + terser-webpack-plugin: ^5.3.3 tslib: ^2.4.0 update-notifier: ^5.1.0 url-loader: ^4.1.1 wait-on: ^6.0.1 - webpack: ^5.72.0 + webpack: ^5.73.0 webpack-bundle-analyzer: ^4.5.0 - webpack-dev-server: ^4.8.1 + webpack-dev-server: ^4.9.3 webpack-merge: ^5.8.0 webpackbar: ^5.0.2 peerDependencies: @@ -1535,52 +1545,53 @@ __metadata: react-dom: ^16.8.4 || ^17.0.0 bin: docusaurus: bin/docusaurus.mjs - checksum: a060258c22a73c209583d83e3fad6941742436f6c2abc2d306cc99688eee2ed99a514c855984ea46ecfe202e25ab05ad249dc711234c56b7f39508136d0479c4 + checksum: c8f12d87d0e456784a28f2d55bdd31c0ea65f2cf35446f3faa063fc0651e5037553de84b051b1a264799dfdff35d337fb4656574a50269554b07ebe6a64eb2e5 languageName: node linkType: hard -"@docusaurus/cssnano-preset@npm:2.0.0-beta.20": - version: 2.0.0-beta.20 - resolution: "@docusaurus/cssnano-preset@npm:2.0.0-beta.20" +"@docusaurus/cssnano-preset@npm:2.1.0": + version: 2.1.0 + resolution: "@docusaurus/cssnano-preset@npm:2.1.0" dependencies: - cssnano-preset-advanced: ^5.3.3 - postcss: ^8.4.13 + cssnano-preset-advanced: ^5.3.8 + postcss: ^8.4.14 postcss-sort-media-queries: ^4.2.1 - checksum: 3576722fd3bcf0c8d32f0b303ea9de67eb333000b791e26d825c478a34bc3af7a52e2bde311cc51c7004f037dd8766cd94c8a7cc2e7d12025f2b13a93c823e17 + tslib: ^2.4.0 + checksum: 3589dcd8dc24e13598bdc9194470bbe3633dadf758db7860b9782df0e82adab5b4661167eeba2d49cce2f6a1e84b6126b1e9186c0d2bc3495f013f7fd25864e1 languageName: node linkType: hard -"@docusaurus/logger@npm:2.0.0-beta.20": - version: 2.0.0-beta.20 - resolution: "@docusaurus/logger@npm:2.0.0-beta.20" +"@docusaurus/logger@npm:2.1.0": + version: 2.1.0 + resolution: "@docusaurus/logger@npm:2.1.0" dependencies: chalk: ^4.1.2 tslib: ^2.4.0 - checksum: 71ac220746f09bdf0180369460c44c324a0bc931617d9868ccc2b739ab6f15a800c277cf62d54c1c354f5db3251b558006ef4ad7512dd2f21060c437e1e69e5e + checksum: af13df70b65b5ffedb3faafc9b9a1a26380ff33967e306bf0f3c7cf168efcec8d488712cbfefe5e60a1b416bde6e451b800a978477508bbae2a19c38250e86a5 languageName: node linkType: hard -"@docusaurus/lqip-loader@npm:2.0.0-beta.20": - version: 2.0.0-beta.20 - resolution: "@docusaurus/lqip-loader@npm:2.0.0-beta.20" +"@docusaurus/lqip-loader@npm:2.1.0": + version: 2.1.0 + resolution: "@docusaurus/lqip-loader@npm:2.1.0" dependencies: - "@docusaurus/logger": 2.0.0-beta.20 + "@docusaurus/logger": 2.1.0 file-loader: ^6.2.0 lodash: ^4.17.21 - sharp: ^0.30.4 + sharp: ^0.30.7 tslib: ^2.4.0 - checksum: 3d65bcebb8a93e15ce28892168ebe38732a031b5f7edd48df6e4b00a4411bb2d62cb2d5c754d7c84a1c18672dd1a7b3e73500ad90ef8ef5eccc08123f4e43568 + checksum: 15bbf5a3930caac43e38f6ca7a4493a600b455f91ba8ffdffaf84cda5c5ca538b12edba54f41ce8c6568a91679915718099d9cc3df91ff66ddcd5293ec5beaab languageName: node linkType: hard -"@docusaurus/mdx-loader@npm:2.0.0-beta.20": - version: 2.0.0-beta.20 - resolution: "@docusaurus/mdx-loader@npm:2.0.0-beta.20" +"@docusaurus/mdx-loader@npm:2.1.0": + version: 2.1.0 + resolution: "@docusaurus/mdx-loader@npm:2.1.0" dependencies: - "@babel/parser": ^7.17.10 - "@babel/traverse": ^7.17.10 - "@docusaurus/logger": 2.0.0-beta.20 - "@docusaurus/utils": 2.0.0-beta.20 + "@babel/parser": ^7.18.8 + "@babel/traverse": ^7.18.8 + "@docusaurus/logger": 2.1.0 + "@docusaurus/utils": 2.1.0 "@mdx-js/mdx": ^1.6.22 escape-html: ^1.0.3 file-loader: ^6.2.0 @@ -1590,117 +1601,124 @@ __metadata: remark-emoji: ^2.2.0 stringify-object: ^3.3.0 tslib: ^2.4.0 + unified: ^9.2.2 unist-util-visit: ^2.0.3 url-loader: ^4.1.1 - webpack: ^5.72.0 + webpack: ^5.73.0 peerDependencies: react: ^16.8.4 || ^17.0.0 react-dom: ^16.8.4 || ^17.0.0 - checksum: 722018c2973ff1187d29d7fe53db7db902df928e7badbeaa866f912d077638855bd6c9bccfa270e0d05f0a7dc98b024fb1bb16c1cb7614b4140a2d9517debbca + checksum: 799982ad50e182f4428d13a52c2790eba5b3cb84744220bd55fd64672a5388305f07cac078581dc66047775e3ccb30c4e49d8c1b285a2ebe38670859896cfe5c languageName: node linkType: hard -"@docusaurus/module-type-aliases@npm:2.0.0-beta.20": - version: 2.0.0-beta.20 - resolution: "@docusaurus/module-type-aliases@npm:2.0.0-beta.20" +"@docusaurus/module-type-aliases@npm:2.1.0": + version: 2.1.0 + resolution: "@docusaurus/module-type-aliases@npm:2.1.0" dependencies: - "@docusaurus/types": 2.0.0-beta.20 + "@docusaurus/react-loadable": 5.5.2 + "@docusaurus/types": 2.1.0 + "@types/history": ^4.7.11 "@types/react": "*" "@types/react-router-config": "*" "@types/react-router-dom": "*" react-helmet-async: "*" + react-loadable: "npm:@docusaurus/react-loadable@5.5.2" peerDependencies: react: "*" react-dom: "*" - checksum: adcae941361d0d6b6d02f1d685bc17a560ba610ef76fe528b712d2e568ac449bfe3fdc918aee44ba6b8a22411dc47813a501e0ecd8ba027281f18c2e08458823 + checksum: 229e792fbc6b272944d1d61d3375256ff9dde61344bc32d60ccfa35be8ee535cb32b0ac697f997a10cae984000f56ad9c246a14bf111b4b45e71bc8f9f4dd1d6 languageName: node linkType: hard -"@docusaurus/plugin-content-blog@npm:2.0.0-beta.20, @docusaurus/plugin-content-blog@npm:^2.0.0-beta.20": - version: 2.0.0-beta.20 - resolution: "@docusaurus/plugin-content-blog@npm:2.0.0-beta.20" - dependencies: - "@docusaurus/core": 2.0.0-beta.20 - "@docusaurus/logger": 2.0.0-beta.20 - "@docusaurus/mdx-loader": 2.0.0-beta.20 - "@docusaurus/utils": 2.0.0-beta.20 - "@docusaurus/utils-common": 2.0.0-beta.20 - "@docusaurus/utils-validation": 2.0.0-beta.20 - cheerio: ^1.0.0-rc.10 +"@docusaurus/plugin-content-blog@npm:2.1.0, @docusaurus/plugin-content-blog@npm:^2.0.0-beta.20": + version: 2.1.0 + resolution: "@docusaurus/plugin-content-blog@npm:2.1.0" + dependencies: + "@docusaurus/core": 2.1.0 + "@docusaurus/logger": 2.1.0 + "@docusaurus/mdx-loader": 2.1.0 + "@docusaurus/types": 2.1.0 + "@docusaurus/utils": 2.1.0 + "@docusaurus/utils-common": 2.1.0 + "@docusaurus/utils-validation": 2.1.0 + cheerio: ^1.0.0-rc.12 feed: ^4.2.2 fs-extra: ^10.1.0 lodash: ^4.17.21 reading-time: ^1.5.0 - remark-admonitions: ^1.2.1 tslib: ^2.4.0 unist-util-visit: ^2.0.3 utility-types: ^3.10.0 - webpack: ^5.72.0 + webpack: ^5.73.0 peerDependencies: react: ^16.8.4 || ^17.0.0 react-dom: ^16.8.4 || ^17.0.0 - checksum: 6d8e63da83b34abac093c6f49e0133f2f38e6086ee87796820d9ca00ea70b6332a5959ea2fc772961fcf8fbcf183b21758d0f0212ebdbf4aa9503093bcce8607 + checksum: 9cc47886f9be8eb2088034e4caa0e7e6579246bc7a4169dc1beffcc80dcf46c8d210aa85d5df759dba95dbe615247399f434cc71e32b0441e091480a855c013f languageName: node linkType: hard -"@docusaurus/plugin-content-docs@npm:2.0.0-beta.20, @docusaurus/plugin-content-docs@npm:^2.0.0-beta.20": - version: 2.0.0-beta.20 - resolution: "@docusaurus/plugin-content-docs@npm:2.0.0-beta.20" - dependencies: - "@docusaurus/core": 2.0.0-beta.20 - "@docusaurus/logger": 2.0.0-beta.20 - "@docusaurus/mdx-loader": 2.0.0-beta.20 - "@docusaurus/utils": 2.0.0-beta.20 - "@docusaurus/utils-validation": 2.0.0-beta.20 +"@docusaurus/plugin-content-docs@npm:2.1.0, @docusaurus/plugin-content-docs@npm:^2.0.0-beta.20": + version: 2.1.0 + resolution: "@docusaurus/plugin-content-docs@npm:2.1.0" + dependencies: + "@docusaurus/core": 2.1.0 + "@docusaurus/logger": 2.1.0 + "@docusaurus/mdx-loader": 2.1.0 + "@docusaurus/module-type-aliases": 2.1.0 + "@docusaurus/types": 2.1.0 + "@docusaurus/utils": 2.1.0 + "@docusaurus/utils-validation": 2.1.0 + "@types/react-router-config": ^5.0.6 combine-promises: ^1.1.0 fs-extra: ^10.1.0 import-fresh: ^3.3.0 js-yaml: ^4.1.0 lodash: ^4.17.21 - remark-admonitions: ^1.2.1 tslib: ^2.4.0 utility-types: ^3.10.0 - webpack: ^5.72.0 + webpack: ^5.73.0 peerDependencies: react: ^16.8.4 || ^17.0.0 react-dom: ^16.8.4 || ^17.0.0 - checksum: d9c0e836b4de06d66662d5c50d828b0c8e493243cf758bf4f0efb73077376a2906b9344b272fe2a550302a16af8dbbfc9dbacbbcf836553c3c61970fb0658977 + checksum: 40100821e07dcb37192f1b93b84f22ced932054c21342a45019241d13eb13b1663465db123aef2494c2f73c018e41400b6694008b0c9e09d4ec84c96ad7974fe languageName: node linkType: hard -"@docusaurus/plugin-content-pages@npm:2.0.0-beta.20, @docusaurus/plugin-content-pages@npm:^2.0.0-beta.20": - version: 2.0.0-beta.20 - resolution: "@docusaurus/plugin-content-pages@npm:2.0.0-beta.20" +"@docusaurus/plugin-content-pages@npm:2.1.0, @docusaurus/plugin-content-pages@npm:^2.0.0-beta.20": + version: 2.1.0 + resolution: "@docusaurus/plugin-content-pages@npm:2.1.0" dependencies: - "@docusaurus/core": 2.0.0-beta.20 - "@docusaurus/mdx-loader": 2.0.0-beta.20 - "@docusaurus/utils": 2.0.0-beta.20 - "@docusaurus/utils-validation": 2.0.0-beta.20 + "@docusaurus/core": 2.1.0 + "@docusaurus/mdx-loader": 2.1.0 + "@docusaurus/types": 2.1.0 + "@docusaurus/utils": 2.1.0 + "@docusaurus/utils-validation": 2.1.0 fs-extra: ^10.1.0 - remark-admonitions: ^1.2.1 tslib: ^2.4.0 - webpack: ^5.72.0 + webpack: ^5.73.0 peerDependencies: react: ^16.8.4 || ^17.0.0 react-dom: ^16.8.4 || ^17.0.0 - checksum: f1a17b2655ee4a2b1b3c59dafd39232cf84f6f96c5d5af90c1902d03aad471e26bc1c13e94745265ac669438291c732dd728e894528a32bd8dd94fd9f176a8c6 + checksum: c2fc027ce6b0b1cdbe5c6d1c2d9289cde80f263bb34297425783b7687cdfae1d4c045736b94f45735d6f4275978f2890f39c7ed383a558b48e84582cc67ab39b languageName: node linkType: hard -"@docusaurus/plugin-ideal-image@npm:latest": - version: 2.0.0-beta.20 - resolution: "@docusaurus/plugin-ideal-image@npm:2.0.0-beta.20" +"@docusaurus/plugin-ideal-image@npm:^2.0.1": + version: 2.1.0 + resolution: "@docusaurus/plugin-ideal-image@npm:2.1.0" dependencies: - "@docusaurus/core": 2.0.0-beta.20 - "@docusaurus/lqip-loader": 2.0.0-beta.20 + "@docusaurus/core": 2.1.0 + "@docusaurus/lqip-loader": 2.1.0 "@docusaurus/responsive-loader": ^1.7.0 - "@docusaurus/theme-translations": 2.0.0-beta.20 - "@docusaurus/utils-validation": 2.0.0-beta.20 + "@docusaurus/theme-translations": 2.1.0 + "@docusaurus/types": 2.1.0 + "@docusaurus/utils-validation": 2.1.0 "@endiliey/react-ideal-image": ^0.0.11 - react-waypoint: ^10.1.0 - sharp: ^0.30.4 + react-waypoint: ^10.3.0 + sharp: ^0.30.7 tslib: ^2.4.0 - webpack: ^5.72.0 + webpack: ^5.73.0 peerDependencies: jimp: "*" react: ^16.8.4 || ^17.0.0 @@ -1708,7 +1726,7 @@ __metadata: peerDependenciesMeta: jimp: optional: true - checksum: 29522dc905ba96a670165448fcfffe2494b3a3bacf11944a2a5c10afbed090f4fa5afc1f46fc69d1affb92172166f856cd37de4f2ca047647bf03830f08800c0 + checksum: 53cd9af14344d4cd4d6d30295a5bb700743eeec70e3a1eeb4e06fa8bce79f124cacca63c024f8996d79d64624a7ea4f2bf814a814dd601d2269188c6ae9f623c languageName: node linkType: hard @@ -1742,108 +1760,127 @@ __metadata: linkType: hard "@docusaurus/theme-classic@npm:^2.0.0-beta.20": - version: 2.0.0-beta.20 - resolution: "@docusaurus/theme-classic@npm:2.0.0-beta.20" - dependencies: - "@docusaurus/core": 2.0.0-beta.20 - "@docusaurus/plugin-content-blog": 2.0.0-beta.20 - "@docusaurus/plugin-content-docs": 2.0.0-beta.20 - "@docusaurus/plugin-content-pages": 2.0.0-beta.20 - "@docusaurus/theme-common": 2.0.0-beta.20 - "@docusaurus/theme-translations": 2.0.0-beta.20 - "@docusaurus/utils": 2.0.0-beta.20 - "@docusaurus/utils-common": 2.0.0-beta.20 - "@docusaurus/utils-validation": 2.0.0-beta.20 + version: 2.1.0 + resolution: "@docusaurus/theme-classic@npm:2.1.0" + dependencies: + "@docusaurus/core": 2.1.0 + "@docusaurus/mdx-loader": 2.1.0 + "@docusaurus/module-type-aliases": 2.1.0 + "@docusaurus/plugin-content-blog": 2.1.0 + "@docusaurus/plugin-content-docs": 2.1.0 + "@docusaurus/plugin-content-pages": 2.1.0 + "@docusaurus/theme-common": 2.1.0 + "@docusaurus/theme-translations": 2.1.0 + "@docusaurus/types": 2.1.0 + "@docusaurus/utils": 2.1.0 + "@docusaurus/utils-common": 2.1.0 + "@docusaurus/utils-validation": 2.1.0 "@mdx-js/react": ^1.6.22 - clsx: ^1.1.1 + clsx: ^1.2.1 copy-text-to-clipboard: ^3.0.1 - infima: 0.2.0-alpha.39 + infima: 0.2.0-alpha.42 lodash: ^4.17.21 nprogress: ^0.2.0 - postcss: ^8.4.13 - prism-react-renderer: ^1.3.1 + postcss: ^8.4.14 + prism-react-renderer: ^1.3.5 prismjs: ^1.28.0 - react-router-dom: ^5.2.0 + react-router-dom: ^5.3.3 rtlcss: ^3.5.0 + tslib: ^2.4.0 + utility-types: ^3.10.0 peerDependencies: react: ^16.8.4 || ^17.0.0 react-dom: ^16.8.4 || ^17.0.0 - checksum: a7086e35735ea34229976dee6a70d95f78dc2cabcd34d7e75c3acdfa6dbb08ba83e03dd6584c43231f9b149cfa30d115c64a0bbe6a981ed006c9956c1f1bb7a1 + checksum: e6c57a187ef8fa5af322f5e39ab8f36c3e0fb0e4d01b4d85f951e1f3edec18c73f7c51ae8f9ba7c8c7d106c723ff0d7b393e781d88dfc7f604e2bcc865fe47ba languageName: node linkType: hard -"@docusaurus/theme-common@npm:2.0.0-beta.20, @docusaurus/theme-common@npm:latest": - version: 2.0.0-beta.20 - resolution: "@docusaurus/theme-common@npm:2.0.0-beta.20" - dependencies: - "@docusaurus/module-type-aliases": 2.0.0-beta.20 - "@docusaurus/plugin-content-blog": 2.0.0-beta.20 - "@docusaurus/plugin-content-docs": 2.0.0-beta.20 - "@docusaurus/plugin-content-pages": 2.0.0-beta.20 - clsx: ^1.1.1 +"@docusaurus/theme-common@npm:2.1.0, @docusaurus/theme-common@npm:^2.0.1": + version: 2.1.0 + resolution: "@docusaurus/theme-common@npm:2.1.0" + dependencies: + "@docusaurus/mdx-loader": 2.1.0 + "@docusaurus/module-type-aliases": 2.1.0 + "@docusaurus/plugin-content-blog": 2.1.0 + "@docusaurus/plugin-content-docs": 2.1.0 + "@docusaurus/plugin-content-pages": 2.1.0 + "@docusaurus/utils": 2.1.0 + "@types/history": ^4.7.11 + "@types/react": "*" + "@types/react-router-config": "*" + clsx: ^1.2.1 parse-numeric-range: ^1.3.0 - prism-react-renderer: ^1.3.1 + prism-react-renderer: ^1.3.5 tslib: ^2.4.0 utility-types: ^3.10.0 peerDependencies: react: ^16.8.4 || ^17.0.0 react-dom: ^16.8.4 || ^17.0.0 - checksum: 353944a6ae7920e0b72d2ca8ed96c6bb533fe1e05b079ebb9b52d9467a375d675e9a5f6ef5e561337c543ce2c52ae7f523a5b6916f79f07b91615c1683cbc011 + checksum: 8620309cf8610aa2b2c577ec0e14deb2b6b2465ff847b666eacab2c79d3dbee0883070dc390495de3be9b33fdd0e667285e13ca99670460757064bec979983c5 languageName: node linkType: hard -"@docusaurus/theme-translations@npm:2.0.0-beta.20": - version: 2.0.0-beta.20 - resolution: "@docusaurus/theme-translations@npm:2.0.0-beta.20" +"@docusaurus/theme-translations@npm:2.1.0": + version: 2.1.0 + resolution: "@docusaurus/theme-translations@npm:2.1.0" dependencies: fs-extra: ^10.1.0 tslib: ^2.4.0 - checksum: 079340ccbe9021b065eb2083c309b02b24ee745274046cb90b14a63f2625184c568e46a79ac1de6b2c4fc4a07fd2eb0abdb417bd530f74c591f8ef78c54850a7 + checksum: 26d9f2889d44097c5a4e343d48cbd5d849fe7dbc9489402a9d71f35cca5e254fc6d9ffc360409eeea2c70a1dda92f6ee95630c6cb69b20688ce351505bbe18cc languageName: node linkType: hard -"@docusaurus/types@npm:2.0.0-beta.20": - version: 2.0.0-beta.20 - resolution: "@docusaurus/types@npm:2.0.0-beta.20" +"@docusaurus/types@npm:2.1.0": + version: 2.1.0 + resolution: "@docusaurus/types@npm:2.1.0" dependencies: + "@types/history": ^4.7.11 + "@types/react": "*" commander: ^5.1.0 - history: ^4.9.0 joi: ^17.6.0 react-helmet-async: ^1.3.0 utility-types: ^3.10.0 - webpack: ^5.72.0 + webpack: ^5.73.0 webpack-merge: ^5.8.0 - checksum: 25aade44696326719a7eae063b5031b69ab78bb6cebccf036e4b9a8a81e1fa8a1561b30fbab6ad76476e5422ca0119f50a8215e1b43cd164bfc9b208b9789003 + peerDependencies: + react: ^16.8.4 || ^17.0.0 + react-dom: ^16.8.4 || ^17.0.0 + checksum: e6112cd682410e6979862ddd5763b14f4d5becb6b0be147c9745bb00b56845c491a62279a9dde7af54819a4a09aa0bb7a8b7cc9609414aee554e3d996a6992b8 languageName: node linkType: hard -"@docusaurus/utils-common@npm:2.0.0-beta.20": - version: 2.0.0-beta.20 - resolution: "@docusaurus/utils-common@npm:2.0.0-beta.20" +"@docusaurus/utils-common@npm:2.1.0": + version: 2.1.0 + resolution: "@docusaurus/utils-common@npm:2.1.0" dependencies: tslib: ^2.4.0 - checksum: 79eee4a1b8cd99a65afb08e0ff726702c9139bebeef408a4653f14c2c9dd3005d91b4dba25da3323f9c5d12ebec69285a5309edf2d339306a8c54ab692031642 + peerDependencies: + "@docusaurus/types": "*" + peerDependenciesMeta: + "@docusaurus/types": + optional: true + checksum: b4a9282f5595285a8e7d957a1d10ad46b2244875cd955fd5d4799ccc908f06ecd27283679ecdbe4394f3e326a74ceacd1c624c01158d5bcfdcb6be62c15fc6f0 languageName: node linkType: hard -"@docusaurus/utils-validation@npm:2.0.0-beta.20": - version: 2.0.0-beta.20 - resolution: "@docusaurus/utils-validation@npm:2.0.0-beta.20" +"@docusaurus/utils-validation@npm:2.1.0": + version: 2.1.0 + resolution: "@docusaurus/utils-validation@npm:2.1.0" dependencies: - "@docusaurus/logger": 2.0.0-beta.20 - "@docusaurus/utils": 2.0.0-beta.20 + "@docusaurus/logger": 2.1.0 + "@docusaurus/utils": 2.1.0 joi: ^17.6.0 js-yaml: ^4.1.0 tslib: ^2.4.0 - checksum: 6d97711d4c89ae9acb87fce3f8fca5827b2a7e0a7bedcb720cc836127839e1baaf2b68a76e0600f9076f25198f9b54a4b17be9c050e251db106898363060fea8 + checksum: 63fa924768a7e7af99d5a84765429849f604f4e90ce4f60de7747b3fab8cd365315a1bcb1ad99bc56e92179e30eb54bd3526dc397283a522802b0cc71037d7ed languageName: node linkType: hard -"@docusaurus/utils@npm:2.0.0-beta.20": - version: 2.0.0-beta.20 - resolution: "@docusaurus/utils@npm:2.0.0-beta.20" +"@docusaurus/utils@npm:2.1.0": + version: 2.1.0 + resolution: "@docusaurus/utils@npm:2.1.0" dependencies: - "@docusaurus/logger": 2.0.0-beta.20 + "@docusaurus/logger": 2.1.0 "@svgr/webpack": ^6.2.1 file-loader: ^6.2.0 fs-extra: ^10.1.0 @@ -1857,22 +1894,27 @@ __metadata: shelljs: ^0.8.5 tslib: ^2.4.0 url-loader: ^4.1.1 - webpack: ^5.72.0 - checksum: 2bba29f97926662944930374eb14a0c92034753bf720e807f54864a514682bef05af36bd13d2863ee379cb560eff97f570ea091bfaba1ed1c7e9606f51e29f46 + webpack: ^5.73.0 + peerDependencies: + "@docusaurus/types": "*" + peerDependenciesMeta: + "@docusaurus/types": + optional: true + checksum: 970c8b2716e3c3030b7ce6c460bf74cbf7612d1f155f345a2f59c681c60f5d11b09d156c0e159923b85b47c3dc02efe447fd1ad5457213ca5e91f98e4c697f90 languageName: node linkType: hard -"@emotion/babel-plugin@npm:^11.7.1": - version: 11.9.2 - resolution: "@emotion/babel-plugin@npm:11.9.2" +"@emotion/babel-plugin@npm:^11.10.0": + version: 11.10.2 + resolution: "@emotion/babel-plugin@npm:11.10.2" dependencies: - "@babel/helper-module-imports": ^7.12.13 - "@babel/plugin-syntax-jsx": ^7.12.13 - "@babel/runtime": ^7.13.10 - "@emotion/hash": ^0.8.0 - "@emotion/memoize": ^0.7.5 - "@emotion/serialize": ^1.0.2 - babel-plugin-macros: ^2.6.1 + "@babel/helper-module-imports": ^7.16.7 + "@babel/plugin-syntax-jsx": ^7.17.12 + "@babel/runtime": ^7.18.3 + "@emotion/hash": ^0.9.0 + "@emotion/memoize": ^0.8.0 + "@emotion/serialize": ^1.1.0 + babel-plugin-macros: ^3.1.0 convert-source-map: ^1.5.0 escape-string-regexp: ^4.0.0 find-root: ^1.1.0 @@ -1880,47 +1922,48 @@ __metadata: stylis: 4.0.13 peerDependencies: "@babel/core": ^7.0.0 - checksum: 2d2c4fadd389862896bcbc5f42c9b9c1a199810173fcf14e5520506c7179c2ddb991b8832fd273f42104cf0dae98886ad8e767b5e38ad235b652d903c3b8a328 + checksum: 7f9e84b3c00b4db5a829c6880549c6a816b3defcaf828cb37808737f3c17b22a45a06e48334f38f5729b218812252857ced27d3a12dd8ca1e260e4b1d0045dfd languageName: node linkType: hard -"@emotion/cache@npm:^11.4.0, @emotion/cache@npm:^11.7.1": - version: 11.7.1 - resolution: "@emotion/cache@npm:11.7.1" +"@emotion/cache@npm:^11.10.0, @emotion/cache@npm:^11.4.0": + version: 11.10.3 + resolution: "@emotion/cache@npm:11.10.3" dependencies: - "@emotion/memoize": ^0.7.4 - "@emotion/sheet": ^1.1.0 - "@emotion/utils": ^1.0.0 - "@emotion/weak-memoize": ^0.2.5 + "@emotion/memoize": ^0.8.0 + "@emotion/sheet": ^1.2.0 + "@emotion/utils": ^1.2.0 + "@emotion/weak-memoize": ^0.3.0 stylis: 4.0.13 - checksum: cf7aa8fe3bacfdedcda94b53e76a7635e122043439715fcfbf7f1a81340cfe6099a59134481a03ec3e0437466566d18528577d1e6ea92f5b98c372b8b38a8f35 + checksum: d31291eff1b270d8db6f471b2b9b3bc5d786c296838631f101837747ff5afa8e8890655279457c68ce2cee23256ab02a25c177f5487b5061da82c7354c1bdce5 languageName: node linkType: hard -"@emotion/hash@npm:^0.8.0": - version: 0.8.0 - resolution: "@emotion/hash@npm:0.8.0" - checksum: 4b35d88a97e67275c1d990c96d3b0450451d089d1508619488fc0acb882cb1ac91e93246d471346ebd1b5402215941ef4162efe5b51534859b39d8b3a0e3ffaa +"@emotion/hash@npm:^0.9.0": + version: 0.9.0 + resolution: "@emotion/hash@npm:0.9.0" + checksum: b63428f7c8186607acdca5d003700cecf0ded519d0b5c5cc3b3154eafcad6ff433f8361bd2bac8882715b557e6f06945694aeb6ba8b25c6095d7a88570e2e0bb languageName: node linkType: hard -"@emotion/memoize@npm:^0.7.4, @emotion/memoize@npm:^0.7.5": - version: 0.7.5 - resolution: "@emotion/memoize@npm:0.7.5" - checksum: 83da8d4a7649a92c72f960817692bc6be13cc13e107b9f7e878d63766525ed4402881bfeb3cda61145c050281e7e260f114a0a2870515527346f2ef896b915b3 +"@emotion/memoize@npm:^0.8.0": + version: 0.8.0 + resolution: "@emotion/memoize@npm:0.8.0" + checksum: c87bb110b829edd8e1c13b90a6bc37cebc39af29c7599a1e66a48e06f9bec43e8e53495ba86278cc52e7589549492c8dfdc81d19f4fdec0cee6ba13d2ad2c928 languageName: node linkType: hard "@emotion/react@npm:^11.8.1": - version: 11.9.0 - resolution: "@emotion/react@npm:11.9.0" - dependencies: - "@babel/runtime": ^7.13.10 - "@emotion/babel-plugin": ^11.7.1 - "@emotion/cache": ^11.7.1 - "@emotion/serialize": ^1.0.3 - "@emotion/utils": ^1.1.0 - "@emotion/weak-memoize": ^0.2.5 + version: 11.10.4 + resolution: "@emotion/react@npm:11.10.4" + dependencies: + "@babel/runtime": ^7.18.3 + "@emotion/babel-plugin": ^11.10.0 + "@emotion/cache": ^11.10.0 + "@emotion/serialize": ^1.1.0 + "@emotion/use-insertion-effect-with-fallbacks": ^1.0.0 + "@emotion/utils": ^1.2.0 + "@emotion/weak-memoize": ^0.3.0 hoist-non-react-statics: ^3.3.1 peerDependencies: "@babel/core": ^7.0.0 @@ -1930,48 +1973,57 @@ __metadata: optional: true "@types/react": optional: true - checksum: 4ceb004f942fb7557a55ea17aad2c48c4cd48ed5a780ccdc2993e4bded2f94d7c1764bd2f4fbe53f5b26059263599cec64ff66d29447e281a58c60b39c72e5cc + checksum: 7555f6a1840c71d841386be2ec98ebfd6399923bd6a61247c7b07283f9a056f57e83c4fdd9ea7a7fcc3d88e5e04bb03168b4f0557934bcd501c88af4db16e1e0 languageName: node linkType: hard -"@emotion/serialize@npm:^1.0.2, @emotion/serialize@npm:^1.0.3": - version: 1.0.3 - resolution: "@emotion/serialize@npm:1.0.3" +"@emotion/serialize@npm:^1.1.0": + version: 1.1.0 + resolution: "@emotion/serialize@npm:1.1.0" dependencies: - "@emotion/hash": ^0.8.0 - "@emotion/memoize": ^0.7.4 - "@emotion/unitless": ^0.7.5 - "@emotion/utils": ^1.0.0 + "@emotion/hash": ^0.9.0 + "@emotion/memoize": ^0.8.0 + "@emotion/unitless": ^0.8.0 + "@emotion/utils": ^1.2.0 csstype: ^3.0.2 - checksum: 99a9053bd98c99d63af542ebee029281eeaf653e3a12e97ee79bad7330c68408104c30be6fc07a528e38bb69aba680655181744b76ec6c6f459c121cb805fac2 + checksum: 8f22f83194ad76cb3bbee481daa57fdc65ca3078a5db9e219c04151341ef93af80c7057aea17b64446682d275918f7ecc0c20e977c1af153c79a1485503fe717 languageName: node linkType: hard -"@emotion/sheet@npm:^1.1.0": - version: 1.1.0 - resolution: "@emotion/sheet@npm:1.1.0" - checksum: a4b74e16a8fea1157413efe4904f5f679d724323cb605d66d20a0b98744422f5d411fca927ceb52e4de454a0a819c5273ca9496db9f011b4ecd17b9f1b212007 +"@emotion/sheet@npm:^1.2.0": + version: 1.2.0 + resolution: "@emotion/sheet@npm:1.2.0" + checksum: b3771e47963d36c179f9a1119055d7e5d18e2718e73ebe2b4b1c56f4bbf4ea6b12c50bbc52cd502f03f7981beb2fbb3fee2638b6f5ef6c5f223b06f8bf88ec7b languageName: node linkType: hard -"@emotion/unitless@npm:^0.7.5": - version: 0.7.5 - resolution: "@emotion/unitless@npm:0.7.5" - checksum: f976e5345b53fae9414a7b2e7a949aa6b52f8bdbcc84458b1ddc0729e77ba1d1dfdff9960e0da60183877873d3a631fa24d9695dd714ed94bcd3ba5196586a6b +"@emotion/unitless@npm:^0.8.0": + version: 0.8.0 + resolution: "@emotion/unitless@npm:0.8.0" + checksum: 176141117ed23c0eb6e53a054a69c63e17ae532ec4210907a20b2208f91771821835f1c63dd2ec63e30e22fcc984026d7f933773ee6526dd038e0850919fae7a languageName: node linkType: hard -"@emotion/utils@npm:^1.0.0, @emotion/utils@npm:^1.1.0": - version: 1.1.0 - resolution: "@emotion/utils@npm:1.1.0" - checksum: d3b681ca3a23b07033ac6c6937e71010a5549ac8ccec325eb6c91a7e48d9a73db83fa5dadc58be981bb125d7c00fedca868ea4362b1da9e02866615f96be4df1 +"@emotion/use-insertion-effect-with-fallbacks@npm:^1.0.0": + version: 1.0.0 + resolution: "@emotion/use-insertion-effect-with-fallbacks@npm:1.0.0" + peerDependencies: + react: ">=16.8.0" + checksum: 4f06a3b48258c832aa8022a262572061a31ff078d377e9164cccc99951309d70f4466e774fe704461b2f8715007a82ed625a54a5c7a127c89017d3ce3187d4f1 languageName: node linkType: hard -"@emotion/weak-memoize@npm:^0.2.5": - version: 0.2.5 - resolution: "@emotion/weak-memoize@npm:0.2.5" - checksum: 27d402b0c683b94658220b6d47840346ee582329ca2a15ec9c233492e0f1a27687ccb233b76eedc922f2e185e444cc89f7b97a81a1d3e5ae9f075bab08e965ea +"@emotion/utils@npm:^1.2.0": + version: 1.2.0 + resolution: "@emotion/utils@npm:1.2.0" + checksum: 55457a49ddd4db6a014ea0454dc09eaa23eedfb837095c8ff90470cb26a303f7ceb5fcc1e2190ef64683e64cfd33d3ba3ca3109cd87d12bc9e379e4195c9a4dd + languageName: node + linkType: hard + +"@emotion/weak-memoize@npm:^0.3.0": + version: 0.3.0 + resolution: "@emotion/weak-memoize@npm:0.3.0" + checksum: f43ef4c8b7de70d9fa5eb3105921724651e4188e895beb71f0c5919dc899a7b8743e1fdd99d38b9092dd5722c7be2312ebb47fbdad0c4e38bea58f6df5885cc0 languageName: node linkType: hard @@ -2011,7 +2063,7 @@ __metadata: "@iota-community/iota-wiki-cli@iota-community/iota-wiki-cli#v2": version: 1.7.0 - resolution: "@iota-community/iota-wiki-cli@https://github.com/iota-community/iota-wiki-cli.git#commit=ee983421e6029e4802c69c9adf812c8ab80b0c42" + resolution: "@iota-community/iota-wiki-cli@https://github.com/iota-community/iota-wiki-cli.git#commit=62b709be5cec92768985521aaa58bf3d09a5f3b9" dependencies: "@babel/generator": ^7.17.9 "@babel/parser": ^7.17.9 @@ -2037,16 +2089,16 @@ __metadata: react-dom: ^17.0.2 bin: iota-wiki: dist/index.js - checksum: 2c87920005f6cb4985398cd10b1c1f6e7eeb431b00855d785e71f7dd51524a367675060a1416af5d405d93412b251e73c2ca5f6a8fa82e32c9a4bddbef4eaeb5 + checksum: c8ff6808523df0196c25cb0b54d9c571ab17dbea25ea732b0f7e70cd0105b0759d6adccbdcfc22adc161c087812b1323f6d82a7537c06b92014477bf21afc846 languageName: node linkType: hard "@iota-wiki/plugin-tutorial@npm:^1.0.4": - version: 1.0.4 - resolution: "@iota-wiki/plugin-tutorial@npm:1.0.4" + version: 1.0.6 + resolution: "@iota-wiki/plugin-tutorial@npm:1.0.6" dependencies: - "@docusaurus/plugin-ideal-image": latest - "@docusaurus/theme-common": latest + "@docusaurus/plugin-ideal-image": ^2.0.1 + "@docusaurus/theme-common": ^2.0.1 "@popperjs/core": ^2.11.5 clsx: ^1.1.1 react: ^17.0.2 @@ -2057,7 +2109,7 @@ __metadata: peerDependencies: react: ^17.0.2 react-dom: ^17.0.2 - checksum: 3220971a49ee2fb542e2ed2c0903b2648f1b7d1021a0063a4c05153b40de18777b4d07ad9508ed8f75c39cf46399ea44ef3feb0d347900ddc13e15da9cdd99df + checksum: ed462b8c767035efd5be58926f003a27c150c481b4d0a5ea12706b8d656a345fc9306625e6c714386a5fd9458e5abff74313487cdcf29e3aa9941eae2bd2d90f languageName: node linkType: hard @@ -2071,45 +2123,55 @@ __metadata: languageName: node linkType: hard -"@jridgewell/gen-mapping@npm:^0.3.0": - version: 0.3.1 - resolution: "@jridgewell/gen-mapping@npm:0.3.1" +"@jridgewell/gen-mapping@npm:^0.3.0, @jridgewell/gen-mapping@npm:^0.3.2": + version: 0.3.2 + resolution: "@jridgewell/gen-mapping@npm:0.3.2" dependencies: - "@jridgewell/set-array": ^1.0.0 + "@jridgewell/set-array": ^1.0.1 "@jridgewell/sourcemap-codec": ^1.4.10 "@jridgewell/trace-mapping": ^0.3.9 - checksum: e9e7bb3335dea9e60872089761d4e8e089597360cdb1af90370e9d53b7d67232c1e0a3ab65fbfef4fc785745193fbc56bff9f3a6cab6c6ce3f15e12b4191f86b + checksum: 1832707a1c476afebe4d0fbbd4b9434fdb51a4c3e009ab1e9938648e21b7a97049fa6009393bdf05cab7504108413441df26d8a3c12193996e65493a4efb6882 languageName: node linkType: hard "@jridgewell/resolve-uri@npm:^3.0.3": - version: 3.0.7 - resolution: "@jridgewell/resolve-uri@npm:3.0.7" - checksum: 94f454f4cef8f0acaad85745fd3ca6cd0d62ef731cf9f952ecb89b8b2ce5e20998cd52be31311cedc5fa5b28b1708a15f3ad9df0fe1447ee4f42959b036c4b5b + version: 3.1.0 + resolution: "@jridgewell/resolve-uri@npm:3.1.0" + checksum: b5ceaaf9a110fcb2780d1d8f8d4a0bfd216702f31c988d8042e5f8fbe353c55d9b0f55a1733afdc64806f8e79c485d2464680ac48a0d9fcadb9548ee6b81d267 languageName: node linkType: hard -"@jridgewell/set-array@npm:^1.0.0": - version: 1.1.1 - resolution: "@jridgewell/set-array@npm:1.1.1" - checksum: cc5d91e0381c347e3edee4ca90b3c292df9e6e55f29acbe0dd97de8651b4730e9ab761406fd572effa79972a0edc55647b627f8c72315e276d959508853d9bf2 +"@jridgewell/set-array@npm:^1.0.0, @jridgewell/set-array@npm:^1.0.1": + version: 1.1.2 + resolution: "@jridgewell/set-array@npm:1.1.2" + checksum: 69a84d5980385f396ff60a175f7177af0b8da4ddb81824cb7016a9ef914eee9806c72b6b65942003c63f7983d4f39a5c6c27185bbca88eb4690b62075602e28e + languageName: node + linkType: hard + +"@jridgewell/source-map@npm:^0.3.2": + version: 0.3.2 + resolution: "@jridgewell/source-map@npm:0.3.2" + dependencies: + "@jridgewell/gen-mapping": ^0.3.0 + "@jridgewell/trace-mapping": ^0.3.9 + checksum: 1b83f0eb944e77b70559a394d5d3b3f98a81fcc186946aceb3ef42d036762b52ef71493c6c0a3b7c1d2f08785f53ba2df1277fe629a06e6109588ff4cdcf7482 languageName: node linkType: hard "@jridgewell/sourcemap-codec@npm:^1.4.10": - version: 1.4.13 - resolution: "@jridgewell/sourcemap-codec@npm:1.4.13" - checksum: f14449096f60a5f921262322fef65ce0bbbfb778080b3b20212080bcefdeba621c43a58c27065bd536ecb4cc767b18eb9c45f15b6b98a4970139572b60603a1c + version: 1.4.14 + resolution: "@jridgewell/sourcemap-codec@npm:1.4.14" + checksum: 61100637b6d173d3ba786a5dff019e1a74b1f394f323c1fee337ff390239f053b87266c7a948777f4b1ee68c01a8ad0ab61e5ff4abb5a012a0b091bec391ab97 languageName: node linkType: hard -"@jridgewell/trace-mapping@npm:^0.3.9": - version: 0.3.13 - resolution: "@jridgewell/trace-mapping@npm:0.3.13" +"@jridgewell/trace-mapping@npm:^0.3.14, @jridgewell/trace-mapping@npm:^0.3.9": + version: 0.3.15 + resolution: "@jridgewell/trace-mapping@npm:0.3.15" dependencies: "@jridgewell/resolve-uri": ^3.0.3 "@jridgewell/sourcemap-codec": ^1.4.10 - checksum: e38254e830472248ca10a6ed1ae75af5e8514f0680245a5e7b53bc3c030fd8691d4d3115d80595b45d3badead68269769ed47ecbbdd67db1343a11f05700e75a + checksum: 38917e9c2b014d469a9f51c016ed506acbe44dd16ec2f6f99b553ebf3764d22abadbf992f2367b6d2b3511f3eae8ed3a8963f6c1030093fda23efd35ecab2bae languageName: node linkType: hard @@ -2191,22 +2253,22 @@ __metadata: linkType: hard "@npmcli/fs@npm:^2.1.0": - version: 2.1.0 - resolution: "@npmcli/fs@npm:2.1.0" + version: 2.1.2 + resolution: "@npmcli/fs@npm:2.1.2" dependencies: "@gar/promisify": ^1.1.3 semver: ^7.3.5 - checksum: 6ec6d678af6da49f9dac50cd882d7f661934dd278972ffbaacde40d9eaa2871292d634000a0cca9510f6fc29855fbd4af433e1adbff90a524ec3eaf140f1219b + checksum: 405074965e72d4c9d728931b64d2d38e6ea12066d4fad651ac253d175e413c06fe4350970c783db0d749181da8fe49c42d3880bd1cbc12cd68e3a7964d820225 languageName: node linkType: hard "@npmcli/move-file@npm:^2.0.0": - version: 2.0.0 - resolution: "@npmcli/move-file@npm:2.0.0" + version: 2.0.1 + resolution: "@npmcli/move-file@npm:2.0.1" dependencies: mkdirp: ^1.0.4 rimraf: ^3.0.2 - checksum: 1388777b507b0c592d53f41b9d182e1a8de7763bc625fc07999b8edbc22325f074e5b3ec90af79c89d6987fdb2325bc66d59f483258543c14a43661621f841b0 + checksum: 52dc02259d98da517fae4cb3a0a3850227bdae4939dda1980b788a7670636ca2b4a01b58df03dd5f65c1e3cb70c50fa8ce5762b582b3f499ec30ee5ce1fd9380 languageName: node linkType: hard @@ -2218,9 +2280,9 @@ __metadata: linkType: hard "@popperjs/core@npm:^2.11.5": - version: 2.11.5 - resolution: "@popperjs/core@npm:2.11.5" - checksum: fd7f9dca3fb716d7426332b6ee283f88d2724c0ab342fb678865a640bad403dfb9eeebd8204a406986162f7e2b33394f104320008b74d0e9066d7322f70ea35d + version: 2.11.6 + resolution: "@popperjs/core@npm:2.11.6" + checksum: 47fb328cec1924559d759b48235c78574f2d71a8a6c4c03edb6de5d7074078371633b91e39bbf3f901b32aa8af9b9d8f82834856d2f5737a23475036b16817f0 languageName: node linkType: hard @@ -2254,169 +2316,168 @@ __metadata: languageName: node linkType: hard -"@slorber/static-site-generator-webpack-plugin@npm:^4.0.4": - version: 4.0.4 - resolution: "@slorber/static-site-generator-webpack-plugin@npm:4.0.4" +"@slorber/static-site-generator-webpack-plugin@npm:^4.0.7": + version: 4.0.7 + resolution: "@slorber/static-site-generator-webpack-plugin@npm:4.0.7" dependencies: - bluebird: ^3.7.1 - cheerio: ^0.22.0 eval: ^0.1.8 - webpack-sources: ^1.4.3 - checksum: b6005e5fb306347d18d6823a070f50c59f690f4814fc928b048c8cbd56d00bb19332a4f18ad5824c1af1e9d19ec8b6deb365bd4a63cfcfb062020bda65ae0319 + p-map: ^4.0.0 + webpack-sources: ^3.2.2 + checksum: a1e1d8b22dd51059524993f3fdd6861db10eb950debc389e5dd650702287fa2004eace03e6bc8f25b977bd7bc01d76a50aa271cbb73c58a8ec558945d728f307 languageName: node linkType: hard -"@svgr/babel-plugin-add-jsx-attribute@npm:^6.0.0": - version: 6.0.0 - resolution: "@svgr/babel-plugin-add-jsx-attribute@npm:6.0.0" +"@svgr/babel-plugin-add-jsx-attribute@npm:^6.3.1": + version: 6.3.1 + resolution: "@svgr/babel-plugin-add-jsx-attribute@npm:6.3.1" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 8200dfa2ee903a42a376fec73feb591414cced5674dbff646be85bf6f3587ff74ecbaffa14e2cc096d0b3325630d30872c3f350a8ac501e6672a8e7b1ff3e0f5 + checksum: 3a04515743af5f67c3c38cf414f225cb4c266db29fbf37f4bd970be0ab5b6a2c18e9e8c7de3303a70168909106077860b0fdfb9ee4de9c50d994181b4850e615 languageName: node linkType: hard -"@svgr/babel-plugin-remove-jsx-attribute@npm:^6.0.0": - version: 6.0.0 - resolution: "@svgr/babel-plugin-remove-jsx-attribute@npm:6.0.0" +"@svgr/babel-plugin-remove-jsx-attribute@npm:^6.3.1": + version: 6.3.1 + resolution: "@svgr/babel-plugin-remove-jsx-attribute@npm:6.3.1" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 82c988ed40f88640fcd68fc24ff3dbf729673d59cf1627ed0aa5f0c992a1ddc220fe23e7f23ba39110cd47720cc7c630e70333f1a25ff6a65662584317ff2385 + checksum: ea78848a1d987a30320f84263399769d80064a593cf8af41bb5d4e1699869f9395d3ed18c7d35a06c85d4c46f93df3a9864981d6844296c7a26d19b6bfc39098 languageName: node linkType: hard -"@svgr/babel-plugin-remove-jsx-empty-expression@npm:^6.0.0": - version: 6.0.0 - resolution: "@svgr/babel-plugin-remove-jsx-empty-expression@npm:6.0.0" +"@svgr/babel-plugin-remove-jsx-empty-expression@npm:^6.3.1": + version: 6.3.1 + resolution: "@svgr/babel-plugin-remove-jsx-empty-expression@npm:6.3.1" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: c80e3ff4082ebb4aa07a6bc115d98c320c3f69dc9b74c22552084ca9043cd87f8dcc3b7fd40950433d0325848427446d7aadba979f84867b3e35ef0271483866 + checksum: 3975ee4ca649fde5acba30748f7766c1362b7b39b54d6164b8f27a13cee0b0f2b2cf05e8eda476a4c833be42697a1e0b47b0c8fae8a66563ba23ac9537fdd502 languageName: node linkType: hard -"@svgr/babel-plugin-replace-jsx-attribute-value@npm:^6.0.0": - version: 6.0.0 - resolution: "@svgr/babel-plugin-replace-jsx-attribute-value@npm:6.0.0" +"@svgr/babel-plugin-replace-jsx-attribute-value@npm:^6.3.1": + version: 6.3.1 + resolution: "@svgr/babel-plugin-replace-jsx-attribute-value@npm:6.3.1" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: d6b5e5a9834caf3e08c286843185a6ebde90c1223be09d789a6aaf30d75a18a77ee8672b3182f1c5b585e123c2b45e80dd1304e69e62272818ef0b00599c57aa + checksum: 8a65eb8aa99e3c3e4710aff34d20099a4f2a610d79a5ef705ce4050ff28a25c1f22d813c5021a6c9399725559aba28580674f68b4b5a202028754541e3243453 languageName: node linkType: hard -"@svgr/babel-plugin-svg-dynamic-title@npm:^6.0.0": - version: 6.0.0 - resolution: "@svgr/babel-plugin-svg-dynamic-title@npm:6.0.0" +"@svgr/babel-plugin-svg-dynamic-title@npm:^6.3.1": + version: 6.3.1 + resolution: "@svgr/babel-plugin-svg-dynamic-title@npm:6.3.1" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: b62e0eb16d056545f86aaa3f97928c82de619dbbe2de879e7c6c4d9436d5bd86fa11de3f3e309ab69c4ca37d5cf293b11de6e8e81e302ea5fb5121fb0564b643 + checksum: 026f440d2e609532b1a40434dbd97cae54d0ed9090a6f4069d75523611f6d45ac9983a5c69c10cfd4a6ab76bc854c529c99c327e1a11fd8e65b6f59a930181b9 languageName: node linkType: hard -"@svgr/babel-plugin-svg-em-dimensions@npm:^6.0.0": - version: 6.0.0 - resolution: "@svgr/babel-plugin-svg-em-dimensions@npm:6.0.0" +"@svgr/babel-plugin-svg-em-dimensions@npm:^6.3.1": + version: 6.3.1 + resolution: "@svgr/babel-plugin-svg-em-dimensions@npm:6.3.1" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 873c6ef439064f18c68b652fa21bab94668d5647a545146fc24dad82141a9d455fd969e3d89357ae60db6caaec9fbd9253dabddadde095a36eee1e21f6060611 + checksum: 02aa7fa0afd6def11af7f00401918926626faba861f9869b7359d532d524dcf5062810728bf5e8117275dd4c340dc34a24d55c8c705c7a6d678988db8619428b languageName: node linkType: hard -"@svgr/babel-plugin-transform-react-native-svg@npm:^6.0.0": - version: 6.0.0 - resolution: "@svgr/babel-plugin-transform-react-native-svg@npm:6.0.0" +"@svgr/babel-plugin-transform-react-native-svg@npm:^6.3.1": + version: 6.3.1 + resolution: "@svgr/babel-plugin-transform-react-native-svg@npm:6.3.1" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 29df306ce059ed01e30cdcda9684d3b8bbb9513bfd0c257dc351d54ef6472b2ed0de2766f60acacde38bcc84dffd995f08b354308e20b8fc982234530ce1eeab + checksum: 2cbe20f7016eab8de3515c2bf9887a6399e20d8078614b1316952794ec03c331ea0127c689a258c115b5ca29c279fafef972238c8b491841c49a86b84f408088 languageName: node linkType: hard -"@svgr/babel-plugin-transform-svg-component@npm:^6.2.0": - version: 6.2.0 - resolution: "@svgr/babel-plugin-transform-svg-component@npm:6.2.0" +"@svgr/babel-plugin-transform-svg-component@npm:^6.3.1": + version: 6.3.1 + resolution: "@svgr/babel-plugin-transform-svg-component@npm:6.3.1" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 2d4c4ff27c65d26dc4e6fbbdb85ab1fce701473c0616a7f0a55a671f530c4ad3a56e21c627c4b649b592bb9731fc7238f2c39871bc27a8e090dce8b751b1f9d5 + checksum: 76113730f5cbcc58d42e2254168db98bc40201cd7e90d58cd3137f332fd8328ae113ce64a59c2a60e9ca92730030eb7e4ab8476fdbc31cf9ec0cc8221a7ffb96 languageName: node linkType: hard -"@svgr/babel-preset@npm:^6.2.0": - version: 6.2.0 - resolution: "@svgr/babel-preset@npm:6.2.0" +"@svgr/babel-preset@npm:^6.3.1": + version: 6.3.1 + resolution: "@svgr/babel-preset@npm:6.3.1" dependencies: - "@svgr/babel-plugin-add-jsx-attribute": ^6.0.0 - "@svgr/babel-plugin-remove-jsx-attribute": ^6.0.0 - "@svgr/babel-plugin-remove-jsx-empty-expression": ^6.0.0 - "@svgr/babel-plugin-replace-jsx-attribute-value": ^6.0.0 - "@svgr/babel-plugin-svg-dynamic-title": ^6.0.0 - "@svgr/babel-plugin-svg-em-dimensions": ^6.0.0 - "@svgr/babel-plugin-transform-react-native-svg": ^6.0.0 - "@svgr/babel-plugin-transform-svg-component": ^6.2.0 + "@svgr/babel-plugin-add-jsx-attribute": ^6.3.1 + "@svgr/babel-plugin-remove-jsx-attribute": ^6.3.1 + "@svgr/babel-plugin-remove-jsx-empty-expression": ^6.3.1 + "@svgr/babel-plugin-replace-jsx-attribute-value": ^6.3.1 + "@svgr/babel-plugin-svg-dynamic-title": ^6.3.1 + "@svgr/babel-plugin-svg-em-dimensions": ^6.3.1 + "@svgr/babel-plugin-transform-react-native-svg": ^6.3.1 + "@svgr/babel-plugin-transform-svg-component": ^6.3.1 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 9a5ce414815df2c5f05add8a322ce42182563198a8d379850834d801fda3319eed5a3f7f1174c5163626dd9f8f4af36cad7049b0603c8de21e1bc859b931bcea + checksum: c9cdb0889d63d8fa178c90b016e36515e6803ba5c96e980f0233798f63cac29a1e871effa2c17a40e3eaffdb150ad8e98676cc14c97dd8f978f67541c594a05f languageName: node linkType: hard -"@svgr/core@npm:^6.2.1": - version: 6.2.1 - resolution: "@svgr/core@npm:6.2.1" +"@svgr/core@npm:^6.3.1": + version: 6.3.1 + resolution: "@svgr/core@npm:6.3.1" dependencies: - "@svgr/plugin-jsx": ^6.2.1 + "@svgr/plugin-jsx": ^6.3.1 camelcase: ^6.2.0 cosmiconfig: ^7.0.1 - checksum: b3eff9b081e8f1bec7931f5946e2bc848d969dfd6f9349b869148405e97289183ccaa9c00b5d128f69c34257a3cf4bda75cdf03d6b5f1e9238f4c6169c4b4064 + checksum: 753b043f48d5bfef8aa02976d5ed5a885c60e74e5ac0cdf2b00045e7867443ae722a57160ae46e7a2db563d1ecbe1fda4585a21e84ac1337b6b94113f25b9925 languageName: node linkType: hard -"@svgr/hast-util-to-babel-ast@npm:^6.2.1": - version: 6.2.1 - resolution: "@svgr/hast-util-to-babel-ast@npm:6.2.1" +"@svgr/hast-util-to-babel-ast@npm:^6.3.1": + version: 6.3.1 + resolution: "@svgr/hast-util-to-babel-ast@npm:6.3.1" dependencies: - "@babel/types": ^7.15.6 - entities: ^3.0.1 - checksum: c99b05736e9a3bbedf14330080104c30d8843ad0e39ad13b4438600ba75eaced728873934f4e6786813a40e97462a47f94dbab0fcd6a99bc71e88f1b0a9c5b32 + "@babel/types": ^7.18.4 + entities: ^4.3.0 + checksum: e54b48a85795e103cfe918fa7102bf4603e6541dd35ee04061fad62edffa5a60d8aa210f709fe81b50c9fb6041f8e62fabf093443266323c649903200aaea604 languageName: node linkType: hard -"@svgr/plugin-jsx@npm:^6.2.1": - version: 6.2.1 - resolution: "@svgr/plugin-jsx@npm:6.2.1" +"@svgr/plugin-jsx@npm:^6.3.1": + version: 6.3.1 + resolution: "@svgr/plugin-jsx@npm:6.3.1" dependencies: - "@babel/core": ^7.15.5 - "@svgr/babel-preset": ^6.2.0 - "@svgr/hast-util-to-babel-ast": ^6.2.1 - svg-parser: ^2.0.2 + "@babel/core": ^7.18.5 + "@svgr/babel-preset": ^6.3.1 + "@svgr/hast-util-to-babel-ast": ^6.3.1 + svg-parser: ^2.0.4 peerDependencies: "@svgr/core": ^6.0.0 - checksum: 998164c3c30cf788f033f7f93cb929a948af7e52eaba6b16d0d9c667d28af671850a96108664da2551b1e5d59656fbc94ce23141735a1092d01f2f12ff2127ce + checksum: a2e487dc28d2b69b94b7d96e5cb2593857e559e64c8cb4f818035b8a43ba84e5ddb67f966d15f173b544e807e48a1cda1065da8f9064b94b8d62bbe8cb8c4d73 languageName: node linkType: hard -"@svgr/plugin-svgo@npm:^6.2.0": - version: 6.2.0 - resolution: "@svgr/plugin-svgo@npm:6.2.0" +"@svgr/plugin-svgo@npm:^6.3.1": + version: 6.3.1 + resolution: "@svgr/plugin-svgo@npm:6.3.1" dependencies: cosmiconfig: ^7.0.1 deepmerge: ^4.2.2 - svgo: ^2.5.0 + svgo: ^2.8.0 peerDependencies: "@svgr/core": ^6.0.0 - checksum: 74d3aedd0fcaafbfe4985924b4d40e63536a686988eff52a3411cf83851ce2afc1f5e84e203dae18ab896db48c0b824dcfb8c5dd5b071b4ea90d00fc08951254 + checksum: 037d6f91ba7f362764527408661f7fc4a4a296e9dc142a5f2e33fc88dc63dafd305452caae3091e9adb63adf029e0ce20c604d5af787b968f98aad261a834679 languageName: node linkType: hard "@svgr/webpack@npm:^6.2.1": - version: 6.2.1 - resolution: "@svgr/webpack@npm:6.2.1" + version: 6.3.1 + resolution: "@svgr/webpack@npm:6.3.1" dependencies: - "@babel/core": ^7.15.5 - "@babel/plugin-transform-react-constant-elements": ^7.14.5 - "@babel/preset-env": ^7.15.6 - "@babel/preset-react": ^7.14.5 - "@babel/preset-typescript": ^7.15.0 - "@svgr/core": ^6.2.1 - "@svgr/plugin-jsx": ^6.2.1 - "@svgr/plugin-svgo": ^6.2.0 - checksum: 3da7e61942d7fc3c5cdd0ffd2fbc5520168bc75bf783920e843e920bdc462f9869d47a16ca37be9f3435c90eb89c0d4acd044a0f2e1ad478ff2bc90d65e6c2dd + "@babel/core": ^7.18.5 + "@babel/plugin-transform-react-constant-elements": ^7.17.12 + "@babel/preset-env": ^7.18.2 + "@babel/preset-react": ^7.17.12 + "@babel/preset-typescript": ^7.17.12 + "@svgr/core": ^6.3.1 + "@svgr/plugin-jsx": ^6.3.1 + "@svgr/plugin-svgo": ^6.3.1 + checksum: 36784eacf80601462ede7eab66347423a8635e68aa9f152308c81878b071807adee152a28eed2cce9c72faaf6553dd500f68f00601062ec6821ec0a3a77f4e13 languageName: node linkType: hard @@ -2489,26 +2550,33 @@ __metadata: linkType: hard "@types/eslint-scope@npm:^3.7.3": - version: 3.7.3 - resolution: "@types/eslint-scope@npm:3.7.3" + version: 3.7.4 + resolution: "@types/eslint-scope@npm:3.7.4" dependencies: "@types/eslint": "*" "@types/estree": "*" - checksum: 6772b05e1b92003d1f295e81bc847a61f4fbe8ddab77ffa49e84ed3f9552513bdde677eb53ef167753901282857dd1d604d9f82eddb34a233495932b2dc3dc17 + checksum: ea6a9363e92f301cd3888194469f9ec9d0021fe0a397a97a6dd689e7545c75de0bd2153dfb13d3ab532853a278b6572c6f678ce846980669e41029d205653460 languageName: node linkType: hard "@types/eslint@npm:*": - version: 8.4.2 - resolution: "@types/eslint@npm:8.4.2" + version: 8.4.6 + resolution: "@types/eslint@npm:8.4.6" dependencies: "@types/estree": "*" "@types/json-schema": "*" - checksum: e81268cdeb8d64d84af649344df88f064ece0f05db62072657c976b6162ffe16f94b6480a5367d627c629272c2d86d0ee8c24f7095e98f8e743b16f98500673b + checksum: bfaf27b00031b2238139003965475d023306119e467947f7a43a41e380918e365618e2ae6a6ae638697f6421a6bb1571db078695ff5e548f23618000b38acd23 + languageName: node + linkType: hard + +"@types/estree@npm:*": + version: 1.0.0 + resolution: "@types/estree@npm:1.0.0" + checksum: 910d97fb7092c6738d30a7430ae4786a38542023c6302b95d46f49420b797f21619cdde11fa92b338366268795884111c2eb10356e4bd2c8ad5b92941e9e6443 languageName: node linkType: hard -"@types/estree@npm:*, @types/estree@npm:^0.0.51": +"@types/estree@npm:^0.0.51": version: 0.0.51 resolution: "@types/estree@npm:0.0.51" checksum: e56a3bcf759fd9185e992e7fdb3c6a5f81e8ff120e871641607581fb3728d16c811702a7d40fa5f869b7f7b4437ab6a87eb8d98ffafeee51e85bbe955932a189 @@ -2516,13 +2584,13 @@ __metadata: linkType: hard "@types/express-serve-static-core@npm:*, @types/express-serve-static-core@npm:^4.17.18": - version: 4.17.28 - resolution: "@types/express-serve-static-core@npm:4.17.28" + version: 4.17.30 + resolution: "@types/express-serve-static-core@npm:4.17.30" dependencies: "@types/node": "*" "@types/qs": "*" "@types/range-parser": "*" - checksum: 826489811a5b371c10f02443b4ca894ffc05813bfdf2b60c224f5c18ac9a30a2e518cb9ef9fdfcaa2a1bb17f8bfa4ed1859ccdb252e879c9276271b4ee2df5a9 + checksum: c40d9027884ab9e97fa29d9d41d1b75a5966109312e26594cf03c61b278b5bf8e095f53589e47899b34a2e224291a44043617695c3e8bd22284f988e48582ee6 languageName: node linkType: hard @@ -2595,17 +2663,17 @@ __metadata: languageName: node linkType: hard -"@types/mime@npm:^1": - version: 1.3.2 - resolution: "@types/mime@npm:1.3.2" - checksum: 0493368244cced1a69cb791b485a260a422e6fcc857782e1178d1e6f219f1b161793e9f87f5fae1b219af0f50bee24fcbe733a18b4be8fdd07a38a8fb91146fd +"@types/mime@npm:*": + version: 3.0.1 + resolution: "@types/mime@npm:3.0.1" + checksum: 4040fac73fd0cea2460e29b348c1a6173da747f3a87da0dbce80dd7a9355a3d0e51d6d9a401654f3e5550620e3718b5a899b2ec1debf18424e298a2c605346e7 languageName: node linkType: hard "@types/node@npm:*": - version: 17.0.35 - resolution: "@types/node@npm:17.0.35" - checksum: 7a24946ae7fd20267ed92466384f594e448bfb151081158d565cc635d406ecb29ea8fb85fcd2a1f71efccf26fb5bd3c6f509bde56077eb8b832b847a6664bc62 + version: 18.7.14 + resolution: "@types/node@npm:18.7.14" + checksum: 99cf28ff854100158de875cca23c7acc3cc01dfee526a52b90b7f36767c821bcbaf2be0a98a70f06f3b78f3c60639168ff949d725b61e2e124f9f71f1fb8043d languageName: node linkType: hard @@ -2644,7 +2712,7 @@ __metadata: languageName: node linkType: hard -"@types/react-router-config@npm:*": +"@types/react-router-config@npm:*, @types/react-router-config@npm:^5.0.6": version: 5.0.6 resolution: "@types/react-router-config@npm:5.0.6" dependencies: @@ -2677,22 +2745,22 @@ __metadata: linkType: hard "@types/react-transition-group@npm:^4.4.0": - version: 4.4.4 - resolution: "@types/react-transition-group@npm:4.4.4" + version: 4.4.5 + resolution: "@types/react-transition-group@npm:4.4.5" dependencies: "@types/react": "*" - checksum: 86e9ff9731798e12bc2afe0304678918769633b531dcf6397f86af81718fb7930ef8648e894eeb3718fc6eab6eb885cfb9b82a44d1d74e10951ee11ebc4643ae + checksum: 265f1c74061556708ffe8d15559e35c60d6c11478c9950d3735575d2c116ca69f461d85effa06d73a613eb8b73c84fd32682feb57cf7c5f9e4284021dbca25b0 languageName: node linkType: hard "@types/react@npm:*": - version: 18.0.9 - resolution: "@types/react@npm:18.0.9" + version: 18.0.18 + resolution: "@types/react@npm:18.0.18" dependencies: "@types/prop-types": "*" "@types/scheduler": "*" csstype: ^3.0.2 - checksum: 162364dad716d9017ee34aabf2ea37499709ebbdef70392ae1b39225985971e1a46f121efb9c5c7da92144ee1d96d4525df806a7c1c03a5db7fd31dd034ddc7a + checksum: 6d72d35ab3eecf382a5e0f225923f5a2c753045fce02e4e29713f36c99a24f0f770666a49dde96167f37c86271f93339d1b7e2b8969d011b137a9ebd24ee6806 languageName: node linkType: hard @@ -2728,13 +2796,13 @@ __metadata: languageName: node linkType: hard -"@types/serve-static@npm:*": - version: 1.13.10 - resolution: "@types/serve-static@npm:1.13.10" +"@types/serve-static@npm:*, @types/serve-static@npm:^1.13.10": + version: 1.15.0 + resolution: "@types/serve-static@npm:1.15.0" dependencies: - "@types/mime": ^1 + "@types/mime": "*" "@types/node": "*" - checksum: eaca858739483e3ded254cad7d7a679dc2c8b3f52c8bb0cd845b3b7eb1984bde0371fdcb0a5c83aa12e6daf61b6beb762545021f520f08a1fe882a3fa4ea5554 + checksum: b6ac93d471fb0f53ddcac1f9b67572a09cd62806f7db5855244b28f6f421139626f24799392566e97d1ffc61b12f9de7f30380c39fcae3c8a161fe161d44edf2 languageName: node linkType: hard @@ -2935,13 +3003,13 @@ __metadata: languageName: node linkType: hard -"@yarnpkg/fslib@npm:^2.6.2": - version: 2.6.2 - resolution: "@yarnpkg/fslib@npm:2.6.2" +"@yarnpkg/fslib@npm:^2.7.1": + version: 2.7.1 + resolution: "@yarnpkg/fslib@npm:2.7.1" dependencies: "@yarnpkg/libzip": ^2.2.4 tslib: ^1.13.0 - checksum: df5c73d399397b73b6b3bed15be7b56d5aa380270e4428aba8e67834a1994c693b03ef4b34ab6a8bb97eded4487a1f344b12dbd953040f05ab0a73fa5730a78b + checksum: 91ff9c52008175a1ce0227328fe56ed6255695353dc5cfb472f7b113e4a133b7722db2208959bb41c91fc23318bac28fedfa6879db5f1f645d1fcbff3b3c0185 languageName: node linkType: hard @@ -2966,13 +3034,13 @@ __metadata: linkType: hard "@yarnpkg/shell@npm:^3.2.0": - version: 3.2.2 - resolution: "@yarnpkg/shell@npm:3.2.2" + version: 3.2.4 + resolution: "@yarnpkg/shell@npm:3.2.4" dependencies: - "@yarnpkg/fslib": ^2.6.2 + "@yarnpkg/fslib": ^2.7.1 "@yarnpkg/parsers": ^2.5.1 chalk: ^3.0.0 - clipanion: ^3.2.0-rc.4 + clipanion: 3.2.0-rc.4 cross-spawn: 7.0.3 fast-glob: ^3.2.2 micromatch: ^4.0.2 @@ -2980,7 +3048,7 @@ __metadata: tslib: ^1.13.0 bin: shell: ./lib/cli.js - checksum: 7bb18285545ea918312fef4e4cb22a3475759f7ecddef33a4906ab3cd955ff093a26bde751c38cc7558094a30dac20601f32622c78eed913cfea130478a4dbf9 + checksum: 78eb96d5a5d9af093b50d9727cee33f2edfb5a552b5750f237c3324c89763f965738cc7827dd7d2c1092b54e10b6a16e6390faa2880bfa984f7b29be1de925ba languageName: node linkType: hard @@ -3017,12 +3085,12 @@ __metadata: languageName: node linkType: hard -"acorn@npm:^8.0.4, acorn@npm:^8.4.1, acorn@npm:^8.5.0": - version: 8.7.1 - resolution: "acorn@npm:8.7.1" +"acorn@npm:^8.0.4, acorn@npm:^8.5.0, acorn@npm:^8.7.1": + version: 8.8.0 + resolution: "acorn@npm:8.8.0" bin: acorn: bin/acorn - checksum: aca0aabf98826717920ac2583fdcad0a6fbe4e583fdb6e843af2594e907455aeafe30b1e14f1757cd83ce1776773cf8296ffc3a4acf13f0bd3dfebcf1db6ae80 + checksum: 7270ca82b242eafe5687a11fea6e088c960af712683756abf0791b68855ea9cace3057bd5e998ffcef50c944810c1e0ca1da526d02b32110e13c722aa959afdc languageName: node linkType: hard @@ -3148,13 +3216,6 @@ __metadata: languageName: node linkType: hard -"ansi-regex@npm:^2.0.0": - version: 2.1.1 - resolution: "ansi-regex@npm:2.1.1" - checksum: 190abd03e4ff86794f338a31795d262c1dfe8c91f7e01d04f13f646f1dcb16c5800818f886047876f1272f065570ab86b24b99089f8b68a0e11ff19aed4ca8f1 - languageName: node - linkType: hard - "ansi-regex@npm:^5.0.1": version: 5.0.1 resolution: "ansi-regex@npm:5.0.1" @@ -3204,13 +3265,6 @@ __metadata: languageName: node linkType: hard -"aproba@npm:^1.0.3": - version: 1.2.0 - resolution: "aproba@npm:1.2.0" - checksum: 0fca141966559d195072ed047658b6e6c4fe92428c385dd38e288eacfc55807e7b4989322f030faff32c0f46bb0bc10f1e0ac32ec22d25315a1e5bbc0ebb76dc - languageName: node - linkType: hard - "aproba@npm:^1.0.3 || ^2.0.0": version: 2.0.0 resolution: "aproba@npm:2.0.0" @@ -3219,22 +3273,12 @@ __metadata: linkType: hard "are-we-there-yet@npm:^3.0.0": - version: 3.0.0 - resolution: "are-we-there-yet@npm:3.0.0" + version: 3.0.1 + resolution: "are-we-there-yet@npm:3.0.1" dependencies: delegates: ^1.0.0 readable-stream: ^3.6.0 - checksum: 348edfdd931b0b50868b55402c01c3f64df1d4c229ab6f063539a5025fd6c5f5bb8a0cab409bbed8d75d34762d22aa91b7c20b4204eb8177063158d9ba792981 - languageName: node - linkType: hard - -"are-we-there-yet@npm:~1.1.2": - version: 1.1.7 - resolution: "are-we-there-yet@npm:1.1.7" - dependencies: - delegates: ^1.0.0 - readable-stream: ^2.0.6 - checksum: 70d251719c969b2745bfe5ddf3ebaefa846a636e90a6d5212573676af5d6670e15457761d4725731e19cbebdce42c4ab0cbedf23ab047f2a08274985aa10a3c7 + checksum: 52590c24860fa7173bedeb69a4c05fb573473e860197f618b9a28432ee4379049336727ae3a1f9c4cb083114601c1140cee578376164d0e651217a9843f9fe83 languageName: node linkType: hard @@ -3282,13 +3326,6 @@ __metadata: languageName: node linkType: hard -"array-union@npm:^3.0.1": - version: 3.0.1 - resolution: "array-union@npm:3.0.1" - checksum: 47b29f88258e8f37ffb93ddaa327d4308edd950b52943c172b73558afdd3fa74cfd68816ba5aa4b894242cf281fa3c6d0362ae057e4a18bddbaedbe46ebe7112 - languageName: node - linkType: hard - "astral-regex@npm:^2.0.0": version: 2.0.0 resolution: "astral-regex@npm:2.0.0" @@ -3297,9 +3334,9 @@ __metadata: linkType: hard "async-lock@npm:^1.1.0": - version: 1.3.1 - resolution: "async-lock@npm:1.3.1" - checksum: b11aca6d74431a42f9a3010ce7e55ac18c208f389433f55c07249ccd69dc63bc77fade28957d2d36212811d80b9c067a010341fb1e3d6a819b4fb74a122c4c27 + version: 1.3.2 + resolution: "async-lock@npm:1.3.2" + checksum: d66d6163d4a5c1d09f60118d850da53394a2b8ccae28e0613a88fc33e8b0c2b3266c4f6ed0bc5c9889ad8a926fc886a3e06600775c7264289e8814e39018cd7f languageName: node linkType: hard @@ -3317,12 +3354,12 @@ __metadata: languageName: node linkType: hard -"autoprefixer@npm:^10.3.7, autoprefixer@npm:^10.4.5": - version: 10.4.7 - resolution: "autoprefixer@npm:10.4.7" +"autoprefixer@npm:^10.3.7, autoprefixer@npm:^10.4.7": + version: 10.4.8 + resolution: "autoprefixer@npm:10.4.8" dependencies: - browserslist: ^4.20.3 - caniuse-lite: ^1.0.30001335 + browserslist: ^4.21.3 + caniuse-lite: ^1.0.30001373 fraction.js: ^4.2.0 normalize-range: ^0.1.2 picocolors: ^1.0.0 @@ -3331,7 +3368,7 @@ __metadata: postcss: ^8.1.0 bin: autoprefixer: bin/autoprefixer - checksum: 0e55d0d19806c672ec0c79cc23c27cf77e90edf2600670735266ba33ec5294458f404baaa2f7cd4cfe359cf7a97b3c86f01886bdbdc129a4f2f76ca5977a91af + checksum: 06cb4c497bb948714d5b1b4f7e7465fd88c50f90788fc2020b3d97d7661fb4dd0d9918c1b09dd3e909acd4485cbb27ad99085487d8ed5d75915e646d2b535770 languageName: node linkType: hard @@ -3380,15 +3417,6 @@ __metadata: languageName: node linkType: hard -"babel-plugin-dynamic-import-node@npm:2.3.0": - version: 2.3.0 - resolution: "babel-plugin-dynamic-import-node@npm:2.3.0" - dependencies: - object.assign: ^4.1.0 - checksum: 8a8a631bb5257f1ea7efc64533640aaabadea891c4ec1bcb4a6d10f88f76f326bce88013131a24ef1716ad1046e9919ddf06b6293a863af1178d45466452809d - languageName: node - linkType: hard - "babel-plugin-dynamic-import-node@npm:^2.3.3": version: 2.3.3 resolution: "babel-plugin-dynamic-import-node@npm:2.3.3" @@ -3407,50 +3435,50 @@ __metadata: languageName: node linkType: hard -"babel-plugin-macros@npm:^2.6.1": - version: 2.8.0 - resolution: "babel-plugin-macros@npm:2.8.0" +"babel-plugin-macros@npm:^3.1.0": + version: 3.1.0 + resolution: "babel-plugin-macros@npm:3.1.0" dependencies: - "@babel/runtime": ^7.7.2 - cosmiconfig: ^6.0.0 - resolve: ^1.12.0 - checksum: 59b09a21cf3ae1e14186c1b021917d004b49b953824b24953a54c6502da79e8051d4ac31cfd4a0ae7f6ea5ddf1f7edd93df4895dd3c3982a5b2431859c2889ac + "@babel/runtime": ^7.12.5 + cosmiconfig: ^7.0.0 + resolve: ^1.19.0 + checksum: 765de4abebd3e4688ebdfbff8571ddc8cd8061f839bb6c3e550b0344a4027b04c60491f843296ce3f3379fb356cc873d57a9ee6694262547eb822c14a25be9a6 languageName: node linkType: hard -"babel-plugin-polyfill-corejs2@npm:^0.3.0": - version: 0.3.1 - resolution: "babel-plugin-polyfill-corejs2@npm:0.3.1" +"babel-plugin-polyfill-corejs2@npm:^0.3.2": + version: 0.3.2 + resolution: "babel-plugin-polyfill-corejs2@npm:0.3.2" dependencies: - "@babel/compat-data": ^7.13.11 - "@babel/helper-define-polyfill-provider": ^0.3.1 + "@babel/compat-data": ^7.17.7 + "@babel/helper-define-polyfill-provider": ^0.3.2 semver: ^6.1.1 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: ca873f14ccd6d2942013345a956de8165d0913556ec29756a748157140f5312f79eed487674e0ca562285880f05829b3712d72e1e4b412c2ce46bd6a50b4b975 + checksum: a76e7bb1a5cc0a4507baa523c23f9efd75764069a25845beba92290386e5e48ed85b894005ece3b527e13c3d2d9c6589cc0a23befb72ea6fc7aa8711f231bb4d languageName: node linkType: hard -"babel-plugin-polyfill-corejs3@npm:^0.5.0": - version: 0.5.2 - resolution: "babel-plugin-polyfill-corejs3@npm:0.5.2" +"babel-plugin-polyfill-corejs3@npm:^0.5.3": + version: 0.5.3 + resolution: "babel-plugin-polyfill-corejs3@npm:0.5.3" dependencies: - "@babel/helper-define-polyfill-provider": ^0.3.1 + "@babel/helper-define-polyfill-provider": ^0.3.2 core-js-compat: ^3.21.0 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 2f3184c73f80f00ac876a5ebcad945fd8d2ae70e5f85b7ab6cc3bc69bc74025f4f7070de7abbb2a7274c78e130bd34fc13f4c85342da28205930364a1ef0aa21 + checksum: 9c6644a1b0afbe59e402827fdafc6f44994ff92c5b2f258659cbbfd228f7075dea49e95114af10e66d70f36cbde12ff1d81263eb67be749b3ef0e2c18cf3c16d languageName: node linkType: hard -"babel-plugin-polyfill-regenerator@npm:^0.3.0": - version: 0.3.1 - resolution: "babel-plugin-polyfill-regenerator@npm:0.3.1" +"babel-plugin-polyfill-regenerator@npm:^0.4.0": + version: 0.4.0 + resolution: "babel-plugin-polyfill-regenerator@npm:0.4.0" dependencies: - "@babel/helper-define-polyfill-provider": ^0.3.1 + "@babel/helper-define-polyfill-provider": ^0.3.2 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: f1473df7b700d6795ca41301b1e65a0aff15ce6c1463fc0ce2cf0c821114b0330920f59d4cebf52976363ee817ba29ad2758544a4661a724b08191080b9fe1da + checksum: 699aa9c0dc5a2259d7fa52b26613fa1e782439eee54cd98506991f87fddf0c00eec6c5b1917edf586c170731d9e318903bc41210225a691e7bb8087652bbda94 languageName: node linkType: hard @@ -3507,13 +3535,6 @@ __metadata: languageName: node linkType: hard -"bluebird@npm:^3.7.1": - version: 3.7.2 - resolution: "bluebird@npm:3.7.2" - checksum: 869417503c722e7dc54ca46715f70e15f4d9c602a423a02c825570862d12935be59ed9c7ba34a9b31f186c017c23cac6b54e35446f8353059c101da73eac22ef - languageName: node - linkType: hard - "body-parser@npm:1.20.0": version: 1.20.0 resolution: "body-parser@npm:1.20.0" @@ -3535,18 +3556,18 @@ __metadata: linkType: hard "bonjour-service@npm:^1.0.11": - version: 1.0.12 - resolution: "bonjour-service@npm:1.0.12" + version: 1.0.14 + resolution: "bonjour-service@npm:1.0.14" dependencies: array-flatten: ^2.1.2 dns-equal: ^1.0.0 fast-deep-equal: ^3.1.3 - multicast-dns: ^7.2.4 - checksum: 0dde8504351dcf7b7c354c73cd34625aa0aa3a1c325e054242d8a20aaba3fe11e109b0588f13620643ceedbda9b00c5e0b0e0f8e3d19f0033dc70bf96bdd39a5 + multicast-dns: ^7.2.5 + checksum: 4a825bbf1824147ba8295a182fb3e86a8bae5159a08e2f118e829a0c988043a559f1f6e4eab425fe17ee9a1f080115d30430e78962e53f75bb03e2021ee7c5b2 languageName: node linkType: hard -"boolbase@npm:^1.0.0, boolbase@npm:~1.0.0": +"boolbase@npm:^1.0.0": version: 1.0.0 resolution: "boolbase@npm:1.0.0" checksum: 3e25c80ef626c3a3487c73dbfc70ac322ec830666c9ad915d11b701142fab25ec1e63eff2c450c74347acfd2de854ccde865cd79ef4db1683f7c7b046ea43bb0 @@ -3613,18 +3634,17 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.0.0, browserslist@npm:^4.14.5, browserslist@npm:^4.16.6, browserslist@npm:^4.18.1, browserslist@npm:^4.20.2, browserslist@npm:^4.20.3": - version: 4.20.3 - resolution: "browserslist@npm:4.20.3" +"browserslist@npm:^4.0.0, browserslist@npm:^4.14.5, browserslist@npm:^4.16.6, browserslist@npm:^4.18.1, browserslist@npm:^4.20.2, browserslist@npm:^4.20.3, browserslist@npm:^4.21.3": + version: 4.21.3 + resolution: "browserslist@npm:4.21.3" dependencies: - caniuse-lite: ^1.0.30001332 - electron-to-chromium: ^1.4.118 - escalade: ^3.1.1 - node-releases: ^2.0.3 - picocolors: ^1.0.0 + caniuse-lite: ^1.0.30001370 + electron-to-chromium: ^1.4.202 + node-releases: ^2.0.6 + update-browserslist-db: ^1.0.5 bin: browserslist: cli.js - checksum: 1e4b719ac2ca0fe235218a606e8b8ef16b8809e0973b924158c39fbc435a0b0fe43437ea52dd6ef5ad2efcb83fcb07431244e472270177814217f7c563651f7d + checksum: ff512a7bcca1c530e2854bbdfc7be2791d0fb524097a6340e56e1d5924164c7e4e0a9b070de04cdc4c149d15cb4d4275cb7c626ebbce954278a2823aaad2452a languageName: node linkType: hard @@ -3660,8 +3680,8 @@ __metadata: linkType: hard "cacache@npm:^16.1.0": - version: 16.1.0 - resolution: "cacache@npm:16.1.0" + version: 16.1.3 + resolution: "cacache@npm:16.1.3" dependencies: "@npmcli/fs": ^2.1.0 "@npmcli/move-file": ^2.0.0 @@ -3680,8 +3700,8 @@ __metadata: rimraf: ^3.0.2 ssri: ^9.0.0 tar: ^6.1.11 - unique-filename: ^1.1.1 - checksum: ddfcf92f079f24ccecef4e2ca1e4428443787b61429b921803b020fd0f33d9ac829ac47837b74b40868d8ae4f1b2ed82e164cdaa5508fbd790eee005a9d88469 + unique-filename: ^2.0.0 + checksum: d91409e6e57d7d9a3a25e5dcc589c84e75b178ae8ea7de05cbf6b783f77a5fae938f6e8fda6f5257ed70000be27a681e1e44829251bfffe4c10216002f8f14e6 languageName: node linkType: hard @@ -3700,7 +3720,7 @@ __metadata: languageName: node linkType: hard -"call-bind@npm:^1.0.0": +"call-bind@npm:^1.0.0, call-bind@npm:^1.0.2": version: 1.0.2 resolution: "call-bind@npm:1.0.2" dependencies: @@ -3753,14 +3773,14 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001332, caniuse-lite@npm:^1.0.30001335": - version: 1.0.30001344 - resolution: "caniuse-lite@npm:1.0.30001344" - checksum: 9dba66f796dc98632dced4c5d487d0fad219e137a27c634eec68520f2e598a613e3371b9207e15a078689a629128eca898793e37fc98841821ab481bddad51b9 +"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001370, caniuse-lite@npm:^1.0.30001373": + version: 1.0.30001388 + resolution: "caniuse-lite@npm:1.0.30001388" + checksum: 4934f99bf99521484d4048023f57046812fbc9218924dbe30bb8379cd7b0315d9489b7de4d226a9921b0a9fd5d05b12023930ad6b4104854fb03dcbf999784db languageName: node linkType: hard -"ccount@npm:^1.0.0, ccount@npm:^1.0.3": +"ccount@npm:^1.0.0": version: 1.1.0 resolution: "ccount@npm:1.1.0" checksum: b335a79d0aa4308919cf7507babcfa04ac63d389ebed49dbf26990d4607c8a4713cde93cc83e707d84571ddfe1e7615dad248be9bc422ae4c188210f71b08b78 @@ -3833,33 +3853,9 @@ __metadata: languageName: node linkType: hard -"cheerio@npm:^0.22.0": - version: 0.22.0 - resolution: "cheerio@npm:0.22.0" - dependencies: - css-select: ~1.2.0 - dom-serializer: ~0.1.0 - entities: ~1.1.1 - htmlparser2: ^3.9.1 - lodash.assignin: ^4.0.9 - lodash.bind: ^4.1.4 - lodash.defaults: ^4.0.1 - lodash.filter: ^4.4.0 - lodash.flatten: ^4.2.0 - lodash.foreach: ^4.3.0 - lodash.map: ^4.4.0 - lodash.merge: ^4.4.0 - lodash.pick: ^4.2.1 - lodash.reduce: ^4.4.0 - lodash.reject: ^4.4.0 - lodash.some: ^4.4.0 - checksum: b0a6cfa61eb7ae96e4cb8cfeeb14eb45bb790fa40098509268629c4cecca5b99124aabe6daa1154c497ac8def47bc3f9706cef5f0e8a6177a0c137d4bdaaf8b7 - languageName: node - linkType: hard - -"cheerio@npm:^1.0.0-rc.10": - version: 1.0.0-rc.11 - resolution: "cheerio@npm:1.0.0-rc.11" +"cheerio@npm:^1.0.0-rc.12": + version: 1.0.0-rc.12 + resolution: "cheerio@npm:1.0.0-rc.12" dependencies: cheerio-select: ^2.1.0 dom-serializer: ^2.0.0 @@ -3868,8 +3864,7 @@ __metadata: htmlparser2: ^8.0.1 parse5: ^7.0.0 parse5-htmlparser2-tree-adapter: ^7.0.0 - tslib: ^2.4.0 - checksum: 7619edcbecafb70ca6ca842ce149307a84e8d451432a888d82959b2aa04e2090701658f25eac75821e0832cc1305bdbcf02f17175102fc1723f119f3c9ece17a + checksum: 5d4c1b7a53cf22d3a2eddc0aff70cf23cbb30d01a4c79013e703a012475c02461aa1fcd99127e8d83a02216386ed6942b2c8103845fd0812300dd199e6e7e054 languageName: node linkType: hard @@ -3921,11 +3916,11 @@ __metadata: linkType: hard "clean-css@npm:^5.2.2, clean-css@npm:^5.3.0": - version: 5.3.0 - resolution: "clean-css@npm:5.3.0" + version: 5.3.1 + resolution: "clean-css@npm:5.3.1" dependencies: source-map: ~0.6.0 - checksum: 29e15ef4678e1eb571546128cb7a922c5301c1bd12ee30a6e8141c72789b8130739a9a5b335435a6ee108400336a561ebd9faabe1a7d8808eb48d39cff390cd7 + checksum: 860696c60503cbfec480b5f92f62729246304b55950571af7292f2687b57f86b277f2b9fefe6f64643d409008018b78383972b55c2cc859792dcc8658988fb16 languageName: node linkType: hard @@ -3967,9 +3962,9 @@ __metadata: linkType: hard "cli-spinners@npm:^2.3.0": - version: 2.6.1 - resolution: "cli-spinners@npm:2.6.1" - checksum: 423409baaa7a58e5104b46ca1745fbfc5888bbd0b0c5a626e052ae1387060839c8efd512fb127e25769b3dc9562db1dc1b5add6e0b93b7ef64f477feb6416a45 + version: 2.7.0 + resolution: "cli-spinners@npm:2.7.0" + checksum: a9afaf73f58d1f951fb23742f503631b3cf513f43f4c7acb1b640100eb76bfa16efbcd1994d149ffc6603a6d75dd3d4a516a76f125f90dce437de9b16fd0ee6f languageName: node linkType: hard @@ -3996,7 +3991,18 @@ __metadata: languageName: node linkType: hard -"clipanion@npm:^3.2.0-rc.10, clipanion@npm:^3.2.0-rc.4": +"clipanion@npm:3.2.0-rc.4": + version: 3.2.0-rc.4 + resolution: "clipanion@npm:3.2.0-rc.4" + dependencies: + typanion: ^3.3.1 + peerDependencies: + typanion: "*" + checksum: c9d8ba9e16dca3016c32f42107a7602c52c9176626e0c815113c32b614ca125a9707221ec9df9c0a06e9741a23e0664153db1522c4f80b29f4b4d427fba002be + languageName: node + linkType: hard + +"clipanion@npm:^3.2.0-rc.10": version: 3.2.0-rc.11 resolution: "clipanion@npm:3.2.0-rc.11" dependencies: @@ -4019,18 +4025,18 @@ __metadata: linkType: hard "clone-response@npm:^1.0.2": - version: 1.0.2 - resolution: "clone-response@npm:1.0.2" + version: 1.0.3 + resolution: "clone-response@npm:1.0.3" dependencies: mimic-response: ^1.0.0 - checksum: 2d0e61547fc66276e0903be9654ada422515f5a15741691352000d47e8c00c226061221074ce2c0064d12e975e84a8687cfd35d8b405750cb4e772f87b256eda + checksum: 4e671cac39b11c60aa8ba0a450657194a5d6504df51bca3fac5b3bd0145c4f8e8464898f87c8406b83232e3bc5cca555f51c1f9c8ac023969ebfbf7f6bdabb2e languageName: node linkType: hard -"clsx@npm:^1.1.1": - version: 1.1.1 - resolution: "clsx@npm:1.1.1" - checksum: ff052650329773b9b245177305fc4c4dc3129f7b2be84af4f58dc5defa99538c61d4207be7419405a5f8f3d92007c954f4daba5a7b74e563d5de71c28c830063 +"clsx@npm:^1.1.1, clsx@npm:^1.2.1": + version: 1.2.1 + resolution: "clsx@npm:1.2.1" + checksum: 30befca8019b2eb7dbad38cff6266cf543091dae2825c856a62a8ccf2c3ab9c2907c4d12b288b73101196767f66812365400a227581484a05f968b0307cfaf12 languageName: node linkType: hard @@ -4043,13 +4049,6 @@ __metadata: languageName: node linkType: hard -"code-point-at@npm:^1.0.0": - version: 1.1.0 - resolution: "code-point-at@npm:1.1.0" - checksum: 17d5666611f9b16d64fdf48176d9b7fb1c7d1c1607a189f7e600040a11a6616982876af148230336adb7d8fe728a559f743a4e29db3747e3b1a32fa7f4529681 - languageName: node - linkType: hard - "collapse-white-space@npm:^1.0.2": version: 1.0.6 resolution: "collapse-white-space@npm:1.0.6" @@ -4119,16 +4118,16 @@ __metadata: linkType: hard "colord@npm:^2.9.1": - version: 2.9.2 - resolution: "colord@npm:2.9.2" - checksum: 2aa6a9b3abbce74ba3c563886cfeb433ea0d7df5ad6f4a560005eddab1ddf7c0fc98f39b09b599767a19c86dd3837b77f66f036e479515d4b17347006dbd6d9f + version: 2.9.3 + resolution: "colord@npm:2.9.3" + checksum: 95d909bfbcfd8d5605cbb5af56f2d1ce2b323990258fd7c0d2eb0e6d3bb177254d7fb8213758db56bb4ede708964f78c6b992b326615f81a18a6aaf11d64c650 languageName: node linkType: hard "colorette@npm:^2.0.10": - version: 2.0.16 - resolution: "colorette@npm:2.0.16" - checksum: cd55596a3a2d1071c1a28eee7fd8a5387593ff1bd10a3e8d0a6221499311fe34a9f2b9272d77c391e0e003dcdc8934fb2f8d106e7ef1f7516f8060c901d41a27 + version: 2.0.19 + resolution: "colorette@npm:2.0.19" + checksum: 888cf5493f781e5fcf54ce4d49e9d7d698f96ea2b2ef67906834bb319a392c667f9ec69f4a10e268d2946d13a9503d2d19b3abaaaf174e3451bfe91fb9d82427 languageName: node linkType: hard @@ -4226,10 +4225,10 @@ __metadata: languageName: node linkType: hard -"connect-history-api-fallback@npm:^1.6.0": - version: 1.6.0 - resolution: "connect-history-api-fallback@npm:1.6.0" - checksum: 804ca2be28c999032ecd37a9f71405e5d7b7a4b3defcebbe41077bb8c5a0a150d7b59f51dcc33b2de30bc7e217a31d10f8cfad27e8e74c2fc7655eeba82d6e7e +"connect-history-api-fallback@npm:^2.0.0": + version: 2.0.0 + resolution: "connect-history-api-fallback@npm:2.0.0" + checksum: dc5368690f4a5c413889792f8df70d5941ca9da44523cde3f87af0745faee5ee16afb8195434550f0504726642734f2683d6c07f8b460f828a12c45fbd4c9a68 languageName: node linkType: hard @@ -4240,7 +4239,7 @@ __metadata: languageName: node linkType: hard -"console-control-strings@npm:^1.0.0, console-control-strings@npm:^1.1.0, console-control-strings@npm:~1.1.0": +"console-control-strings@npm:^1.1.0": version: 1.1.0 resolution: "console-control-strings@npm:1.1.0" checksum: 8755d76787f94e6cf79ce4666f0c5519906d7f5b02d4b884cf41e11dcd759ed69c57da0670afd9236d229a46e0f9cf519db0cd829c6dca820bb5a5c3def584ed @@ -4314,43 +4313,43 @@ __metadata: languageName: node linkType: hard -"copy-webpack-plugin@npm:^10.2.4": - version: 10.2.4 - resolution: "copy-webpack-plugin@npm:10.2.4" +"copy-webpack-plugin@npm:^11.0.0": + version: 11.0.0 + resolution: "copy-webpack-plugin@npm:11.0.0" dependencies: - fast-glob: ^3.2.7 + fast-glob: ^3.2.11 glob-parent: ^6.0.1 - globby: ^12.0.2 + globby: ^13.1.1 normalize-path: ^3.0.0 schema-utils: ^4.0.0 serialize-javascript: ^6.0.0 peerDependencies: webpack: ^5.1.0 - checksum: 87f0f4530ab3e58ec06a7c3182028dfd8cc85b045a0d18c4464caafeae1ed1141c2aad6eae37e100a74a72b69dc48c93af358c07038b7a22f490a678c0ab142e + checksum: df4f8743f003a29ee7dd3d9b1789998a3a99051c92afb2ba2203d3dacfa696f4e757b275560fafb8f206e520a0aa78af34b990324a0e36c2326cefdeef3ca82e languageName: node linkType: hard "core-js-compat@npm:^3.21.0, core-js-compat@npm:^3.22.1": - version: 3.22.7 - resolution: "core-js-compat@npm:3.22.7" + version: 3.25.0 + resolution: "core-js-compat@npm:3.25.0" dependencies: - browserslist: ^4.20.3 + browserslist: ^4.21.3 semver: 7.0.0 - checksum: 036148c150ae6dec864cf175100d56ba0fa2ee9e43b94e6e3d9605d39d53c367aa2409aa1affc485a3135fd73bcab7a4f5f3ab2707420923f7f144ac1997eccf + checksum: a40e072a67e65f34cb4b3a85bfdbbfcb0f3e1e2e171718763b13da4da897b5b6228603468027a0920096f0ee7e2c3906de95e26c9b6fe8f9595552bf2bc8f852 languageName: node linkType: hard "core-js-pure@npm:^3.20.2": - version: 3.22.7 - resolution: "core-js-pure@npm:3.22.7" - checksum: 6358882377e1f0433efc1c492cc5d764d664f7651a72e534b1c9d989fe82b9986b7602b735fee03f368187fb17c3829047a949c41078c710c92b3557d889042b + version: 3.25.0 + resolution: "core-js-pure@npm:3.25.0" + checksum: 041cef3c4fa03b30eea6aa8539db00a02ea264e8542b9b787428f43e727e67050c742f46dbd75bc9ab544524a54e1ee55d8b23602dc8a2da485a3741a5f95df7 languageName: node linkType: hard -"core-js@npm:^3.22.3": - version: 3.22.7 - resolution: "core-js@npm:3.22.7" - checksum: c5f1d8a96b6d1828d02583603d9df1fcbf45f95454585ff9d49ba7ea1470bf1422d00561044939bf4952465ae4ae2bf30a39b4874da8ed2741a3f3996bd175ab +"core-js@npm:^3.23.3": + version: 3.25.0 + resolution: "core-js@npm:3.25.0" + checksum: 5a72740bf5babaf2e6203da4c0e831a0b97c3fe6f21b4c1aa8601d3a927660a22128dd8f0e86ce84778c4e5372ab0fc03c7944afa7e215c7fb13b2c79d5d5504 languageName: node linkType: hard @@ -4414,12 +4413,12 @@ __metadata: languageName: node linkType: hard -"css-declaration-sorter@npm:^6.2.2": - version: 6.2.2 - resolution: "css-declaration-sorter@npm:6.2.2" +"css-declaration-sorter@npm:^6.3.0": + version: 6.3.1 + resolution: "css-declaration-sorter@npm:6.3.1" peerDependencies: postcss: ^8.0.9 - checksum: afd3aea1b763b7abb5a9d0e10e973e99520b528522be421d9ef13d4fa7ead2cd48acd85d48c0fd0e954f596da2181dafbafc176a080ab017ebd1909a8231c9b4 + checksum: ff0d9989ee21ec4c42430b9bb86c43f973ed5024d68f30edc1e3fb07a22828ce3c3e5b922019f2ccbff606722e43c407c5c76e3cddac523ac4afcb31e4b2601c languageName: node linkType: hard @@ -4441,13 +4440,13 @@ __metadata: languageName: node linkType: hard -"css-minimizer-webpack-plugin@npm:^3.4.1": - version: 3.4.1 - resolution: "css-minimizer-webpack-plugin@npm:3.4.1" +"css-minimizer-webpack-plugin@npm:^4.0.0": + version: 4.0.0 + resolution: "css-minimizer-webpack-plugin@npm:4.0.0" dependencies: - cssnano: ^5.0.6 - jest-worker: ^27.0.2 - postcss: ^8.3.5 + cssnano: ^5.1.8 + jest-worker: ^27.5.1 + postcss: ^8.4.13 schema-utils: ^4.0.0 serialize-javascript: ^6.0.0 source-map: ^0.6.1 @@ -4462,7 +4461,7 @@ __metadata: optional: true esbuild: optional: true - checksum: 065c6c1eadb7c99267db5d04d6f3909e9968b73c4cb79ab9e4502a5fbf1a3d564cfe6f8e0bff8e4ab00d4ed233e9c3c76a4ebe0ee89150b3d9ecb064ddf1e5e9 + checksum: 18487ee9aacdb0cc4e9fc1921f5d7a519c94203332b845b9a6d95434365d275fafff7dbfe21355347b8bbb8266078b7e60f7bac771f15eb30dfed5a29016debc languageName: node linkType: hard @@ -4492,18 +4491,6 @@ __metadata: languageName: node linkType: hard -"css-select@npm:~1.2.0": - version: 1.2.0 - resolution: "css-select@npm:1.2.0" - dependencies: - boolbase: ~1.0.0 - css-what: 2.1 - domutils: 1.5.1 - nth-check: ~1.0.1 - checksum: 607cca60d2f5c56701fe5f800bbe668b114395c503d4e4808edbbbe70b8be3c96a6407428dc0227fcbdf335b20468e6a9e7fd689185edfb57d402e1e4837c9b7 - languageName: node - linkType: hard - "css-tree@npm:^1.1.2, css-tree@npm:^1.1.3": version: 1.1.3 resolution: "css-tree@npm:1.1.3" @@ -4514,13 +4501,6 @@ __metadata: languageName: node linkType: hard -"css-what@npm:2.1": - version: 2.1.3 - resolution: "css-what@npm:2.1.3" - checksum: a52d56c591a7e1c37506d0d8c4fdef72537fb8eb4cb68711485997a88d76b5a3342b73a7c79176268f95b428596c447ad7fa3488224a6b8b532e2f1f2ee8545c - languageName: node - linkType: hard - "css-what@npm:^6.0.1, css-what@npm:^6.1.0": version: 6.1.0 resolution: "css-what@npm:6.1.0" @@ -4537,58 +4517,58 @@ __metadata: languageName: node linkType: hard -"cssnano-preset-advanced@npm:^5.3.3": - version: 5.3.5 - resolution: "cssnano-preset-advanced@npm:5.3.5" +"cssnano-preset-advanced@npm:^5.3.8": + version: 5.3.8 + resolution: "cssnano-preset-advanced@npm:5.3.8" dependencies: autoprefixer: ^10.3.7 - cssnano-preset-default: ^5.2.9 + cssnano-preset-default: ^5.2.12 postcss-discard-unused: ^5.1.0 postcss-merge-idents: ^5.1.1 postcss-reduce-idents: ^5.2.0 postcss-zindex: ^5.1.0 peerDependencies: postcss: ^8.2.15 - checksum: a4c26b684ed8b1ca1e5c70fba91ff120eabeacf532d8e917c818698206aae473bcd47847c7505b66d5763f9a604a848036ba9f6a9f4534fd5d7bd83a4ea0092a + checksum: ba18332d39b629393931410779b1e15f7f6019aa223fa419fad4ee9eecfa586f3f9e659acabb83a91db8998c95d91efc43d15551cfadbf8b587c5a90bf9002d9 languageName: node linkType: hard -"cssnano-preset-default@npm:^5.2.9": - version: 5.2.9 - resolution: "cssnano-preset-default@npm:5.2.9" +"cssnano-preset-default@npm:^5.2.12": + version: 5.2.12 + resolution: "cssnano-preset-default@npm:5.2.12" dependencies: - css-declaration-sorter: ^6.2.2 + css-declaration-sorter: ^6.3.0 cssnano-utils: ^3.1.0 postcss-calc: ^8.2.3 postcss-colormin: ^5.3.0 - postcss-convert-values: ^5.1.1 - postcss-discard-comments: ^5.1.1 + postcss-convert-values: ^5.1.2 + postcss-discard-comments: ^5.1.2 postcss-discard-duplicates: ^5.1.0 postcss-discard-empty: ^5.1.1 postcss-discard-overridden: ^5.1.0 - postcss-merge-longhand: ^5.1.5 - postcss-merge-rules: ^5.1.1 + postcss-merge-longhand: ^5.1.6 + postcss-merge-rules: ^5.1.2 postcss-minify-font-values: ^5.1.0 postcss-minify-gradients: ^5.1.1 postcss-minify-params: ^5.1.3 - postcss-minify-selectors: ^5.2.0 + postcss-minify-selectors: ^5.2.1 postcss-normalize-charset: ^5.1.0 postcss-normalize-display-values: ^5.1.0 - postcss-normalize-positions: ^5.1.0 - postcss-normalize-repeat-style: ^5.1.0 + postcss-normalize-positions: ^5.1.1 + postcss-normalize-repeat-style: ^5.1.1 postcss-normalize-string: ^5.1.0 postcss-normalize-timing-functions: ^5.1.0 postcss-normalize-unicode: ^5.1.0 postcss-normalize-url: ^5.1.0 postcss-normalize-whitespace: ^5.1.1 - postcss-ordered-values: ^5.1.1 + postcss-ordered-values: ^5.1.3 postcss-reduce-initial: ^5.1.0 postcss-reduce-transforms: ^5.1.0 postcss-svgo: ^5.1.0 postcss-unique-selectors: ^5.1.1 peerDependencies: postcss: ^8.2.15 - checksum: a93ecc41274456f2e482700df795d1e431142987d94ff54b3d0b49fe02f092945aa5eaf90d9f65f135ebea2b8c22efe71b944e489c8ef1a397a8257571bd6477 + checksum: 3d6c05e7719f05c577c3123dc8f823ddc055ec5402ee8184cea1832c209a87ab11aa2aa2cba3e6f4ae6e144c1f3f5122fad1bc7c3086bc3441770f2733e03f58 languageName: node linkType: hard @@ -4601,16 +4581,16 @@ __metadata: languageName: node linkType: hard -"cssnano@npm:^5.0.6, cssnano@npm:^5.1.7": - version: 5.1.9 - resolution: "cssnano@npm:5.1.9" +"cssnano@npm:^5.1.12, cssnano@npm:^5.1.8": + version: 5.1.13 + resolution: "cssnano@npm:5.1.13" dependencies: - cssnano-preset-default: ^5.2.9 + cssnano-preset-default: ^5.2.12 lilconfig: ^2.0.3 yaml: ^1.10.2 peerDependencies: postcss: ^8.2.15 - checksum: 25932e83187bfffbe6116d4d5fef20f6bfa9fbd1cdc1145173bc757579740a4ae6b9d40ca67bdf7b644ff2c65784a1a168e1a3e08208120ab6c822056b356b95 + checksum: 3af0810c98626794e3386e690cd633c73ce472cb138f1011b69956de5071920ddce9d45f857018bb72cd2c3ed19674d65edade591110a6d5acd7c3109ef5d5d6 languageName: node linkType: hard @@ -4706,7 +4686,7 @@ __metadata: languageName: node linkType: hard -"define-properties@npm:^1.1.3": +"define-properties@npm:^1.1.4": version: 1.1.4 resolution: "define-properties@npm:1.1.4" dependencies: @@ -4716,7 +4696,7 @@ __metadata: languageName: node linkType: hard -"del@npm:^6.0.0": +"del@npm:^6.1.1": version: 6.1.1 resolution: "del@npm:6.1.1" dependencies: @@ -4833,11 +4813,11 @@ __metadata: linkType: hard "dns-packet@npm:^5.2.2": - version: 5.3.1 - resolution: "dns-packet@npm:5.3.1" + version: 5.4.0 + resolution: "dns-packet@npm:5.4.0" dependencies: "@leichtgewicht/ip-codec": ^2.0.1 - checksum: 196ff74a0669126cf5fc901a5849b72f625bd7a4cb163b3f4d41fbe19ed0b017cf7674daef5b0acbd448c094fcd795e501d7066f301be428e4acecfcf3c5f336 + checksum: a169963848e8539dfd8a19058562f9e1c15c0f82cbf76fa98942f11c46f3c74e7e7c82e3a8a5182d4c9e6ff19e21be738dbd098a876dde755d3aedd2cc730880 languageName: node linkType: hard @@ -4860,16 +4840,6 @@ __metadata: languageName: node linkType: hard -"dom-serializer@npm:0": - version: 0.2.2 - resolution: "dom-serializer@npm:0.2.2" - dependencies: - domelementtype: ^2.0.1 - entities: ^2.0.0 - checksum: 376344893e4feccab649a14ca1a46473e9961f40fe62479ea692d4fee4d9df1c00ca8654811a79c1ca7b020096987e1ca4fb4d7f8bae32c1db800a680a0e5d5e - languageName: node - linkType: hard - "dom-serializer@npm:^1.0.1": version: 1.4.1 resolution: "dom-serializer@npm:1.4.1" @@ -4892,23 +4862,6 @@ __metadata: languageName: node linkType: hard -"dom-serializer@npm:~0.1.0": - version: 0.1.1 - resolution: "dom-serializer@npm:0.1.1" - dependencies: - domelementtype: ^1.3.0 - entities: ^1.1.1 - checksum: 4f6a3eff802273741931cfd3c800fab4e683236eed10628d6605f52538a6bc0ce4770f3ca2ad68a27412c103ae9b6cdaed3c0a8e20d2704192bde497bc875215 - languageName: node - linkType: hard - -"domelementtype@npm:1, domelementtype@npm:^1.3.0, domelementtype@npm:^1.3.1": - version: 1.3.1 - resolution: "domelementtype@npm:1.3.1" - checksum: 7893da40218ae2106ec6ffc146b17f203487a52f5228b032ea7aa470e41dfe03e1bd762d0ee0139e792195efda765434b04b43cddcf63207b098f6ae44b36ad6 - languageName: node - linkType: hard - "domelementtype@npm:^2.0.1, domelementtype@npm:^2.2.0, domelementtype@npm:^2.3.0": version: 2.3.0 resolution: "domelementtype@npm:2.3.0" @@ -4916,15 +4869,6 @@ __metadata: languageName: node linkType: hard -"domhandler@npm:^2.3.0": - version: 2.4.2 - resolution: "domhandler@npm:2.4.2" - dependencies: - domelementtype: 1 - checksum: 49bd70c9c784f845cd047e1dfb3611bd10891c05719acfc93f01fc726a419ed09fbe0b69f9064392d556a63fffc5a02010856cedae9368f4817146d95a97011f - languageName: node - linkType: hard - "domhandler@npm:^4.0.0, domhandler@npm:^4.2.0, domhandler@npm:^4.3.1": version: 4.3.1 resolution: "domhandler@npm:4.3.1" @@ -4943,26 +4887,6 @@ __metadata: languageName: node linkType: hard -"domutils@npm:1.5.1": - version: 1.5.1 - resolution: "domutils@npm:1.5.1" - dependencies: - dom-serializer: 0 - domelementtype: 1 - checksum: 800d1f9d1c2e637267dae078ff6e24461e6be1baeb52fa70f2e7e7520816c032a925997cd15d822de53ef9896abb1f35e5c439d301500a9cd6b46a395f6f6ca0 - languageName: node - linkType: hard - -"domutils@npm:^1.5.1": - version: 1.7.0 - resolution: "domutils@npm:1.7.0" - dependencies: - dom-serializer: 0 - domelementtype: 1 - checksum: f60a725b1f73c1ae82f4894b691601ecc6ecb68320d87923ac3633137627c7865725af813ae5d188ad3954283853bcf46779eb50304ec5d5354044569fcefd2b - languageName: node - linkType: hard - "domutils@npm:^2.5.2, domutils@npm:^2.8.0": version: 2.8.0 resolution: "domutils@npm:2.8.0" @@ -5005,9 +4929,9 @@ __metadata: linkType: hard "duplexer3@npm:^0.1.4": - version: 0.1.4 - resolution: "duplexer3@npm:0.1.4" - checksum: c2fd6969314607d23439c583699aaa43c4100d66b3e161df55dccd731acc57d5c81a64bb4f250805fbe434ddb1d2623fee2386fb890f5886ca1298690ec53415 + version: 0.1.5 + resolution: "duplexer3@npm:0.1.5" + checksum: e677cb4c48f031ca728601d6a20bf6aed4c629d69ef9643cb89c67583d673c4ec9317cc6427501f38bd8c368d3a18f173987cc02bd99d8cf8fe3d94259a22a20 languageName: node linkType: hard @@ -5032,10 +4956,10 @@ __metadata: languageName: node linkType: hard -"electron-to-chromium@npm:^1.4.118": - version: 1.4.140 - resolution: "electron-to-chromium@npm:1.4.140" - checksum: bf06151bdd76dbcf00c97215d0c79479a4d2116e4a1734ee319cf83865ceab56ee834b3f4347bf9c01ae5c0a953fb0b93e2f097c3ed33f6292d03bcb40af651d +"electron-to-chromium@npm:^1.4.202": + version: 1.4.241 + resolution: "electron-to-chromium@npm:1.4.241" + checksum: a3d77207f4c46b64633eab3dcb46b4dcbd3e7e03f12ab81928a20ac4f585a9f04968176094318995f376645436325ddc8085f99bc985bc53b04d6c2016f1f9b0 languageName: node linkType: hard @@ -5092,20 +5016,13 @@ __metadata: languageName: node linkType: hard -"enhanced-resolve@npm:^5.9.3": - version: 5.9.3 - resolution: "enhanced-resolve@npm:5.9.3" +"enhanced-resolve@npm:^5.10.0": + version: 5.10.0 + resolution: "enhanced-resolve@npm:5.10.0" dependencies: graceful-fs: ^4.2.4 tapable: ^2.2.0 - checksum: 64c2dbbdd608d1a4df93b6e60786c603a1faf3b2e66dfd051d62cf4cfaeeb5e800166183685587208d62e9f7afff3f78f3d5978e32cd80125ba0c83b59a79d78 - languageName: node - linkType: hard - -"entities@npm:^1.1.1, entities@npm:~1.1.1": - version: 1.1.2 - resolution: "entities@npm:1.1.2" - checksum: d537b02799bdd4784ffd714d000597ed168727bddf4885da887c5a491d735739029a00794f1998abbf35f3f6aeda32ef5c15010dca1817d401903a501b6d3e05 + checksum: 0bb9830704db271610f900e8d79d70a740ea16f251263362b0c91af545576d09fe50103496606c1300a05e588372d6f9780a9bc2e30ce8ef9b827ec8f44687ff languageName: node linkType: hard @@ -5116,17 +5033,10 @@ __metadata: languageName: node linkType: hard -"entities@npm:^3.0.1": - version: 3.0.1 - resolution: "entities@npm:3.0.1" - checksum: aaf7f12033f0939be91f5161593f853f2da55866db55ccbf72f45430b8977e2b79dbd58c53d0fdd2d00bd7d313b75b0968d09f038df88e308aa97e39f9456572 - languageName: node - linkType: hard - -"entities@npm:^4.2.0, entities@npm:^4.3.0": - version: 4.3.0 - resolution: "entities@npm:4.3.0" - checksum: f6abacfe1f4ee06a98aae713ed0b97d4dbd1fcd4c90840d16c6c7535a4e34df1445614c987b7b359ab8362823f050158b8fd435652f0ac18c45683174cbec6ce +"entities@npm:^4.2.0, entities@npm:^4.3.0, entities@npm:^4.4.0": + version: 4.4.0 + resolution: "entities@npm:4.4.0" + checksum: 84d250329f4b56b40fa93ed067b194db21e8815e4eb9b59f43a086f0ecd342814f6bc483de8a77da5d64e0f626033192b1b4f1792232a7ea6b970ebe0f3187c2 languageName: node linkType: hard @@ -5376,7 +5286,7 @@ __metadata: languageName: node linkType: hard -"fast-glob@npm:^3.2.2, fast-glob@npm:^3.2.7, fast-glob@npm:^3.2.9": +"fast-glob@npm:^3.2.11, fast-glob@npm:^3.2.2, fast-glob@npm:^3.2.9": version: 3.2.11 resolution: "fast-glob@npm:3.2.11" dependencies: @@ -5641,7 +5551,7 @@ __metadata: languageName: node linkType: hard -"fs-monkey@npm:1.0.3": +"fs-monkey@npm:^1.0.3": version: 1.0.3 resolution: "fs-monkey@npm:1.0.3" checksum: cf50804833f9b88a476911ae911fe50f61a98d986df52f890bd97e7262796d023698cb2309fa9b74fdd8974f04315b648748a0a8ee059e7d5257b293bfc409c0 @@ -5697,22 +5607,6 @@ __metadata: languageName: node linkType: hard -"gauge@npm:~2.7.3": - version: 2.7.4 - resolution: "gauge@npm:2.7.4" - dependencies: - aproba: ^1.0.3 - console-control-strings: ^1.0.0 - has-unicode: ^2.0.0 - object-assign: ^4.1.0 - signal-exit: ^3.0.0 - string-width: ^1.0.1 - strip-ansi: ^3.0.1 - wide-align: ^1.1.0 - checksum: a89b53cee65579b46832e050b5f3a79a832cc422c190de79c6b8e2e15296ab92faddde6ddf2d376875cbba2b043efa99b9e1ed8124e7365f61b04e3cee9d40ee - languageName: node - linkType: hard - "gensync@npm:^1.0.0-beta.1, gensync@npm:^1.0.0-beta.2": version: 1.0.0-beta.2 resolution: "gensync@npm:1.0.0-beta.2" @@ -5721,13 +5615,13 @@ __metadata: linkType: hard "get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.1": - version: 1.1.1 - resolution: "get-intrinsic@npm:1.1.1" + version: 1.1.2 + resolution: "get-intrinsic@npm:1.1.2" dependencies: function-bind: ^1.1.1 has: ^1.0.3 - has-symbols: ^1.0.1 - checksum: a9fe2ca8fa3f07f9b0d30fb202bcd01f3d9b9b6b732452e79c48e79f7d6d8d003af3f9e38514250e3553fdc83c61650851cb6870832ac89deaaceb08e3721a17 + has-symbols: ^1.0.3 + checksum: 252f45491f2ba88ebf5b38018020c7cc3279de54b1d67ffb70c0cdf1dfa8ab31cd56467b5d117a8b4275b7a4dde91f86766b163a17a850f036528a7b2faafb2b languageName: node linkType: hard @@ -5879,17 +5773,16 @@ __metadata: languageName: node linkType: hard -"globby@npm:^12.0.2": - version: 12.2.0 - resolution: "globby@npm:12.2.0" +"globby@npm:^13.1.1": + version: 13.1.2 + resolution: "globby@npm:13.1.2" dependencies: - array-union: ^3.0.1 dir-glob: ^3.0.1 - fast-glob: ^3.2.7 - ignore: ^5.1.9 + fast-glob: ^3.2.11 + ignore: ^5.2.0 merge2: ^1.4.1 slash: ^4.0.0 - checksum: 2539379a7fff3473d3e7c68b4540ba38f36970f43f760e36e301515d5cb98a0c5736554957d90390906bee632327beb2f9518d1acd6911f61e436db11b0da5b5 + checksum: c148fcda0c981f00fb434bb94ca258f0a9d23cedbde6fb3f37098e1abde5b065019e2c63fe2aa2fad4daf2b54bf360b4d0423d85fb3a63d09ed75a2837d4de0f languageName: node linkType: hard @@ -5970,14 +5863,14 @@ __metadata: languageName: node linkType: hard -"has-symbols@npm:^1.0.1": +"has-symbols@npm:^1.0.3": version: 1.0.3 resolution: "has-symbols@npm:1.0.3" checksum: a054c40c631c0d5741a8285010a0777ea0c068f99ed43e5d6eb12972da223f8af553a455132fdb0801bdcfa0e0f443c0c03a68d8555aa529b3144b446c3f2410 languageName: node linkType: hard -"has-unicode@npm:^2.0.0, has-unicode@npm:^2.0.1": +"has-unicode@npm:^2.0.1": version: 2.0.1 resolution: "has-unicode@npm:2.0.1" checksum: 1eab07a7436512db0be40a710b29b5dc21fa04880b7f63c9980b706683127e3c1b57cb80ea96d47991bdae2dfe479604f6a1ba410106ee1046a41d1bd0814400 @@ -6015,19 +5908,6 @@ __metadata: languageName: node linkType: hard -"hast-util-from-parse5@npm:^5.0.0": - version: 5.0.3 - resolution: "hast-util-from-parse5@npm:5.0.3" - dependencies: - ccount: ^1.0.3 - hastscript: ^5.0.0 - property-information: ^5.0.0 - web-namespaces: ^1.1.2 - xtend: ^4.0.1 - checksum: 31ecd040dd03bda38b8efbcb93ed95b19619bc8548da19973b6cdbb36302bc54c84662be345e6a4f3a53cf8b33956b502916e349871dc095802ca39cfe55040a - languageName: node - linkType: hard - "hast-util-from-parse5@npm:^6.0.0": version: 6.0.1 resolution: "hast-util-from-parse5@npm:6.0.1" @@ -6080,18 +5960,6 @@ __metadata: languageName: node linkType: hard -"hastscript@npm:^5.0.0": - version: 5.1.2 - resolution: "hastscript@npm:5.1.2" - dependencies: - comma-separated-tokens: ^1.0.0 - hast-util-parse-selector: ^2.0.0 - property-information: ^5.0.0 - space-separated-tokens: ^1.0.0 - checksum: 662321af446f09c76d67af31d05823f382ce1e6c007828dc77f899f310cea682c00216b67c317a4ebe7f0c05e50552c4810d214e6ed4e95388f7b7d7fc93158f - languageName: node - linkType: hard - "hastscript@npm:^6.0.0": version: 6.0.0 resolution: "hastscript@npm:6.0.0" @@ -6209,20 +6077,6 @@ __metadata: languageName: node linkType: hard -"htmlparser2@npm:^3.9.1": - version: 3.10.1 - resolution: "htmlparser2@npm:3.10.1" - dependencies: - domelementtype: ^1.3.1 - domhandler: ^2.3.0 - domutils: ^1.5.1 - entities: ^1.1.1 - inherits: ^2.0.1 - readable-stream: ^3.1.1 - checksum: 6875f7dd875aa10be17d9b130e3738cd8ed4010b1f2edaf4442c82dfafe9d9336b155870dcc39f38843cbf7fef5e4fcfdf0c4c1fd4db3a1b91a1e0ee8f6c3475 - languageName: node - linkType: hard - "htmlparser2@npm:^6.1.0": version: 6.1.0 resolution: "htmlparser2@npm:6.1.0" @@ -6287,9 +6141,9 @@ __metadata: linkType: hard "http-parser-js@npm:>=0.5.1": - version: 0.5.6 - resolution: "http-parser-js@npm:0.5.6" - checksum: 8a92f6782542211c77936104ea1eca3c86a95420eb286b100f6421630f29d8f94fd4cc7a245df8e078791d86cd9a237091094440ffb0cd1b44a3f85bfbf539fa + version: 0.5.8 + resolution: "http-parser-js@npm:0.5.8" + checksum: 6bbdf2429858e8cf13c62375b0bfb6dc3955ca0f32e58237488bc86cd2378f31d31785fd3ac4ce93f1c74e0189cf8823c91f5cb061696214fd368d2452dc871d languageName: node linkType: hard @@ -6405,7 +6259,7 @@ __metadata: languageName: node linkType: hard -"ignore@npm:^5.1.4, ignore@npm:^5.1.9, ignore@npm:^5.2.0": +"ignore@npm:^5.1.4, ignore@npm:^5.2.0": version: 5.2.0 resolution: "ignore@npm:5.2.0" checksum: 6b1f926792d614f64c6c83da3a1f9c83f6196c2839aa41e1e32dd7b8d174cef2e329d75caabb62cb61ce9dc432f75e67d07d122a037312db7caa73166a1bdb77 @@ -6413,20 +6267,20 @@ __metadata: linkType: hard "image-size@npm:^1.0.1": - version: 1.0.1 - resolution: "image-size@npm:1.0.1" + version: 1.0.2 + resolution: "image-size@npm:1.0.2" dependencies: queue: 6.0.2 bin: image-size: bin/image-size.js - checksum: ffa74672dc7a1b6529c66255adbfe4e7865408004db88ed100855816f03175494ec21ef9dad199b8685b5b194996ebe83ab27803af152adb66a301172fdd622d + checksum: 01745fdb47f87cecf538e69c63f9adc5bfab30a345345c2de91105f3afbd1bfcfba1256af02bf3323077b33b0004469a837e077bf0cbb9c907e9c1e9e7547585 languageName: node linkType: hard "immer@npm:^9.0.7": - version: 9.0.14 - resolution: "immer@npm:9.0.14" - checksum: 17f1365c06d653e672a4f609f08e7203e9ab4b4284818332d6ca9b3f3577a0e3c0066ca7933b636fbae560df79a4b3fde70ed717ce3c6e95c39bf3d5861d5be9 + version: 9.0.15 + resolution: "immer@npm:9.0.15" + checksum: 92e3d63e810e3c3c2bb61b70c45443e37ef983ad12924e3edaf03725ae5979618f5b473439bb3bb4a8c4769f25132f18dec10ea15c40f0b20da5691ff96ff611 languageName: node linkType: hard @@ -6468,10 +6322,10 @@ __metadata: languageName: node linkType: hard -"infima@npm:0.2.0-alpha.39": - version: 0.2.0-alpha.39 - resolution: "infima@npm:0.2.0-alpha.39" - checksum: 707517ba05e5240812c9f80135167fdd6a74e349ba8faae1722da3fe258cc00a7ece0ffefb0347183e7bef33f6d1aa3588650361f5e073cdec93c9b58bcaf331 +"infima@npm:0.2.0-alpha.42": + version: 0.2.0-alpha.42 + resolution: "infima@npm:0.2.0-alpha.42" + checksum: 7206f36639c00a08daab811fedc748068951497efb5ec948cba846fb23856443668015f6bd65ddebe857cc2235f6ca98429f7018c73dcac47b0361ef4721bb8f languageName: node linkType: hard @@ -6624,10 +6478,10 @@ __metadata: languageName: node linkType: hard -"ip@npm:^1.1.5": - version: 1.1.8 - resolution: "ip@npm:1.1.8" - checksum: a2ade53eb339fb0cbe9e69a44caab10d6e3784662285eb5d2677117ee4facc33a64679051c35e0dfdb1a3983a51ce2f5d2cb36446d52e10d01881789b76e28fb +"ip@npm:^2.0.0": + version: 2.0.0 + resolution: "ip@npm:2.0.0" + checksum: cfcfac6b873b701996d71ec82a7dd27ba92450afdb421e356f44044ed688df04567344c36cbacea7d01b1c39a4c732dc012570ebe9bebfb06f27314bca625349 languageName: node linkType: hard @@ -6703,12 +6557,12 @@ __metadata: languageName: node linkType: hard -"is-core-module@npm:^2.8.1": - version: 2.9.0 - resolution: "is-core-module@npm:2.9.0" +"is-core-module@npm:^2.9.0": + version: 2.10.0 + resolution: "is-core-module@npm:2.10.0" dependencies: has: ^1.0.3 - checksum: b27034318b4b462f1c8f1dfb1b32baecd651d891a4e2d1922135daeff4141dfced2b82b07aef83ef54275c4a3526aa38da859223664d0868ca24182badb784ce + checksum: 0f3f77811f430af3256fa7bbc806f9639534b140f8ee69476f632c3e1eb4e28a38be0b9d1b8ecf596179c841b53576129279df95e7051d694dac4ceb6f967593 languageName: node linkType: hard @@ -6742,15 +6596,6 @@ __metadata: languageName: node linkType: hard -"is-fullwidth-code-point@npm:^1.0.0": - version: 1.0.0 - resolution: "is-fullwidth-code-point@npm:1.0.0" - dependencies: - number-is-nan: ^1.0.0 - checksum: 4d46a7465a66a8aebcc5340d3b63a56602133874af576a9ca42c6f0f4bd787a743605771c5f246db77da96605fefeffb65fc1dbe862dcc7328f4b4d03edf5a57 - languageName: node - linkType: hard - "is-fullwidth-code-point@npm:^3.0.0": version: 3.0.0 resolution: "is-fullwidth-code-point@npm:3.0.0" @@ -6943,8 +6788,8 @@ __metadata: linkType: hard "isomorphic-git@npm:^1.17.2": - version: 1.17.2 - resolution: "isomorphic-git@npm:1.17.2" + version: 1.21.0 + resolution: "isomorphic-git@npm:1.21.0" dependencies: async-lock: ^1.1.0 clean-git-ref: ^2.0.1 @@ -6959,11 +6804,11 @@ __metadata: simple-get: ^4.0.1 bin: isogit: cli.cjs - checksum: b23ee00859f5d7057528932bbf52763ec1fb3ef8e07b26874fd39b04fb95144b66d7caf5a3436dc1c18c74b03ea6416be4f2624d202fcf1cd5bc87abf38a4065 + checksum: afbdf98000c586fdcb96f84c054d6ada392b0d2422a7b7baad89e5f5881a79426f8555bdadeebe25202be18bb8eaee3881f8c04b6a289d04b916790bdd5dd5df languageName: node linkType: hard -"jest-worker@npm:^27.0.2, jest-worker@npm:^27.4.5": +"jest-worker@npm:^27.4.5, jest-worker@npm:^27.5.1": version: 27.5.1 resolution: "jest-worker@npm:27.5.1" dependencies: @@ -7132,9 +6977,9 @@ __metadata: linkType: hard "lilconfig@npm:^2.0.3": - version: 2.0.5 - resolution: "lilconfig@npm:2.0.5" - checksum: f7bb9e42656f06930ad04e583026f087508ae408d3526b8b54895e934eb2a966b7aafae569656f2c79a29fe6d779b3ec44ba577e80814734c8655d6f71cdf2d1 + version: 2.0.6 + resolution: "lilconfig@npm:2.0.6" + checksum: 40a3cd72f103b1be5975f2ac1850810b61d4053e20ab09be8d3aeddfe042187e1ba70b4651a7e70f95efa1642e7dc8b2ae395b317b7d7753b241b43cef7c0f7d languageName: node linkType: hard @@ -7198,20 +7043,6 @@ __metadata: languageName: node linkType: hard -"lodash.assignin@npm:^4.0.9": - version: 4.2.0 - resolution: "lodash.assignin@npm:4.2.0" - checksum: 4b55bc1d65ccd7648fdba8a4316d10546929bf0beb5950830d86c559948cf170f0e65b77c95e66b45b511b85a31161714de8b2008d2537627ef3c7759afe36a6 - languageName: node - linkType: hard - -"lodash.bind@npm:^4.1.4": - version: 4.2.1 - resolution: "lodash.bind@npm:4.2.1" - checksum: cf0e41de2fca7704fc0adadc00f7fc871f8cf428990972f072136e4cd153c4d42d88c1418218121380914021c5547be05e4252e61f6280c736a2195cc8b6f4e5 - languageName: node - linkType: hard - "lodash.debounce@npm:^4.0.8": version: 4.0.8 resolution: "lodash.debounce@npm:4.0.8" @@ -7219,34 +7050,6 @@ __metadata: languageName: node linkType: hard -"lodash.defaults@npm:^4.0.1": - version: 4.2.0 - resolution: "lodash.defaults@npm:4.2.0" - checksum: 84923258235592c8886e29de5491946ff8c2ae5c82a7ac5cddd2e3cb697e6fbdfbbb6efcca015795c86eec2bb953a5a2ee4016e3735a3f02720428a40efbb8f1 - languageName: node - linkType: hard - -"lodash.filter@npm:^4.4.0": - version: 4.6.0 - resolution: "lodash.filter@npm:4.6.0" - checksum: f21d245d24818e15b560cb6cadc8404a1bf98bd87d037e5e51858aad57ca2b9db64d87e450a23c8f72dd2c66968efd09b034055ce86d93eef4a4eb6f1bbaf100 - languageName: node - linkType: hard - -"lodash.flatten@npm:^4.2.0": - version: 4.4.0 - resolution: "lodash.flatten@npm:4.4.0" - checksum: 0ac34a393d4b795d4b7421153d27c13ae67e08786c9cbb60ff5b732210d46f833598eee3fb3844bb10070e8488efe390ea53bb567377e0cb47e9e630bf0811cb - languageName: node - linkType: hard - -"lodash.foreach@npm:^4.3.0": - version: 4.5.0 - resolution: "lodash.foreach@npm:4.5.0" - checksum: a940386b158ca0d62994db41fc16529eb8ae67138f29ced38e91f912cb5435d1b0ed34b18e6f7b9ddfc32ab676afc6dfec60d1e22633d8e3e4b33413402ab4ad - languageName: node - linkType: hard - "lodash.isequal@npm:^4.5.0": version: 4.5.0 resolution: "lodash.isequal@npm:4.5.0" @@ -7254,13 +7057,6 @@ __metadata: languageName: node linkType: hard -"lodash.map@npm:^4.4.0": - version: 4.6.0 - resolution: "lodash.map@npm:4.6.0" - checksum: 7369a41d7d24d15ce3bbd02a7faa3a90f6266c38184e64932571b9b21b758bd10c04ffd117d1859be1a44156f29b94df5045eff172bf8a97fddf68bf1002d12f - languageName: node - linkType: hard - "lodash.memoize@npm:^4.1.2": version: 4.1.2 resolution: "lodash.memoize@npm:4.1.2" @@ -7268,48 +7064,6 @@ __metadata: languageName: node linkType: hard -"lodash.merge@npm:^4.4.0": - version: 4.6.2 - resolution: "lodash.merge@npm:4.6.2" - checksum: ad580b4bdbb7ca1f7abf7e1bce63a9a0b98e370cf40194b03380a46b4ed799c9573029599caebc1b14e3f24b111aef72b96674a56cfa105e0f5ac70546cdc005 - languageName: node - linkType: hard - -"lodash.pick@npm:^4.2.1": - version: 4.4.0 - resolution: "lodash.pick@npm:4.4.0" - checksum: 2c36cab7da6b999a20bd3373b40e31a3ef81fa264f34a6979c852c5bc8ac039379686b27380f0cb8e3781610844fafec6949c6fbbebc059c98f8fa8570e3675f - languageName: node - linkType: hard - -"lodash.reduce@npm:^4.4.0": - version: 4.6.0 - resolution: "lodash.reduce@npm:4.6.0" - checksum: 81f2a1045440554f8427f895ef479f1de5c141edd7852dde85a894879312801efae0295116e5cf830c531c1a51cdab8f3628c3ad39fa21a9874bb9158d9ea075 - languageName: node - linkType: hard - -"lodash.reject@npm:^4.4.0": - version: 4.6.0 - resolution: "lodash.reject@npm:4.6.0" - checksum: 730acc78d29ab0a60e0f3cd87bbfe9071625a835791ef66daac7a405c43ec21209fd795fdf9b7485aecead4869f645801bd65c27b9acadce80dee26393793111 - languageName: node - linkType: hard - -"lodash.some@npm:^4.4.0": - version: 4.6.0 - resolution: "lodash.some@npm:4.6.0" - checksum: 4469e76a389446d1166a29f844fb21398c36060d00258ce799710e046c55ed3c1af150c31b4856504e252bc813ba3fdcb6f255c490d9846738dd363a44665322 - languageName: node - linkType: hard - -"lodash.sortby@npm:^4.7.0": - version: 4.7.0 - resolution: "lodash.sortby@npm:4.7.0" - checksum: db170c9396d29d11fe9a9f25668c4993e0c1331bcb941ddbd48fb76f492e732add7f2a47cfdf8e9d740fa59ac41bbfaf931d268bc72aab3ab49e9f89354d718c - languageName: node - linkType: hard - "lodash.uniq@npm:4.5.0, lodash.uniq@npm:^4.5.0": version: 4.5.0 resolution: "lodash.uniq@npm:4.5.0" @@ -7368,9 +7122,9 @@ __metadata: linkType: hard "lru-cache@npm:^7.7.1": - version: 7.10.1 - resolution: "lru-cache@npm:7.10.1" - checksum: e8b190d71ed0fcd7b29c71a3e9b01f851c92d1ef8865ff06b5581ca991db1e5e006920ed4da8b56da1910664ed51abfd76c46fb55e82ac252ff6c970ff910d72 + version: 7.14.0 + resolution: "lru-cache@npm:7.14.0" + checksum: efdd329f2c1bb790b71d497c6c59272e6bc2d7dd060ba55fc136becd3dd31fc8346edb446275504d94cb60d3c8385dbf5267b79b23789e409b2bdf302d13f0d7 languageName: node linkType: hard @@ -7384,8 +7138,8 @@ __metadata: linkType: hard "make-fetch-happen@npm:^10.0.3": - version: 10.1.5 - resolution: "make-fetch-happen@npm:10.1.5" + version: 10.2.1 + resolution: "make-fetch-happen@npm:10.2.1" dependencies: agentkeepalive: ^4.2.1 cacache: ^16.1.0 @@ -7401,9 +7155,9 @@ __metadata: minipass-pipeline: ^1.2.4 negotiator: ^0.6.3 promise-retry: ^2.0.1 - socks-proxy-agent: ^6.1.1 + socks-proxy-agent: ^7.0.0 ssri: ^9.0.0 - checksum: b0b42a1ccdcbc3180749727a52cf6887d9df6218d8ca35101bb9f7ab35729dd166d99203b70149a19a818d1ba72de40b982002ddb0b308c548457f5725d6e7f6 + checksum: 2332eb9a8ec96f1ffeeea56ccefabcb4193693597b132cd110734d50f2928842e22b84cfa1508e921b8385cdfd06dda9ad68645fed62b50fff629a580f5fb72c languageName: node linkType: hard @@ -7484,11 +7238,11 @@ __metadata: linkType: hard "memfs@npm:^3.1.2, memfs@npm:^3.4.3": - version: 3.4.3 - resolution: "memfs@npm:3.4.3" + version: 3.4.7 + resolution: "memfs@npm:3.4.7" dependencies: - fs-monkey: 1.0.3 - checksum: c947ef46e2036524ba120cb42fa502fd75dae8d49d0c53e818d3d3780b9a3a47845705cd1cf51eec04c70f1db590ca7b6c7f78dd5a65883bb253fcedf86f412c + fs-monkey: ^1.0.3 + checksum: fab88266dc576dc4999e38bdf531d703fb798affac2e0dd3fc17470878486844027b2766008ba80c0103b443f52cf9068a5c00f4e1ecf04106f4b29c11855822 languageName: node linkType: hard @@ -7612,14 +7366,14 @@ __metadata: languageName: node linkType: hard -"mini-css-extract-plugin@npm:^2.6.0": - version: 2.6.0 - resolution: "mini-css-extract-plugin@npm:2.6.0" +"mini-css-extract-plugin@npm:^2.6.1": + version: 2.6.1 + resolution: "mini-css-extract-plugin@npm:2.6.1" dependencies: schema-utils: ^4.0.0 peerDependencies: webpack: ^5.0.0 - checksum: ea73bd66558de7a37db094fe68fa130e7a725ab15f880ff8467a75d9c4c2f1576d20720088ea22af9922a94b8600bbfef0c6acaf10d12a32a9bd20a90ba3c35f + checksum: df60840404878c4832b4104799fd29c5a89b06b1e377956c8d4a5729efe0ef301a52e5087d6f383871df5e69a8445922a0ae635c11abf412d7645a7096d0e973 languageName: node linkType: hard @@ -7683,8 +7437,8 @@ __metadata: linkType: hard "minipass-fetch@npm:^2.0.3": - version: 2.1.0 - resolution: "minipass-fetch@npm:2.1.0" + version: 2.1.2 + resolution: "minipass-fetch@npm:2.1.2" dependencies: encoding: ^0.1.13 minipass: ^3.1.6 @@ -7693,7 +7447,7 @@ __metadata: dependenciesMeta: encoding: optional: true - checksum: 1334732859a3f7959ed22589bafd9c40384b885aebb5932328071c33f86b3eb181d54c86919675d1825ab5f1c8e4f328878c863873258d113c29d79a4b0c9c9f + checksum: 3f216be79164e915fc91210cea1850e488793c740534985da017a4cbc7a5ff50506956d0f73bb0cb60e4fe91be08b6b61ef35101706d3ef5da2c8709b5f08f91 languageName: node linkType: hard @@ -7725,11 +7479,11 @@ __metadata: linkType: hard "minipass@npm:^3.0.0, minipass@npm:^3.1.1, minipass@npm:^3.1.6": - version: 3.1.6 - resolution: "minipass@npm:3.1.6" + version: 3.3.5 + resolution: "minipass@npm:3.3.5" dependencies: yallist: ^4.0.0 - checksum: 57a04041413a3531a65062452cb5175f93383ef245d6f4a2961d34386eb9aa8ac11ac7f16f791f5e8bbaf1dfb1ef01596870c88e8822215db57aa591a5bb0a77 + checksum: f89f02bcaa0e0e4bb4c44ec796008e69fbca62db0aba6ead1bc57d25bdaefdf42102130f4f9ecb7d9c6b6cd35ff7b0c7b97d001d3435da8e629fb68af3aea57e languageName: node linkType: hard @@ -7760,9 +7514,9 @@ __metadata: linkType: hard "mrmime@npm:^1.0.0": - version: 1.0.0 - resolution: "mrmime@npm:1.0.0" - checksum: 2c72a40942af7c53bc97d1e9e9c5cb0e6541d18f736811c3a1b46fa2a2b2362480d687daa8ae8372523acaacd82426a4f7ce34b0bf1825ea83b3983e8cb91afd + version: 1.0.1 + resolution: "mrmime@npm:1.0.1" + checksum: cc979da44bbbffebaa8eaf7a45117e851f2d4cb46a3ada6ceb78130466a04c15a0de9a9ce1c8b8ba6f6e1b8618866b1352992bf1757d241c0ddca558b9f28a77 languageName: node linkType: hard @@ -7787,7 +7541,7 @@ __metadata: languageName: node linkType: hard -"multicast-dns@npm:^7.2.4": +"multicast-dns@npm:^7.2.5": version: 7.2.5 resolution: "multicast-dns@npm:7.2.5" dependencies: @@ -7840,11 +7594,11 @@ __metadata: linkType: hard "node-abi@npm:^3.3.0": - version: 3.22.0 - resolution: "node-abi@npm:3.22.0" + version: 3.24.0 + resolution: "node-abi@npm:3.24.0" dependencies: semver: ^7.3.5 - checksum: ad76823920780de39b9712b10c8e5ee424d573b74720b9eeef9ce6523d587f114787aefeabbd34d7a861f9cfab9ac131e1a149243470bb79d6eb5d414a3fa58e + checksum: d90ab48802497b2203800cac71018668e99c246435395ca4f67afcabf689e7e81568ed36e8036bae79a052b63ea5707375bece6ca0a1d2e2b99bfafde7a5c9b2 languageName: node linkType: hard @@ -7874,8 +7628,8 @@ __metadata: linkType: hard "node-gyp@npm:latest": - version: 9.0.0 - resolution: "node-gyp@npm:9.0.0" + version: 9.1.0 + resolution: "node-gyp@npm:9.1.0" dependencies: env-paths: ^2.2.0 glob: ^7.1.4 @@ -7889,14 +7643,14 @@ __metadata: which: ^2.0.2 bin: node-gyp: bin/node-gyp.js - checksum: 4d8ef8860f7e4f4d86c91db3f519d26ed5cc23b48fe54543e2afd86162b4acbd14f21de42a5db344525efb69a991e021b96a68c70c6e2d5f4a5cb770793da6d3 + checksum: 1437fa4a879b5b9010604128e8da8609b57c66034262087539ee04a8b764b8436af2be01bab66f8fc729a3adba2dcc21b10a32b9f552696c3fa8cd657d134fc4 languageName: node linkType: hard -"node-releases@npm:^2.0.3": - version: 2.0.5 - resolution: "node-releases@npm:2.0.5" - checksum: e85d949addd19f8827f32569d2be5751e7812ccf6cc47879d49f79b5234ff4982225e39a3929315f96370823b070640fb04d79fc0ddec8b515a969a03493a42f +"node-releases@npm:^2.0.6": + version: 2.0.6 + resolution: "node-releases@npm:2.0.6" + checksum: e86a926dc9fbb3b41b4c4a89d998afdf140e20a4e8dbe6c0a807f7b2948b42ea97d7fd3ad4868041487b6e9ee98409829c6e4d84a734a4215dff060a7fbeb4bf languageName: node linkType: hard @@ -7948,18 +7702,6 @@ __metadata: languageName: node linkType: hard -"npmlog@npm:^4.0.1": - version: 4.1.2 - resolution: "npmlog@npm:4.1.2" - dependencies: - are-we-there-yet: ~1.1.2 - console-control-strings: ~1.1.0 - gauge: ~2.7.3 - set-blocking: ~2.0.0 - checksum: edbda9f95ec20957a892de1839afc6fb735054c3accf6fbefe767bac9a639fd5cea2baeac6bd2bcd50a85cb54924d57d9886c81c7fbc2332c2ddd19227504192 - languageName: node - linkType: hard - "npmlog@npm:^6.0.0": version: 6.0.2 resolution: "npmlog@npm:6.0.2" @@ -7988,23 +7730,7 @@ __metadata: languageName: node linkType: hard -"nth-check@npm:~1.0.1": - version: 1.0.2 - resolution: "nth-check@npm:1.0.2" - dependencies: - boolbase: ~1.0.0 - checksum: 59e115fdd75b971d0030f42ada3aac23898d4c03aa13371fa8b3339d23461d1badf3fde5aad251fb956aaa75c0a3b9bfcd07c08a34a83b4f9dadfdce1d19337c - languageName: node - linkType: hard - -"number-is-nan@npm:^1.0.0": - version: 1.0.1 - resolution: "number-is-nan@npm:1.0.1" - checksum: 13656bc9aa771b96cef209ffca31c31a03b507ca6862ba7c3f638a283560620d723d52e626d57892c7fff475f4c36ac07f0600f14544692ff595abff214b9ffb - languageName: node - linkType: hard - -"object-assign@npm:^4.1.0, object-assign@npm:^4.1.1": +"object-assign@npm:^4.1.1": version: 4.1.1 resolution: "object-assign@npm:4.1.1" checksum: fcc6e4ea8c7fe48abfbb552578b1c53e0d194086e2e6bbbf59e0a536381a292f39943c6e9628af05b5528aa5e3318bb30d6b2e53cadaf5b8fe9e12c4b69af23f @@ -8026,14 +7752,14 @@ __metadata: linkType: hard "object.assign@npm:^4.1.0": - version: 4.1.2 - resolution: "object.assign@npm:4.1.2" + version: 4.1.4 + resolution: "object.assign@npm:4.1.4" dependencies: - call-bind: ^1.0.0 - define-properties: ^1.1.3 - has-symbols: ^1.0.1 + call-bind: ^1.0.2 + define-properties: ^1.1.4 + has-symbols: ^1.0.3 object-keys: ^1.1.1 - checksum: d621d832ed7b16ac74027adb87196804a500d80d9aca536fccb7ba48d33a7e9306a75f94c1d29cbfa324bc091bfc530bc24789568efdaee6a47fcfa298993814 + checksum: 76cab513a5999acbfe0ff355f15a6a125e71805fcf53de4e9d4e082e1989bdb81d1e329291e1e4e0ae7719f0e4ef80e88fb2d367ae60500d79d25a6224ac8864 languageName: node linkType: hard @@ -8257,13 +7983,6 @@ __metadata: languageName: node linkType: hard -"parse5@npm:^5.0.0": - version: 5.1.1 - resolution: "parse5@npm:5.1.1" - checksum: 613a714af4c1101d1cb9f7cece2558e35b9ae8a0c03518223a4a1e35494624d9a9ad5fad4c13eab66a0e0adccd9aa3d522fc8f5f9cc19789e0579f3fa0bdfc65 - languageName: node - linkType: hard - "parse5@npm:^6.0.0": version: 6.0.1 resolution: "parse5@npm:6.0.1" @@ -8272,11 +7991,11 @@ __metadata: linkType: hard "parse5@npm:^7.0.0": - version: 7.0.0 - resolution: "parse5@npm:7.0.0" + version: 7.1.1 + resolution: "parse5@npm:7.1.1" dependencies: - entities: ^4.3.0 - checksum: 7da5d61cc18eb36ffa71fc861e65cbfd1f23d96483a6631254e627be667dbc9c93ac0b0e6cb17a13a2e4033dab19bfb2f76f38e5936cfb57240ed49036a83fcc + entities: ^4.4.0 + checksum: 8f72fbfa6df83a3f29f58e1818f7bd46b47ff3e26d79c74e10b8fc7ef7ee76163f205113f1b2f6a5b8dc4e31e726f490444f04890cead6e974dbcbe8172b1321 languageName: node linkType: hard @@ -8450,24 +8169,24 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard -"postcss-convert-values@npm:^5.1.1": - version: 5.1.1 - resolution: "postcss-convert-values@npm:5.1.1" +"postcss-convert-values@npm:^5.1.2": + version: 5.1.2 + resolution: "postcss-convert-values@npm:5.1.2" dependencies: browserslist: ^4.20.3 postcss-value-parser: ^4.2.0 peerDependencies: postcss: ^8.2.15 - checksum: 5f582b2159a27faf8667fcba27bc0bbe953d89ceba33579a7b9cc6363555bf05e6aaad9708a2496a335d82e67e5164bdb69f9a58cdc7e3f68abd2e7a3178e5f8 + checksum: b1615daf12d3425bf4edee9451de402702f41019ccfc85f7883d87438becf533b3061a5a3567865029c534147a6c90e89b4c42ae6741c768c879a68d35aea812 languageName: node linkType: hard -"postcss-discard-comments@npm:^5.1.1": - version: 5.1.1 - resolution: "postcss-discard-comments@npm:5.1.1" +"postcss-discard-comments@npm:^5.1.2": + version: 5.1.2 + resolution: "postcss-discard-comments@npm:5.1.2" peerDependencies: postcss: ^8.2.15 - checksum: 578c3cb3e8c6194cf8b5f2170abd6636bf2fe1ec9ba6e03431f5a7e4aae22c6c6605a5e8e2731e824df07c1188f3defa2f4ba28da4adafe45c068af1189f1f2c + checksum: abfd064ebc27aeaf5037643dd51ffaff74d1fa4db56b0523d073ace4248cbb64ffd9787bd6924b0983a9d0bd0e9bf9f10d73b120e50391dc236e0d26c812fa2a languageName: node linkType: hard @@ -8509,17 +8228,17 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard -"postcss-loader@npm:^6.2.1": - version: 6.2.1 - resolution: "postcss-loader@npm:6.2.1" +"postcss-loader@npm:^7.0.0": + version: 7.0.1 + resolution: "postcss-loader@npm:7.0.1" dependencies: cosmiconfig: ^7.0.0 klona: ^2.0.5 - semver: ^7.3.5 + semver: ^7.3.7 peerDependencies: postcss: ^7.0.0 || ^8.0.1 webpack: ^5.0.0 - checksum: e40ae79c3e39df37014677a817b001bd115d8b10dedf53a07b97513d93b1533cd702d7a48831bdd77b9a9484b1ec84a5d4a723f80e83fb28682c75b5e65e8a90 + checksum: 2a3cbcaaade598d4919824d384ae34ffbfc14a9c8db6cc3b154582356f4f44a1c9af9e731b81cf1947b089accf7d0ab7a0c51c717946985f89aa1708d2b4304d languageName: node linkType: hard @@ -8535,21 +8254,21 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard -"postcss-merge-longhand@npm:^5.1.5": - version: 5.1.5 - resolution: "postcss-merge-longhand@npm:5.1.5" +"postcss-merge-longhand@npm:^5.1.6": + version: 5.1.6 + resolution: "postcss-merge-longhand@npm:5.1.6" dependencies: postcss-value-parser: ^4.2.0 stylehacks: ^5.1.0 peerDependencies: postcss: ^8.2.15 - checksum: 1d51e3d6a10f799473e46009ea69ce53c4a82eb2861fd58e6eab17bde3022a3350f440a1d2237a4fa65dde49fa5173f2ffae7781cf4f4e9c5cd15d0d88c7c3d7 + checksum: 327b5474d9e84b8d8aed3e24444938cbf1274326d357b551b700203f03f7bcb615381b92b933770ffe35b154677205af08875373413f2c5e625c34730599707b languageName: node linkType: hard -"postcss-merge-rules@npm:^5.1.1": - version: 5.1.1 - resolution: "postcss-merge-rules@npm:5.1.1" +"postcss-merge-rules@npm:^5.1.2": + version: 5.1.2 + resolution: "postcss-merge-rules@npm:5.1.2" dependencies: browserslist: ^4.16.6 caniuse-api: ^3.0.0 @@ -8557,7 +8276,7 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: postcss-selector-parser: ^6.0.5 peerDependencies: postcss: ^8.2.15 - checksum: 163cba5b688346b6bd142b677439257ada6f910dd32dbcffa2a7a58719a609bb9859f597da382bbd8be14a259bea26248aefd99aa890e8fd3753424dfedbde6e + checksum: fcbc415999a35248dcce03064a5456123663507b05ff0f1de5c97b6effc68014ab0ffd5f06e71cf08d401f037932e271b7db33124c73260f3630a1441212a0c8 languageName: node linkType: hard @@ -8598,14 +8317,14 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard -"postcss-minify-selectors@npm:^5.2.0": - version: 5.2.0 - resolution: "postcss-minify-selectors@npm:5.2.0" +"postcss-minify-selectors@npm:^5.2.1": + version: 5.2.1 + resolution: "postcss-minify-selectors@npm:5.2.1" dependencies: postcss-selector-parser: ^6.0.5 peerDependencies: postcss: ^8.2.15 - checksum: 651fbac038aaba10efaf4ed793d008439042954e5025462c14964ce24ca4bde868bb25ee846a4ff41df8a719fb8ee9f159ef0a036f54e6a09ed937bcc6884cf0 + checksum: 6fdbc84f99a60d56b43df8930707da397775e4c36062a106aea2fd2ac81b5e24e584a1892f4baa4469fa495cb87d1422560eaa8f6c9d500f9f0b691a5f95bab5 languageName: node linkType: hard @@ -8673,25 +8392,25 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard -"postcss-normalize-positions@npm:^5.1.0": - version: 5.1.0 - resolution: "postcss-normalize-positions@npm:5.1.0" +"postcss-normalize-positions@npm:^5.1.1": + version: 5.1.1 + resolution: "postcss-normalize-positions@npm:5.1.1" dependencies: postcss-value-parser: ^4.2.0 peerDependencies: postcss: ^8.2.15 - checksum: 08a1f12cc8e192120c1ee14dc93e603546be507d826a75c2c6ef3224b5e3a17628a42a952317e8349b2708ffdef0560b84dcc20521104317eaa62291cca009f6 + checksum: d9afc233729c496463c7b1cdd06732469f401deb387484c3a2422125b46ec10b4af794c101f8c023af56f01970b72b535e88373b9058ecccbbf88db81662b3c4 languageName: node linkType: hard -"postcss-normalize-repeat-style@npm:^5.1.0": - version: 5.1.0 - resolution: "postcss-normalize-repeat-style@npm:5.1.0" +"postcss-normalize-repeat-style@npm:^5.1.1": + version: 5.1.1 + resolution: "postcss-normalize-repeat-style@npm:5.1.1" dependencies: postcss-value-parser: ^4.2.0 peerDependencies: postcss: ^8.2.15 - checksum: 65176c37741d3321fd346586bf42c292a9dab1e4b77a1004d9cde2ae9e015f6fe330c57b44d0ca854a48bd7db0b169875b8d2b5ca501ac9b5381068c337aac19 + checksum: 2c6ad2b0ae10a1fda156b948c34f78c8f1e185513593de4d7e2480973586675520edfec427645fa168c337b0a6b3ceca26f92b96149741ca98a9806dad30d534 languageName: node linkType: hard @@ -8752,15 +8471,15 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard -"postcss-ordered-values@npm:^5.1.1": - version: 5.1.1 - resolution: "postcss-ordered-values@npm:5.1.1" +"postcss-ordered-values@npm:^5.1.3": + version: 5.1.3 + resolution: "postcss-ordered-values@npm:5.1.3" dependencies: cssnano-utils: ^3.1.0 postcss-value-parser: ^4.2.0 peerDependencies: postcss: ^8.2.15 - checksum: d56825ef03225ccaeff9704b86008d012b91b0ace4b219f6d443aca628b7f0a1da922abc4a3fb8ca802baf09320abaa983f278ac416e1caf2658d119491686a4 + checksum: 6f3ca85b6ceffc68aadaf319d9ee4c5ac16d93195bf8cba2d1559b631555ad61941461cda6d3909faab86e52389846b2b36345cff8f0c3f4eb345b1b8efadcf9 languageName: node linkType: hard @@ -8858,20 +8577,20 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard -"postcss@npm:^8.3.11, postcss@npm:^8.3.5, postcss@npm:^8.4.13, postcss@npm:^8.4.7": - version: 8.4.14 - resolution: "postcss@npm:8.4.14" +"postcss@npm:^8.3.11, postcss@npm:^8.4.13, postcss@npm:^8.4.14, postcss@npm:^8.4.7": + version: 8.4.16 + resolution: "postcss@npm:8.4.16" dependencies: nanoid: ^3.3.4 picocolors: ^1.0.0 source-map-js: ^1.0.2 - checksum: fe58766ff32e4becf65a7d57678995cfd239df6deed2fe0557f038b47c94e4132e7e5f68b5aa820c13adfec32e523b693efaeb65798efb995ce49ccd83953816 + checksum: 10eee25efd77868036403858577da0cefaf2e0905feeaba5770d5438ccdddba3d01cba8063e96b8aac4c6daa0ed413dd5ae0554a433a3c4db38df1d134cffc1f languageName: node linkType: hard -"prebuild-install@npm:^7.1.0": - version: 7.1.0 - resolution: "prebuild-install@npm:7.1.0" +"prebuild-install@npm:^7.1.1": + version: 7.1.1 + resolution: "prebuild-install@npm:7.1.1" dependencies: detect-libc: ^2.0.0 expand-template: ^2.0.3 @@ -8880,7 +8599,6 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: mkdirp-classic: ^0.5.3 napi-build-utils: ^1.0.1 node-abi: ^3.3.0 - npmlog: ^4.0.1 pump: ^3.0.0 rc: ^1.2.7 simple-get: ^4.0.0 @@ -8888,7 +8606,7 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: tunnel-agent: ^0.6.0 bin: prebuild-install: bin.js - checksum: 204f2d89c6d6179fa1039036514aa72f7d0b537e421ef72c40840286e318f41489f00f22c6acc725cce6e10d43825b69dcabeaadfc917db781c58cd56fc25f90 + checksum: dbf96d0146b6b5827fc8f67f72074d2e19c69628b9a7a0a17d0fad1bf37e9f06922896972e074197fc00a52eae912993e6ef5a0d471652f561df5cb516f3f467 languageName: node linkType: hard @@ -8925,19 +8643,19 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard -"prism-react-renderer@npm:^1.3.1": - version: 1.3.3 - resolution: "prism-react-renderer@npm:1.3.3" +"prism-react-renderer@npm:^1.3.5": + version: 1.3.5 + resolution: "prism-react-renderer@npm:1.3.5" peerDependencies: react: ">=0.14.9" - checksum: e5df45271fc1db512b71feab04000c329d83987bfec98d5bcb3ca818ee7c031e56b5ce51f0f67daa1bd31466b11b1f10b6000dc90f6b6553f8ed5234d19b7ac6 + checksum: c18806dcbc4c0b4fd6fd15bd06b4f7c0a6da98d93af235c3e970854994eb9b59e23315abb6cfc29e69da26d36709a47e25da85ab27fed81b6812f0a52caf6dfa languageName: node linkType: hard "prismjs@npm:^1.28.0": - version: 1.28.0 - resolution: "prismjs@npm:1.28.0" - checksum: bde93fb2beb45b7243219fc53855f59ee54b3fa179f315e8f9d66244d756ef984462e10561bbdc6713d3d7e051852472d7c284f5794a8791eeaefea2fb910b16 + version: 1.29.0 + resolution: "prismjs@npm:1.29.0" + checksum: 007a8869d4456ff8049dc59404e32d5666a07d99c3b0e30a18bd3b7676dfa07d1daae9d0f407f20983865fd8da56de91d09cb08e6aa61f5bc420a27c0beeaf93 languageName: node linkType: hard @@ -9110,7 +8828,7 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard -"rc@npm:^1.2.7, rc@npm:^1.2.8": +"rc@npm:1.2.8, rc@npm:^1.2.7, rc@npm:^1.2.8": version: 1.2.8 resolution: "rc@npm:1.2.8" dependencies: @@ -9125,12 +8843,12 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: linkType: hard "react-collapsible@npm:^2.8.4": - version: 2.9.0 - resolution: "react-collapsible@npm:2.9.0" + version: 2.10.0 + resolution: "react-collapsible@npm:2.10.0" peerDependencies: - react: ~15 || ~16 || ~17 - react-dom: ~15 || ~16 || ~17 - checksum: fc133e38169135a633ef6faa61fa3a5d1ac6c1b55fbe24a368091b3b838acc015055cfacc97b555c8ce8b517a84fbf9087e0384d7cc791109fc62ce44b29ff91 + react: ~15 || ~16 || ~17 || ~18 + react-dom: ~15 || ~16 || ~17 || ~18 + checksum: f7a883eb7c4aa91916378dfd4c98dc92a32bbdb4d6b8fad179e34ad1f2bbbab35446f5f6e126d699427bc67d1d70909c69f6be4ee744251bea4e17c7d10be2f3 languageName: node linkType: hard @@ -9167,12 +8885,12 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: linkType: hard "react-devtools-core@npm:^4.19.1": - version: 4.24.6 - resolution: "react-devtools-core@npm:4.24.6" + version: 4.25.0 + resolution: "react-devtools-core@npm:4.25.0" dependencies: shell-quote: ^1.6.1 ws: ^7 - checksum: 1c4ac2186de57c75755cb3ee2db884e49df9fb6bb4b4a718379f3d4ed5be4f17bc7a9a4175f0e249f5f91520b59703505f5e3fb155cf4d428c89b96255091e4d + checksum: 68dae4507c46b875936904f2aba0fe1dda1d7b6fb616ac72e438aec29d7c49aeadaaff264cc148d40795075fdb9c736d1dcf5c50dbe40eaae64c7aa24f313e68 languageName: node linkType: hard @@ -9226,10 +8944,10 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard -"react-is@npm:^17.0.1": - version: 17.0.2 - resolution: "react-is@npm:17.0.2" - checksum: 9d6d111d8990dc98bc5402c1266a808b0459b5d54830bbea24c12d908b536df7883f268a7868cfaedde3dd9d4e0d574db456f84d2e6df9c4526f99bb4b5344d8 +"react-is@npm:^17.0.1 || ^18.0.0": + version: 18.2.0 + resolution: "react-is@npm:18.2.0" + checksum: e72d0ba81b5922759e4aff17e0252bd29988f9642ed817f56b25a3e217e13eea8a7f2322af99a06edb779da12d5d636e9fda473d620df9a3da0df2a74141d53e languageName: node linkType: hard @@ -9284,7 +9002,7 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard -"react-router-dom@npm:^5.2.0": +"react-router-dom@npm:^5.3.3": version: 5.3.3 resolution: "react-router-dom@npm:5.3.3" dependencies: @@ -9301,7 +9019,7 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard -"react-router@npm:5.3.3, react-router@npm:^5.2.0": +"react-router@npm:5.3.3, react-router@npm:^5.3.3": version: 5.3.3 resolution: "react-router@npm:5.3.3" dependencies: @@ -9322,8 +9040,8 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: linkType: hard "react-select@npm:^5.3.0": - version: 5.3.2 - resolution: "react-select@npm:5.3.2" + version: 5.4.0 + resolution: "react-select@npm:5.4.0" dependencies: "@babel/runtime": ^7.12.0 "@emotion/cache": ^11.4.0 @@ -9335,13 +9053,13 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: c8c0ecd75704098ef1010fba4df68bcc5e7d76ffcd6d3bac40bc3017758a9c035a887fa82ead9105dc719c14e04c4164d8b7a8e8fa61d0ed39e4bdf48ee3bf1c + checksum: da896f0f8b3c7f6250148ce9ded1ace60abb35b812be50d34cf7ba980b12ca75533c3bbfd9e722530cc0e70c17e8997c3c7e93743877d84c00b0d90e4da521e6 languageName: node linkType: hard "react-transition-group@npm:^4.3.0": - version: 4.4.2 - resolution: "react-transition-group@npm:4.4.2" + version: 4.4.5 + resolution: "react-transition-group@npm:4.4.5" dependencies: "@babel/runtime": ^7.5.5 dom-helpers: ^5.0.1 @@ -9350,21 +9068,21 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: peerDependencies: react: ">=16.6.0" react-dom: ">=16.6.0" - checksum: b67bf5b3e86dbab72d658b9a52a3589e5960583ab28c7c66272427d8fe30d4c7de422d5046ae96bd2683cdf80cc3264b2516f5ce80cae1dbe6cf3ca6dda392c5 + checksum: 75602840106aa9c6545149d6d7ae1502fb7b7abadcce70a6954c4b64a438ff1cd16fc77a0a1e5197cdd72da398f39eb929ea06f9005c45b132ed34e056ebdeb1 languageName: node linkType: hard -"react-waypoint@npm:^10.1.0": - version: 10.1.0 - resolution: "react-waypoint@npm:10.1.0" +"react-waypoint@npm:^10.3.0": + version: 10.3.0 + resolution: "react-waypoint@npm:10.3.0" dependencies: "@babel/runtime": ^7.12.5 consolidated-events: ^1.1.0 || ^2.0.0 prop-types: ^15.0.0 - react-is: ^17.0.1 + react-is: ^17.0.1 || ^18.0.0 peerDependencies: - react: ^15.3.0 || ^16.0.0 || ^17.0.0 - checksum: 70d7e753eba909a2cd3519cee33e25725a89ad883c94fbc8cf7e5705c94c47d909f5ebcb58a7e0018d8c9ecaaf39c831524bf9e15100a03863173c752cf54db1 + react: ^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + checksum: 5a476432cd4a55ae022b33f82610a1addae92912ec88111cf33c17ef473bbbfc2c695714cb3bd60911259c92c5b6349f80033b022bf1e59e1a4be9b198a70a7a languageName: node linkType: hard @@ -9378,7 +9096,7 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard -"readable-stream@npm:^2.0.1, readable-stream@npm:^2.0.6": +"readable-stream@npm:^2.0.1": version: 2.3.7 resolution: "readable-stream@npm:2.3.7" dependencies: @@ -9470,9 +9188,9 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard -"regexpu-core@npm:^5.0.1": - version: 5.0.1 - resolution: "regexpu-core@npm:5.0.1" +"regexpu-core@npm:^5.1.0": + version: 5.1.0 + resolution: "regexpu-core@npm:5.1.0" dependencies: regenerate: ^1.4.2 regenerate-unicode-properties: ^10.0.1 @@ -9480,16 +9198,16 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: regjsparser: ^0.8.2 unicode-match-property-ecmascript: ^2.0.0 unicode-match-property-value-ecmascript: ^2.0.0 - checksum: 6151a9700dad512fadb5564ad23246d54c880eb9417efa5e5c3658b910c1ff894d622dfd159af2ed527ffd44751bfe98682ae06c717155c254d8e2b4bab62785 + checksum: 7b4eb8d182d9d10537a220a93138df5bc7eaf4ed53e36b95e8427d33ed8a2b081468f1a15d3e5fcee66517e1df7f5ca180b999e046d060badd97150f2ffe87b2 languageName: node linkType: hard "registry-auth-token@npm:^4.0.0": - version: 4.2.1 - resolution: "registry-auth-token@npm:4.2.1" + version: 4.2.2 + resolution: "registry-auth-token@npm:4.2.2" dependencies: - rc: ^1.2.8 - checksum: aa72060b573a50607cfd2dee16d0e51e13ca58b6a80442e74545325dc24d2c38896e6bad229bdcc1fc9759fa81b4066be8693d4d6f45927318e7c793a93e9cd0 + rc: 1.2.8 + checksum: c5030198546ecfdcbcb0722cbc3e260c4f5f174d8d07bdfedd4620e79bfdf17a2db735aa230d600bd388fce6edd26c0a9ed2eb7e9b4641ec15213a28a806688b languageName: node linkType: hard @@ -9520,17 +9238,6 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard -"rehype-parse@npm:^6.0.2": - version: 6.0.2 - resolution: "rehype-parse@npm:6.0.2" - dependencies: - hast-util-from-parse5: ^5.0.0 - parse5: ^5.0.0 - xtend: ^4.0.0 - checksum: f9afca7a8038a402d45d2f6eab31b2ce09100c195007c0bf9340b32e31585c6898f1cf0f4e088c08c5e2adade0fbb59e490ec6291e16751b12bd24d7c1e48ba9 - languageName: node - linkType: hard - "relateurl@npm:^0.2.7": version: 0.2.7 resolution: "relateurl@npm:0.2.7" @@ -9538,17 +9245,6 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard -"remark-admonitions@npm:^1.2.1": - version: 1.2.1 - resolution: "remark-admonitions@npm:1.2.1" - dependencies: - rehype-parse: ^6.0.2 - unified: ^8.4.2 - unist-util-visit: ^2.0.1 - checksum: c80fbc08b57c0054d7b414c8a0a205dee24d53ca9344a055acc3e1d0770d4045ffd7bec244d2316cf4c0cc27cf1a52be29332e7d9595000dbf3276a0b2f04b86 - languageName: node - linkType: hard - "remark-code-import@npm:^0.3.0": version: 0.3.0 resolution: "remark-code-import@npm:0.3.0" @@ -9700,29 +9396,29 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard -"resolve@npm:^1.1.6, resolve@npm:^1.12.0, resolve@npm:^1.14.2, resolve@npm:^1.3.2": - version: 1.22.0 - resolution: "resolve@npm:1.22.0" +"resolve@npm:^1.1.6, resolve@npm:^1.14.2, resolve@npm:^1.19.0, resolve@npm:^1.3.2": + version: 1.22.1 + resolution: "resolve@npm:1.22.1" dependencies: - is-core-module: ^2.8.1 + is-core-module: ^2.9.0 path-parse: ^1.0.7 supports-preserve-symlinks-flag: ^1.0.0 bin: resolve: bin/resolve - checksum: a2d14cc437b3a23996f8c7367eee5c7cf8149c586b07ca2ae00e96581ce59455555a1190be9aa92154785cf9f2042646c200d0e00e0bbd2b8a995a93a0ed3e4e + checksum: 07af5fc1e81aa1d866cbc9e9460fbb67318a10fa3c4deadc35c3ad8a898ee9a71a86a65e4755ac3195e0ea0cfbe201eb323ebe655ce90526fd61917313a34e4e languageName: node linkType: hard -"resolve@patch:resolve@^1.1.6#~builtin, resolve@patch:resolve@^1.12.0#~builtin, resolve@patch:resolve@^1.14.2#~builtin, resolve@patch:resolve@^1.3.2#~builtin": - version: 1.22.0 - resolution: "resolve@patch:resolve@npm%3A1.22.0#~builtin::version=1.22.0&hash=07638b" +"resolve@patch:resolve@^1.1.6#~builtin, resolve@patch:resolve@^1.14.2#~builtin, resolve@patch:resolve@^1.19.0#~builtin, resolve@patch:resolve@^1.3.2#~builtin": + version: 1.22.1 + resolution: "resolve@patch:resolve@npm%3A1.22.1#~builtin::version=1.22.1&hash=07638b" dependencies: - is-core-module: ^2.8.1 + is-core-module: ^2.9.0 path-parse: ^1.0.7 supports-preserve-symlinks-flag: ^1.0.0 bin: resolve: bin/resolve - checksum: c79ecaea36c872ee4a79e3db0d3d4160b593f2ca16e031d8283735acd01715a203607e9ded3f91f68899c2937fa0d49390cddbe0fb2852629212f3cda283f4a7 + checksum: 5656f4d0bedcf8eb52685c1abdf8fbe73a1603bb1160a24d716e27a57f6cecbe2432ff9c89c2bd57542c3a7b9d14b1882b73bfe2e9d7849c9a4c0b8b39f02b8b languageName: node linkType: hard @@ -9808,11 +9504,11 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: linkType: hard "rxjs@npm:^7.5.4": - version: 7.5.5 - resolution: "rxjs@npm:7.5.5" + version: 7.5.6 + resolution: "rxjs@npm:7.5.6" dependencies: tslib: ^2.1.0 - checksum: e034f60805210cce756dd2f49664a8108780b117cf5d0e2281506e9e6387f7b4f1532d974a8c8b09314fa7a16dd2f6cff3462072a5789672b5dcb45c4173f3c6 + checksum: fc05f01364a74dac57490fb3e07ea63b422af04017fae1db641a009073f902ef69f285c5daac31359620dc8d9aee7d81e42b370ca2a8573d1feae0b04329383b languageName: node linkType: hard @@ -10045,7 +9741,7 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard -"set-blocking@npm:^2.0.0, set-blocking@npm:~2.0.0": +"set-blocking@npm:^2.0.0": version: 2.0.0 resolution: "set-blocking@npm:2.0.0" checksum: 6e65a05f7cf7ebdf8b7c75b101e18c0b7e3dff4940d480efed8aad3a36a4005140b660fa1d804cb8bce911cac290441dc728084a30504d3516ac2ff7ad607b02 @@ -10094,20 +9790,20 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard -"sharp@npm:^0.30.4": - version: 0.30.5 - resolution: "sharp@npm:0.30.5" +"sharp@npm:^0.30.7": + version: 0.30.7 + resolution: "sharp@npm:0.30.7" dependencies: color: ^4.2.3 detect-libc: ^2.0.1 node-addon-api: ^5.0.0 node-gyp: latest - prebuild-install: ^7.1.0 + prebuild-install: ^7.1.1 semver: ^7.3.7 simple-get: ^4.0.1 tar-fs: ^2.1.1 tunnel-agent: ^0.6.0 - checksum: e2b6238770ae1498815c14b2711de9398176a8e1ea1131ef583edc4a1c421f55fd9fe52ffc996ea4a259bdfed2f5d68a076a6ddc85499c229025efbe8ccf0b56 + checksum: bbc63ca3c7ea8a5bff32cd77022cfea30e25a03f5bd031e935924bf6cf0e11e3388e8b0e22b3137bf8816aa73407f1e4fbeb190f3a35605c27ffca9f32b91601 languageName: node linkType: hard @@ -10158,7 +9854,7 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard -"signal-exit@npm:^3.0.0, signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.3, signal-exit@npm:^3.0.7": +"signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.3, signal-exit@npm:^3.0.7": version: 3.0.7 resolution: "signal-exit@npm:3.0.7" checksum: a2f098f247adc367dffc27845853e9959b9e88b01cb301658cfe4194352d8d2bb32e18467c786a7fe15f1d44b233ea35633d076d5e737870b7139949d1ab6318 @@ -10242,7 +9938,7 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard -"sockjs@npm:^0.3.21": +"sockjs@npm:^0.3.24": version: 0.3.24 resolution: "sockjs@npm:0.3.24" dependencies: @@ -10253,24 +9949,24 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard -"socks-proxy-agent@npm:^6.1.1": - version: 6.2.0 - resolution: "socks-proxy-agent@npm:6.2.0" +"socks-proxy-agent@npm:^7.0.0": + version: 7.0.0 + resolution: "socks-proxy-agent@npm:7.0.0" dependencies: agent-base: ^6.0.2 debug: ^4.3.3 socks: ^2.6.2 - checksum: 6723fd64fb50334e2b340fd0a80fd8488ffc5bc43d85b7cf1d25612044f814dd7d6ea417fd47602159941236f7f4bd15669fa5d7e1f852598a31288e1a43967b + checksum: 720554370154cbc979e2e9ce6a6ec6ced205d02757d8f5d93fe95adae454fc187a5cbfc6b022afab850a5ce9b4c7d73e0f98e381879cf45f66317a4895953846 languageName: node linkType: hard "socks@npm:^2.6.2": - version: 2.6.2 - resolution: "socks@npm:2.6.2" + version: 2.7.0 + resolution: "socks@npm:2.7.0" dependencies: - ip: ^1.1.5 + ip: ^2.0.0 smart-buffer: ^4.2.0 - checksum: dd9194293059d737759d5c69273850ad4149f448426249325c4bea0e340d1cf3d266c3b022694b0dcf5d31f759de23657244c481fc1e8322add80b7985c36b5e + checksum: 0b5d94e2b3c11e7937b40fc5dac1e80d8b92a330e68c51f1d271ce6980c70adca42a3f8cd47c4a5769956bada074823b53374f2dc5f2ea5c2121b222dec6eadf languageName: node linkType: hard @@ -10281,13 +9977,6 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard -"source-list-map@npm:^2.0.0": - version: 2.0.1 - resolution: "source-list-map@npm:2.0.1" - checksum: 806efc6f75e7cd31e4815e7a3aaf75a45c704871ea4075cb2eb49882c6fca28998f44fc5ac91adb6de03b2882ee6fb02f951fdc85e6a22b338c32bfe19557938 - languageName: node - linkType: hard - "source-map-js@npm:^1.0.2": version: 1.0.2 resolution: "source-map-js@npm:1.0.2" @@ -10312,22 +10001,13 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard -"source-map@npm:^0.6.0, source-map@npm:^0.6.1, source-map@npm:~0.6.0, source-map@npm:~0.6.1": +"source-map@npm:^0.6.0, source-map@npm:^0.6.1, source-map@npm:~0.6.0": version: 0.6.1 resolution: "source-map@npm:0.6.1" checksum: 59ce8640cf3f3124f64ac289012c2b8bd377c238e316fb323ea22fbfe83da07d81e000071d7242cad7a23cd91c7de98e4df8830ec3f133cb6133a5f6e9f67bc2 languageName: node linkType: hard -"source-map@npm:~0.8.0-beta.0": - version: 0.8.0-beta.0 - resolution: "source-map@npm:0.8.0-beta.0" - dependencies: - whatwg-url: ^7.0.0 - checksum: e94169be6461ab0ac0913313ad1719a14c60d402bd22b0ad96f4a6cffd79130d91ab5df0a5336a326b04d2df131c1409f563c9dc0d21a6ca6239a44b6c8dbd92 - languageName: node - linkType: hard - "space-separated-tokens@npm:^1.0.0": version: 1.1.5 resolution: "space-separated-tokens@npm:1.1.5" @@ -10416,9 +10096,9 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: linkType: hard "std-env@npm:^3.0.1": - version: 3.1.1 - resolution: "std-env@npm:3.1.1" - checksum: 7c8b34e9b81ec0da3c63c96414dafd5c4ef6cfb5914f2cf8727314f9f3532eb0325cfbbfe5a04a43c2aa6ab5960571581a89db2c4f26e73a5c954eb1fe5aa68b + version: 3.2.1 + resolution: "std-env@npm:3.2.1" + checksum: c046af2573ba6e02c9ffcfd5a2c3f94f160404915941be177b93d6d1695078e487293fed6ab4fa95680e75a331eccf0f6dbe0b86babda15056c594e600a99dc4 languageName: node linkType: hard @@ -10429,17 +10109,6 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard -"string-width@npm:^1.0.1": - version: 1.0.2 - resolution: "string-width@npm:1.0.2" - dependencies: - code-point-at: ^1.0.0 - is-fullwidth-code-point: ^1.0.0 - strip-ansi: ^3.0.0 - checksum: 5c79439e95bc3bd7233a332c5f5926ab2ee90b23816ed4faa380ce3b2576d7800b0a5bb15ae88ed28737acc7ea06a518c2eef39142dd727adad0e45c776cd37e - languageName: node - linkType: hard - "string-width@npm:^1.0.2 || 2 || 3 || 4, string-width@npm:^4.0.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.2, string-width@npm:^4.2.3": version: 4.2.3 resolution: "string-width@npm:4.2.3" @@ -10491,15 +10160,6 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard -"strip-ansi@npm:^3.0.0, strip-ansi@npm:^3.0.1": - version: 3.0.1 - resolution: "strip-ansi@npm:3.0.1" - dependencies: - ansi-regex: ^2.0.0 - checksum: 9b974de611ce5075c70629c00fa98c46144043db92ae17748fb780f706f7a789e9989fd10597b7c2053ae8d1513fd707816a91f1879b2f71e6ac0b6a863db465 - languageName: node - linkType: hard - "strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": version: 6.0.1 resolution: "strip-ansi@npm:6.0.1" @@ -10608,14 +10268,14 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard -"svg-parser@npm:^2.0.2": +"svg-parser@npm:^2.0.4": version: 2.0.4 resolution: "svg-parser@npm:2.0.4" checksum: b3de6653048212f2ae7afe4a423e04a76ec6d2d06e1bf7eacc618a7c5f7df7faa5105561c57b94579ec831fbbdbf5f190ba56a9205ff39ed13eabdf8ab086ddf languageName: node linkType: hard -"svgo@npm:^2.5.0, svgo@npm:^2.7.0": +"svgo@npm:^2.7.0, svgo@npm:^2.8.0": version: 2.8.0 resolution: "svgo@npm:2.8.0" dependencies: @@ -10685,15 +10345,15 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard -"terser-webpack-plugin@npm:^5.1.3, terser-webpack-plugin@npm:^5.3.1": - version: 5.3.1 - resolution: "terser-webpack-plugin@npm:5.3.1" +"terser-webpack-plugin@npm:^5.1.3, terser-webpack-plugin@npm:^5.3.3": + version: 5.3.6 + resolution: "terser-webpack-plugin@npm:5.3.6" dependencies: + "@jridgewell/trace-mapping": ^0.3.14 jest-worker: ^27.4.5 schema-utils: ^3.1.1 serialize-javascript: ^6.0.0 - source-map: ^0.6.1 - terser: ^5.7.2 + terser: ^5.14.1 peerDependencies: webpack: ^5.1.0 peerDependenciesMeta: @@ -10703,21 +10363,21 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: optional: true uglify-js: optional: true - checksum: 1b808fd4f58ce0b532baacc50b9a850fc69ce0077a0e9e5076d4156c52fab3d40b02d5d9148a3eba64630cf7f40057de54f6a5a87fac1849b1f11d6bfdb42072 + checksum: 8f3448d7fdb0434ce6a0c09d95c462bfd2f4a5a430233d854163337f734a7f5c07c74513d16081e06d4ca33d366d5b1a36f5444219bc41a7403afd6162107bad languageName: node linkType: hard -"terser@npm:^5.10.0, terser@npm:^5.7.2": - version: 5.13.1 - resolution: "terser@npm:5.13.1" +"terser@npm:^5.10.0, terser@npm:^5.14.1": + version: 5.15.0 + resolution: "terser@npm:5.15.0" dependencies: + "@jridgewell/source-map": ^0.3.2 acorn: ^8.5.0 commander: ^2.20.0 - source-map: ~0.8.0-beta.0 source-map-support: ~0.5.20 bin: terser: bin/terser - checksum: 0b1f5043cf5c3973005fe2ae4ff3be82511c336a6430599dacd4e2acf77c974d4474b0f1eec4823977c1f33823147e736ff712ca8e098bee3db25946480fa29d + checksum: b2358c989fcb76b4a1c265f60e175c950d3f776e5f619a9f58f54e8d2d792cd6b4cca86071834075f3b9943556d695357bafdd4ee2390de2fc9fd96ba3efa8c8 languageName: node linkType: hard @@ -10805,15 +10465,6 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard -"tr46@npm:^1.0.1": - version: 1.0.1 - resolution: "tr46@npm:1.0.1" - dependencies: - punycode: ^2.1.0 - checksum: 96d4ed46bc161db75dbf9247a236ea0bfcaf5758baae6749e92afab0bc5a09cb59af21788ede7e55080f2bf02dce3e4a8f2a484cc45164e29f4b5e68f7cbcc1a - languageName: node - linkType: hard - "trim-trailing-lines@npm:^1.0.0": version: 1.1.4 resolution: "trim-trailing-lines@npm:1.1.4" @@ -10858,7 +10509,7 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard -"typanion@npm:^3.8.0": +"typanion@npm:^3.3.1, typanion@npm:^3.8.0": version: 3.9.0 resolution: "typanion@npm:3.9.0" checksum: db635975e86c50399fe019010e489a9adb24a74cd19b15efb1d0f434c7dccd842a9402a6a6d60e8ab6d9f7949fd8604a019802a022427e78dbbe2e74f143527b @@ -10894,9 +10545,9 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: linkType: hard "type-fest@npm:^2.5.0": - version: 2.13.0 - resolution: "type-fest@npm:2.13.0" - checksum: 3492384f759fdeaec7eaa07e79f70e777bf825cf8892690642fa9350818df4a8c50fd697fd1239ae7026064af4dd94e4d5eca27e781e0952ff302af0708a2e69 + version: 2.19.0 + resolution: "type-fest@npm:2.19.0" + checksum: a4ef07ece297c9fba78fc1bd6d85dff4472fe043ede98bd4710d2615d15776902b595abf62bd78339ed6278f021235fb28a96361f8be86ed754f778973a0d278 languageName: node linkType: hard @@ -10974,34 +10625,35 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard -"unified@npm:^8.4.2": - version: 8.4.2 - resolution: "unified@npm:8.4.2" +"unified@npm:^9.2.2": + version: 9.2.2 + resolution: "unified@npm:9.2.2" dependencies: bail: ^1.0.0 extend: ^3.0.0 + is-buffer: ^2.0.0 is-plain-obj: ^2.0.0 trough: ^1.0.0 vfile: ^4.0.0 - checksum: c2af7662d6375b14721df305786b15ba3228cd39c37da748bff00ed08ababd12ce52568f475347f270b1dea72fb0b9608563574a55c29e4f73f8be7ce0a01b4a + checksum: 7c24461be7de4145939739ce50d18227c5fbdf9b3bc5a29dabb1ce26dd3e8bd4a1c385865f6f825f3b49230953ee8b591f23beab3bb3643e3e9dc37aa8a089d5 languageName: node linkType: hard -"unique-filename@npm:^1.1.1": - version: 1.1.1 - resolution: "unique-filename@npm:1.1.1" +"unique-filename@npm:^2.0.0": + version: 2.0.1 + resolution: "unique-filename@npm:2.0.1" dependencies: - unique-slug: ^2.0.0 - checksum: cf4998c9228cc7647ba7814e255dec51be43673903897b1786eff2ac2d670f54d4d733357eb08dea969aa5e6875d0e1bd391d668fbdb5a179744e7c7551a6f80 + unique-slug: ^3.0.0 + checksum: 807acf3381aff319086b64dc7125a9a37c09c44af7620bd4f7f3247fcd5565660ac12d8b80534dcbfd067e6fe88a67e621386dd796a8af828d1337a8420a255f languageName: node linkType: hard -"unique-slug@npm:^2.0.0": - version: 2.0.2 - resolution: "unique-slug@npm:2.0.2" +"unique-slug@npm:^3.0.0": + version: 3.0.0 + resolution: "unique-slug@npm:3.0.0" dependencies: imurmurhash: ^0.1.4 - checksum: 5b6876a645da08d505dedb970d1571f6cebdf87044cb6b740c8dbb24f0d6e1dc8bdbf46825fd09f994d7cf50760e6f6e063cfa197d51c5902c00a861702eb75a + checksum: 49f8d915ba7f0101801b922062ee46b7953256c93ceca74303bd8e6413ae10aa7e8216556b54dc5382895e8221d04f1efaf75f945c2e4a515b4139f77aa6640c languageName: node linkType: hard @@ -11115,6 +10767,20 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard +"update-browserslist-db@npm:^1.0.5": + version: 1.0.7 + resolution: "update-browserslist-db@npm:1.0.7" + dependencies: + escalade: ^3.1.1 + picocolors: ^1.0.0 + peerDependencies: + browserslist: ">= 4.21.0" + bin: + browserslist-lint: cli.js + checksum: 443ed6e77d4607b8bdf12710fe1c0b570fcbb992ebcafaa0c647811e5646fa51e0b5a17641637e10044e4b770bfc3a9ce2a9350a646477545aed882a1fcff8ce + languageName: node + linkType: hard + "update-notifier@npm:^5.1.0": version: 5.1.0 resolution: "update-notifier@npm:5.1.0" @@ -11276,13 +10942,13 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard -"watchpack@npm:^2.3.1": - version: 2.3.1 - resolution: "watchpack@npm:2.3.1" +"watchpack@npm:^2.4.0": + version: 2.4.0 + resolution: "watchpack@npm:2.4.0" dependencies: glob-to-regexp: ^0.4.1 graceful-fs: ^4.1.2 - checksum: 70a34f92842d94b5d842980f866d568d7a467de667c96ae5759c759f46587e49265863171f4650bdbafc5f3870a28f2b4453e9e847098ec4b718b38926d47d22 + checksum: 23d4bc58634dbe13b86093e01c6a68d8096028b664ab7139d58f0c37d962d549a940e98f2f201cecdabd6f9c340338dc73ef8bf094a2249ef582f35183d1a131 languageName: node linkType: hard @@ -11295,23 +10961,16 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard -"web-namespaces@npm:^1.0.0, web-namespaces@npm:^1.1.2": +"web-namespaces@npm:^1.0.0": version: 1.1.4 resolution: "web-namespaces@npm:1.1.4" checksum: 5149842ccbfbc56fe4f8758957b3f8c8616a281874a5bb84aa1b305e4436a9bad853d21c629a7b8f174902449e1489c7a6c724fccf60965077c5636bd8aed42b languageName: node linkType: hard -"webidl-conversions@npm:^4.0.2": - version: 4.0.2 - resolution: "webidl-conversions@npm:4.0.2" - checksum: c93d8dfe908a0140a4ae9c0ebc87a33805b416a33ee638a605b551523eec94a9632165e54632f6d57a39c5f948c4bab10e0e066525e9a4b87a79f0d04fbca374 - languageName: node - linkType: hard - "webpack-bundle-analyzer@npm:^4.5.0": - version: 4.5.0 - resolution: "webpack-bundle-analyzer@npm:4.5.0" + version: 4.6.1 + resolution: "webpack-bundle-analyzer@npm:4.6.1" dependencies: acorn: ^8.0.4 acorn-walk: ^8.0.0 @@ -11324,7 +10983,7 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: ws: ^7.3.1 bin: webpack-bundle-analyzer: lib/bin/analyzer.js - checksum: 158e96810ec213d5665ca1c0b257097db44e1f11c4befefab8352b9e5b10890fcb3e3fc1f7bb400dd58762a8edce5621c92afeca86eb4687d2eb64e93186bfcb + checksum: 4bc97ac6a1d9cd1f133444b0fc9d9091c97f4bd8388f97636ce27abd1ebffaa7dd45d29f6693661a666e77bcc08dff43ab7c2f5e2600a3101b956c94c1d038d0 languageName: node linkType: hard @@ -11343,14 +11002,15 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard -"webpack-dev-server@npm:^4.8.1": - version: 4.9.0 - resolution: "webpack-dev-server@npm:4.9.0" +"webpack-dev-server@npm:^4.9.3": + version: 4.10.1 + resolution: "webpack-dev-server@npm:4.10.1" dependencies: "@types/bonjour": ^3.5.9 "@types/connect-history-api-fallback": ^1.3.5 "@types/express": ^4.17.13 "@types/serve-index": ^1.9.1 + "@types/serve-static": ^1.13.10 "@types/sockjs": ^0.3.33 "@types/ws": ^8.5.1 ansi-html-community: ^0.0.8 @@ -11358,7 +11018,7 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: chokidar: ^3.5.3 colorette: ^2.0.10 compression: ^1.7.4 - connect-history-api-fallback: ^1.6.0 + connect-history-api-fallback: ^2.0.0 default-gateway: ^6.0.3 express: ^4.17.3 graceful-fs: ^4.2.6 @@ -11371,7 +11031,7 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: schema-utils: ^4.0.0 selfsigned: ^2.0.1 serve-index: ^1.9.1 - sockjs: ^0.3.21 + sockjs: ^0.3.24 spdy: ^4.0.2 webpack-dev-middleware: ^5.3.1 ws: ^8.4.2 @@ -11382,7 +11042,7 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: optional: true bin: webpack-dev-server: bin/webpack-dev-server.js - checksum: 3ee3fc9650ede7be37440d404fea2420310f3fb6dfdcfdb71b1d9e4675b04f05843832c9be68ecd4bd4e8e2c960e5d9da299990bd29d05702edfd013fef9e8c8 + checksum: d026e6be63058ba5f881c58c9d49367a26c43d76bb7c2a1d9fb80eeae644099cc098913b4e9f32e2ed89eff0e7cc08e03cae8a1c4e7a1c8f67c5c673ab70761e languageName: node linkType: hard @@ -11396,37 +11056,27 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard -"webpack-sources@npm:^1.4.3": - version: 1.4.3 - resolution: "webpack-sources@npm:1.4.3" - dependencies: - source-list-map: ^2.0.0 - source-map: ~0.6.1 - checksum: 37463dad8d08114930f4bc4882a9602941f07c9f0efa9b6bc78738cd936275b990a596d801ef450d022bb005b109b9f451dd087db2f3c9baf53e8e22cf388f79 - languageName: node - linkType: hard - -"webpack-sources@npm:^3.2.3": +"webpack-sources@npm:^3.2.2, webpack-sources@npm:^3.2.3": version: 3.2.3 resolution: "webpack-sources@npm:3.2.3" checksum: 989e401b9fe3536529e2a99dac8c1bdc50e3a0a2c8669cbafad31271eadd994bc9405f88a3039cd2e29db5e6d9d0926ceb7a1a4e7409ece021fe79c37d9c4607 languageName: node linkType: hard -"webpack@npm:^5.72.0": - version: 5.72.1 - resolution: "webpack@npm:5.72.1" +"webpack@npm:^5.73.0": + version: 5.74.0 + resolution: "webpack@npm:5.74.0" dependencies: "@types/eslint-scope": ^3.7.3 "@types/estree": ^0.0.51 "@webassemblyjs/ast": 1.11.1 "@webassemblyjs/wasm-edit": 1.11.1 "@webassemblyjs/wasm-parser": 1.11.1 - acorn: ^8.4.1 + acorn: ^8.7.1 acorn-import-assertions: ^1.7.6 browserslist: ^4.14.5 chrome-trace-event: ^1.0.2 - enhanced-resolve: ^5.9.3 + enhanced-resolve: ^5.10.0 es-module-lexer: ^0.9.0 eslint-scope: 5.1.1 events: ^3.2.0 @@ -11439,14 +11089,14 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: schema-utils: ^3.1.0 tapable: ^2.1.1 terser-webpack-plugin: ^5.1.3 - watchpack: ^2.3.1 + watchpack: ^2.4.0 webpack-sources: ^3.2.3 peerDependenciesMeta: webpack-cli: optional: true bin: webpack: bin/webpack.js - checksum: d1eff085eee1c67a68f7bf1d077ea202c1e68a0de0e0866274984769838c3f224fbc64e847e1a1bbc6eba9fb6a9965098809cc0be9292b573767bb5d8d2df96e + checksum: 320c41369a75051b19e18c63f408b3dcc481852e992f83d311771c5ec0f05f2946385e8ebef62030cf3587f0a3d2f12779ffdb191569a966847289ba7313f946 languageName: node linkType: hard @@ -11482,17 +11132,6 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard -"whatwg-url@npm:^7.0.0": - version: 7.1.0 - resolution: "whatwg-url@npm:7.1.0" - dependencies: - lodash.sortby: ^4.7.0 - tr46: ^1.0.1 - webidl-conversions: ^4.0.2 - checksum: fecb07c87290b47d2ec2fb6d6ca26daad3c9e211e0e531dd7566e7ff95b5b3525a57d4f32640ad4adf057717e0c215731db842ad761e61d947e81010e05cf5fd - languageName: node - linkType: hard - "which@npm:^1.3.1": version: 1.3.1 resolution: "which@npm:1.3.1" @@ -11515,7 +11154,7 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard -"wide-align@npm:^1.1.0, wide-align@npm:^1.1.5": +"wide-align@npm:^1.1.5": version: 1.1.5 resolution: "wide-align@npm:1.1.5" dependencies: @@ -11602,8 +11241,8 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: linkType: hard "ws@npm:^7, ws@npm:^7.3.1, ws@npm:^7.5.5": - version: 7.5.8 - resolution: "ws@npm:7.5.8" + version: 7.5.9 + resolution: "ws@npm:7.5.9" peerDependencies: bufferutil: ^4.0.1 utf-8-validate: ^5.0.2 @@ -11612,13 +11251,13 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: optional: true utf-8-validate: optional: true - checksum: 49479ccf3ddab6500c5906fbcc316e9c8cd44b0ffb3903a6c1caf9b38cb9e06691685722a4c642cfa7d4c6eb390424fc3142cd4f8b940cfc7a9ce9761b1cd65b + checksum: c3c100a181b731f40b7f2fddf004aa023f79d64f489706a28bc23ff88e87f6a64b3c6651fbec3a84a53960b75159574d7a7385709847a62ddb7ad6af76f49138 languageName: node linkType: hard "ws@npm:^8.4.2": - version: 8.7.0 - resolution: "ws@npm:8.7.0" + version: 8.8.1 + resolution: "ws@npm:8.8.1" peerDependencies: bufferutil: ^4.0.1 utf-8-validate: ^5.0.2 @@ -11627,7 +11266,7 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: optional: true utf-8-validate: optional: true - checksum: 078fa2dbc06b31a45e0057b19e2930d26c222622e355955afe019c9b9b25f62eb2a8eff7cceabdad04910ecd2bd6ef4fa48e6f3673f2fdddff02a6e4c2459584 + checksum: 2152cf862cae0693f3775bc688a6afb2e989d19d626d215e70f5fcd8eb55b1c3b0d3a6a4052905ec320e2d7734e20aeedbf9744496d62f15a26ad79cf4cf7dae languageName: node linkType: hard From 0908e1207dfcf69f16171d4031d066c0c6b7d06d Mon Sep 17 00:00:00 2001 From: "Oliver E. Anderson" Date: Fri, 9 Sep 2022 08:28:36 +0200 Subject: [PATCH 51/89] Generalized Resolver (#970) * WIP generic resolver * implemented dynamic resolve * implemented a few resolver methods * improvements * improvements * Clippy fixes * fixed clippy * Added documentation * implemented default * Added documentation * Improved documentation * Added resolve_core method * moved into_any * removed resolve_core * started considering alternative approach * Made into_delegate work * refactored resolver * started new experiment avoiding impl ValidatorDocument for boxed trait obj * Introduced ValidatorBorrow * Simplified API * Fixed lints * renamed trait * Initial port of Resolver tests * renamed variables in test * Tested one more case * Started building serious test suite * fixed test * Fixed clippy * Some more tests * removed test * Moved resolver to new crate * Added exposed internals under feature flag * Started work on supporting multiple clients attached to the same method * Started work on bindings * fixes * Fixed compilation error * small changes * Added very minimal wrapper around CoreDocument * Added resolve method * take functions by reference * Got a working TS example * reverted to using a single handler per method * started refactor to passing functions in Rust * Adapted tests to async closures * refactored error * started error handling improvements * improved errors * improved errors * removed leftover TODOs * Made Resolver Send + Sync (need to reconsider design for the bindings) * started work on making the resolver agnostic with regards to threads * re-export the Resolver type instead * started command refactor * renamed field * removed Casting error variant * removed unused import * Added ThreadSafeValidatorDocument trait * continued refactor to also support the single threaded scenario. Will finish this tomorrow morning * Fixed bindings * doc tests * removed internals flag * started cleaning up the commands module * cleaned up the commands module * improved documentation * documented the error * fixed documentation * improved docs * Removed BorrowValidator * improved documentation * fixed tests * fixed tests * Fixed bindings * fixed documentation * added missing handler tests * more tests * fixes * fixed tests * added failure tests * removed duplicated code in tests * removed internal method * simplified tests * refactored error * added send sync assertion * moved and improved send sync tests * moved tests * restrict parameters for better inference * Fix workspace setup * Added tests for the happy path * tested concrete resolver in failure tests as well * removed badly ported tests * changed name of Error::cause * Added new method and fixed error * Remove superfluous `Sync` bound on `Future`s * Added verify_presentation * Add convenience method `attach_iota_handler` * Added constructor * fixed bindings * improved typescript definitions * Fixed type conversion * Removed redundant code * fixed tests. TODO: remove changes * removed code * made test more data oriented * made handler errors more flexible * serialized test input * started extending the wasm constructor * Finished the api for the bindings * wasm bindings for CoreDocument * pin tokio version * fix format issue in Cargo.toml * fix format issue in Cargo.toml * fixed clippy warnings * fix doc generating issue * conditionally compile iota-client with features in identity_iota_client * removed unnecessary declarations * unpin tokio version * fixed bindings * merged feat/core_document_bindings * fixed documentation * Fixed clippy warnings * Documented MixedResolver * Moved position of annotation * Simplified construction * renamed tests * started writing tests for the bindings * testing the test * started test * improved tests * completed JS tests * fixed clippy lints in tests * Made handler attachment consistent in the bindings * Removed testing example * removed deleted example for the README * fixed omitted documentation * Update identity_credential/src/validator/validator_document.rs Co-authored-by: Philipp * Update identity_credential/src/validator/validator_document.rs Co-authored-by: Philipp * Update identity_resolver/src/error.rs Co-authored-by: Philipp * addressed review comments * deleted leftover files * Clarify iota handler comment in Wasm * Annotate types; add whitespace for readability * Document `MixedResolver::resolve` * Add whitespace for readability in tests * Improve error message * Remove unnecessary `Sync` bound on `DID` parameter * Remove unnecessary `Send` & `Sync` bounds * Impl `Debug` for `Resolver` * Reexport resolver from `identity_iota` * Regenerate api-reference * Remove another `Send` bound * improve resolver TS test Co-authored-by: PhilippGackstatter Co-authored-by: Abdulrahim Al Methiab --- Cargo.toml | 1 + bindings/wasm/Cargo.toml | 13 +- bindings/wasm/docs/api-reference.md | 459 +++++++++++++++--- bindings/wasm/examples-stardust/README.md | 14 +- bindings/wasm/src/did/mod.rs | 1 + bindings/wasm/src/did/wasm_core_document.rs | 6 + bindings/wasm/src/error.rs | 37 +- bindings/wasm/src/lib.rs | 2 + .../wasm/src/resolver/constructor_input.rs | 51 ++ bindings/wasm/src/resolver/mixed_resolver.rs | 299 ++++++++++++ bindings/wasm/src/resolver/mod.rs | 7 + .../src/resolver/supported_document_types.rs | 81 ++++ bindings/wasm/src/stardust/mod.rs | 3 +- bindings/wasm/tests/resolver.ts | 167 +++++++ examples/0_basic/2_resolve_did.rs | 21 +- examples/Cargo.toml | 1 + identity_credential/src/validator/mod.rs | 2 + .../src/validator/validator_document.rs | 159 +++++- .../fixtures/signed_presentation/README.md | 3 + .../signed_presentation/issuer_bar_doc.json | 11 + .../signed_presentation/issuer_iota_doc.json | 17 + .../signed_presentation/presentation.json | 64 +++ .../signed_presentation/secret_keys.rs | 18 + .../signed_presentation/subject_foo_doc.json | 11 + identity_did/src/did/did.rs | 50 +- identity_iota/Cargo.toml | 4 + identity_iota/src/lib.rs | 7 + identity_iota_core/src/did/iota_did.rs | 51 -- identity_resolver/Cargo.toml | 40 ++ identity_resolver/src/error.rs | 129 +++++ identity_resolver/src/lib.rs | 11 + identity_resolver/src/resolution/commands.rs | 139 ++++++ identity_resolver/src/resolution/mod.rs | 12 + identity_resolver/src/resolution/resolver.rs | 449 +++++++++++++++++ identity_resolver/src/resolution/tests/mod.rs | 14 + .../tests/presentation_validation_errors.rs | 122 +++++ .../src/resolution/tests/resolution_errors.rs | 296 +++++++++++ .../src/resolution/tests/send_sync.rs | 51 ++ .../successful_presentation_validation.rs | 97 ++++ .../tests/valid_presentation_data.rs | 14 + identity_stardust/Cargo.toml | 7 +- .../src/client/identity_client.rs | 7 +- identity_stardust/src/client/iota_client.rs | 9 +- identity_stardust/src/did/stardust_did.rs | 51 -- .../src/document/stardust_document.rs | 6 + 45 files changed, 2797 insertions(+), 217 deletions(-) create mode 100644 bindings/wasm/src/resolver/constructor_input.rs create mode 100644 bindings/wasm/src/resolver/mixed_resolver.rs create mode 100644 bindings/wasm/src/resolver/mod.rs create mode 100644 bindings/wasm/src/resolver/supported_document_types.rs create mode 100644 bindings/wasm/tests/resolver.ts create mode 100644 identity_credential/tests/fixtures/signed_presentation/README.md create mode 100644 identity_credential/tests/fixtures/signed_presentation/issuer_bar_doc.json create mode 100644 identity_credential/tests/fixtures/signed_presentation/issuer_iota_doc.json create mode 100644 identity_credential/tests/fixtures/signed_presentation/presentation.json create mode 100644 identity_credential/tests/fixtures/signed_presentation/secret_keys.rs create mode 100644 identity_credential/tests/fixtures/signed_presentation/subject_foo_doc.json create mode 100644 identity_resolver/Cargo.toml create mode 100644 identity_resolver/src/error.rs create mode 100644 identity_resolver/src/lib.rs create mode 100644 identity_resolver/src/resolution/commands.rs create mode 100644 identity_resolver/src/resolution/mod.rs create mode 100644 identity_resolver/src/resolution/resolver.rs create mode 100644 identity_resolver/src/resolution/tests/mod.rs create mode 100644 identity_resolver/src/resolution/tests/presentation_validation_errors.rs create mode 100644 identity_resolver/src/resolution/tests/resolution_errors.rs create mode 100644 identity_resolver/src/resolution/tests/send_sync.rs create mode 100644 identity_resolver/src/resolution/tests/successful_presentation_validation.rs create mode 100644 identity_resolver/src/resolution/tests/valid_presentation_data.rs diff --git a/Cargo.toml b/Cargo.toml index b4342ed792..5036e8a798 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ members = [ "identity_iota", "identity_iota_client", "identity_iota_core", + "identity_resolver", "identity_stardust", "examples_legacy", diff --git a/bindings/wasm/Cargo.toml b/bindings/wasm/Cargo.toml index 3914cc1ab5..3557b54054 100644 --- a/bindings/wasm/Cargo.toml +++ b/bindings/wasm/Cargo.toml @@ -33,7 +33,12 @@ wasm-bindgen-futures = { version = "0.4", default-features = false } version = "=0.6.0" path = "../../identity_iota" default-features = false -features = ["account", "storage-test-suite", "unstable-encryption", "revocation-bitmap"] +features = [ + "account", + "storage-test-suite", + "unstable-encryption", + "revocation-bitmap", +] [dependencies.identity_stardust] version = "=0.6.0" @@ -41,6 +46,12 @@ path = "../../identity_stardust" default-features = false features = ["client", "revocation-bitmap"] +[dependencies.identity_resolver] +version = "=0.6.0" +path = "../../identity_resolver" +default-features = false +features = ["revocation-bitmap"] + [dev-dependencies] wasm-bindgen-test = { version = "0.3" } diff --git a/bindings/wasm/docs/api-reference.md b/bindings/wasm/docs/api-reference.md index aa6e0c6ea0..9e51cd572e 100644 --- a/bindings/wasm/docs/api-reference.md +++ b/bindings/wasm/docs/api-reference.md @@ -33,12 +33,14 @@ the configuration of previously built accounts.

A method agnostic DID Url.

CoreDocument
-
+

A method-agnostic DID Document.

+
CoreService

A DID Document Service used to enable trusted interactions associated with a DID subject.

CoreVerificationMethod
-
+

A DID Document Verification Method.

+
Credential
CredentialValidationOptions
@@ -102,6 +104,13 @@ situations like these.

MethodType

Supported verification method types.

+
MixedResolver
+

Convenience type for resolving DID documents from different DID methods.

+

Also provides methods for resolving DID Documents associated with +verifiable Credentials and Presentations.

+

Configuration

+

The resolver will only be able to resolve DID documents for methods it has been configured for in the constructor.

+
Network
Presentation
@@ -233,13 +242,13 @@ This variant is the default used if no other variant is specified when construct
FirstError

Return after the first error occurs.

+
MethodRelationship
+
DIDType

Supported types representing a DID that can be generated by the storage interface.

KeyType
-
MethodRelationship
-
DIDMessageEncoding
@@ -263,10 +272,14 @@ publishing to the Tangle. **Kind**: global class * [Account](#Account) - * [.attachMethodRelationships(options)](#Account+attachMethodRelationships) ⇒ Promise.<void> + * [.createService(options)](#Account+createService) ⇒ Promise.<void> + * [.setAlsoKnownAs(options)](#Account+setAlsoKnownAs) ⇒ Promise.<void> + * [.deleteMethod(options)](#Account+deleteMethod) ⇒ Promise.<void> + * [.deleteService(options)](#Account+deleteService) ⇒ Promise.<void> + * [.setController(options)](#Account+setController) ⇒ Promise.<void> * [.createMethod(options)](#Account+createMethod) ⇒ Promise.<void> + * [.attachMethodRelationships(options)](#Account+attachMethodRelationships) ⇒ Promise.<void> * [.detachMethodRelationships(options)](#Account+detachMethodRelationships) ⇒ Promise.<void> - * [.createService(options)](#Account+createService) ⇒ Promise.<void> * [.did()](#Account+did) ⇒ [IotaDID](#IotaDID) * [.autopublish()](#Account+autopublish) ⇒ boolean * [.autosave()](#Account+autosave) ⇒ [AutoSave](#AutoSave) @@ -284,24 +297,61 @@ publishing to the Tangle. * [.unrevokeCredentials(fragment, indices)](#Account+unrevokeCredentials) ⇒ Promise.<void> * [.encryptData(plaintext, associated_data, encryption_algorithm, cek_algorithm, public_key)](#Account+encryptData) ⇒ [Promise.<EncryptedData>](#EncryptedData) * [.decryptData(data, encryption_algorithm, cek_algorithm, fragment)](#Account+decryptData) ⇒ Promise.<Uint8Array> - * [.deleteMethod(options)](#Account+deleteMethod) ⇒ Promise.<void> - * [.deleteService(options)](#Account+deleteService) ⇒ Promise.<void> - * [.setAlsoKnownAs(options)](#Account+setAlsoKnownAs) ⇒ Promise.<void> - * [.setController(options)](#Account+setController) ⇒ Promise.<void> - + -### account.attachMethodRelationships(options) ⇒ Promise.<void> -Attach one or more verification relationships to a method. +### account.createService(options) ⇒ Promise.<void> +Adds a new Service to the DID Document. -Note: the method must exist and be in the set of verification methods; -it cannot be an embedded method. +**Kind**: instance method of [Account](#Account) + +| Param | Type | +| --- | --- | +| options | CreateServiceOptions | + + + +### account.setAlsoKnownAs(options) ⇒ Promise.<void> +Sets the `alsoKnownAs` property in the DID document. **Kind**: instance method of [Account](#Account) | Param | Type | | --- | --- | -| options | AttachMethodRelationshipOptions | +| options | SetAlsoKnownAsOptions | + + + +### account.deleteMethod(options) ⇒ Promise.<void> +Deletes a verification method if the method exists. + +**Kind**: instance method of [Account](#Account) + +| Param | Type | +| --- | --- | +| options | DeleteMethodOptions | + + + +### account.deleteService(options) ⇒ Promise.<void> +Deletes a Service if it exists. + +**Kind**: instance method of [Account](#Account) + +| Param | Type | +| --- | --- | +| options | DeleteServiceOptions | + + + +### account.setController(options) ⇒ Promise.<void> +Sets the controllers of the DID document. + +**Kind**: instance method of [Account](#Account) + +| Param | Type | +| --- | --- | +| options | SetControllerOptions | @@ -314,27 +364,30 @@ Adds a new verification method to the DID document. | --- | --- | | options | CreateMethodOptions | - + -### account.detachMethodRelationships(options) ⇒ Promise.<void> -Detaches the given relationship from the given method, if the method exists. +### account.attachMethodRelationships(options) ⇒ Promise.<void> +Attach one or more verification relationships to a method. + +Note: the method must exist and be in the set of verification methods; +it cannot be an embedded method. **Kind**: instance method of [Account](#Account) | Param | Type | | --- | --- | -| options | DetachMethodRelationshipOptions | +| options | AttachMethodRelationshipOptions | - + -### account.createService(options) ⇒ Promise.<void> -Adds a new Service to the DID Document. +### account.detachMethodRelationships(options) ⇒ Promise.<void> +Detaches the given relationship from the given method, if the method exists. **Kind**: instance method of [Account](#Account) | Param | Type | | --- | --- | -| options | CreateServiceOptions | +| options | DetachMethodRelationshipOptions | @@ -527,50 +580,6 @@ Returns the decrypted text. | cek_algorithm | [CekAlgorithm](#CekAlgorithm) | | fragment | string | - - -### account.deleteMethod(options) ⇒ Promise.<void> -Deletes a verification method if the method exists. - -**Kind**: instance method of [Account](#Account) - -| Param | Type | -| --- | --- | -| options | DeleteMethodOptions | - - - -### account.deleteService(options) ⇒ Promise.<void> -Deletes a Service if it exists. - -**Kind**: instance method of [Account](#Account) - -| Param | Type | -| --- | --- | -| options | DeleteServiceOptions | - - - -### account.setAlsoKnownAs(options) ⇒ Promise.<void> -Sets the `alsoKnownAs` property in the DID document. - -**Kind**: instance method of [Account](#Account) - -| Param | Type | -| --- | --- | -| options | SetAlsoKnownAsOptions | - - - -### account.setController(options) ⇒ Promise.<void> -Sets the controllers of the DID document. - -**Kind**: instance method of [Account](#Account) - -| Param | Type | -| --- | --- | -| options | SetControllerOptions | - ## AccountBuilder @@ -1315,12 +1324,15 @@ Deserializes an instance from a JSON object. ## CoreDocument +A method-agnostic DID Document. + **Kind**: global class * [CoreDocument](#CoreDocument) * [new CoreDocument(values)](#new_CoreDocument_new) * _instance_ * [.id()](#CoreDocument+id) ⇒ [CoreDID](#CoreDID) + * [.setId(id)](#CoreDocument+setId) * [.controller()](#CoreDocument+controller) ⇒ [Array.<CoreDID>](#CoreDID) * [.setController(controllers)](#CoreDocument+setController) * [.alsoKnownAs()](#CoreDocument+alsoKnownAs) ⇒ Array.<string> @@ -1334,8 +1346,11 @@ Deserializes an instance from a JSON object. * [.properties()](#CoreDocument+properties) ⇒ Map.<string, any> * [.setPropertyUnchecked(key, value)](#CoreDocument+setPropertyUnchecked) * [.service()](#CoreDocument+service) ⇒ [Array.<CoreService>](#CoreService) + * [.insertService(service)](#CoreDocument+insertService) ⇒ boolean + * [.removeService(didUrl)](#CoreDocument+removeService) ⇒ boolean * [.resolveService(query)](#CoreDocument+resolveService) ⇒ [CoreService](#CoreService) \| undefined * [.methods()](#CoreDocument+methods) ⇒ [Array.<CoreVerificationMethod>](#CoreVerificationMethod) + * [.verificationRelationships()](#CoreDocument+verificationRelationships) ⇒ Array.<(CoreDIDUrl\|CoreVerificationMethod)> * [.insertMethod(method, scope)](#CoreDocument+insertMethod) * [.removeMethod(did)](#CoreDocument+removeMethod) * [.resolveMethod(query, scope)](#CoreDocument+resolveMethod) ⇒ [CoreVerificationMethod](#CoreVerificationMethod) \| undefined @@ -1344,6 +1359,7 @@ Deserializes an instance from a JSON object. * [.verifyData(data, options)](#CoreDocument+verifyData) ⇒ boolean * [.revokeCredentials(serviceQuery, indices)](#CoreDocument+revokeCredentials) * [.unrevokeCredentials(serviceQuery, indices)](#CoreDocument+unrevokeCredentials) + * [.signData(data, privateKey, methodQuery, options)](#CoreDocument+signData) ⇒ any * [.toJSON()](#CoreDocument+toJSON) ⇒ any * [.clone()](#CoreDocument+clone) ⇒ [CoreDocument](#CoreDocument) * _static_ @@ -1365,6 +1381,17 @@ Creates a new `CoreDocument` with the given properties. Returns a copy of the DID Document `id`. **Kind**: instance method of [CoreDocument](#CoreDocument) + + +### coreDocument.setId(id) +Sets the DID of the document. + +**Kind**: instance method of [CoreDocument](#CoreDocument) + +| Param | Type | +| --- | --- | +| id | [CoreDID](#CoreDID) | + ### coreDocument.controller() ⇒ [Array.<CoreDID>](#CoreDID) @@ -1466,6 +1493,32 @@ This method can overwrite existing properties like `id` and result in an invalid Returns a set of all [CoreService](#CoreService) in the document. **Kind**: instance method of [CoreDocument](#CoreDocument) + + +### coreDocument.insertService(service) ⇒ boolean +Add a new [CoreService](#CoreService) to the document. + +Returns `true` if the service was added. + +**Kind**: instance method of [CoreDocument](#CoreDocument) + +| Param | Type | +| --- | --- | +| service | [CoreService](#CoreService) | + + + +### coreDocument.removeService(didUrl) ⇒ boolean +Remoce a [CoreService](#CoreService) identified by the given [CoreDIDUrl](#CoreDIDUrl) from the document. + +Returns `true` if the service was removed. + +**Kind**: instance method of [CoreDocument](#CoreDocument) + +| Param | Type | +| --- | --- | +| didUrl | [CoreDIDUrl](#CoreDIDUrl) | + ### coreDocument.resolveService(query) ⇒ [CoreService](#CoreService) \| undefined @@ -1483,6 +1536,12 @@ if present. ### coreDocument.methods() ⇒ [Array.<CoreVerificationMethod>](#CoreVerificationMethod) Returns a list of all [CoreVerificationMethod](#CoreVerificationMethod) in the DID Document. +**Kind**: instance method of [CoreDocument](#CoreDocument) + + +### coreDocument.verificationRelationships() ⇒ Array.<(CoreDIDUrl\|CoreVerificationMethod)> +Returns an array of all verification relationships. + **Kind**: instance method of [CoreDocument](#CoreDocument) @@ -1586,6 +1645,23 @@ unrevoke all specified `indices`. | serviceQuery | [CoreDIDUrl](#CoreDIDUrl) \| string | | indices | number \| Array.<number> | + + +### coreDocument.signData(data, privateKey, methodQuery, options) ⇒ any +Creates a signature for the given `data` with the specified DID Document +Verification Method. + +NOTE: use `signSelf` or `signDocument` for DID Documents. + +**Kind**: instance method of [CoreDocument](#CoreDocument) + +| Param | Type | +| --- | --- | +| data | any | +| privateKey | Uint8Array | +| methodQuery | [CoreDIDUrl](#CoreDIDUrl) \| string | +| options | [ProofOptions](#ProofOptions) | + ### coreDocument.toJSON() ⇒ any @@ -1686,16 +1762,23 @@ Deserializes an instance from a JSON object. ## CoreVerificationMethod +A DID Document Verification Method. + **Kind**: global class * [CoreVerificationMethod](#CoreVerificationMethod) * [new CoreVerificationMethod(did, keyType, publicKey, fragment)](#new_CoreVerificationMethod_new) * _instance_ * [.id()](#CoreVerificationMethod+id) ⇒ [CoreDIDUrl](#CoreDIDUrl) + * [.setId(id)](#CoreVerificationMethod+setId) * [.controller()](#CoreVerificationMethod+controller) ⇒ [CoreDID](#CoreDID) * [.setController(did)](#CoreVerificationMethod+setController) * [.type()](#CoreVerificationMethod+type) ⇒ [MethodType](#MethodType) + * [.setType(type_)](#CoreVerificationMethod+setType) * [.data()](#CoreVerificationMethod+data) ⇒ [MethodData](#MethodData) + * [.setData(data)](#CoreVerificationMethod+setData) + * [.properties()](#CoreVerificationMethod+properties) ⇒ Map.<string, any> + * [.setPropertyUnchecked(key, value)](#CoreVerificationMethod+setPropertyUnchecked) * [.toJSON()](#CoreVerificationMethod+toJSON) ⇒ any * [.clone()](#CoreVerificationMethod+clone) ⇒ [CoreVerificationMethod](#CoreVerificationMethod) * _static_ @@ -1720,6 +1803,17 @@ Creates a new `CoreVerificationMethod` from the given `did` and public key. Returns a copy of the `CoreDIDUrl` of the `CoreVerificationMethod`'s `id`. **Kind**: instance method of [CoreVerificationMethod](#CoreVerificationMethod) + + +### coreVerificationMethod.setId(id) +Sets the id of the `CoreVerificationMethod`. + +**Kind**: instance method of [CoreVerificationMethod](#CoreVerificationMethod) + +| Param | Type | +| --- | --- | +| id | [CoreDIDUrl](#CoreDIDUrl) | + ### coreVerificationMethod.controller() ⇒ [CoreDID](#CoreDID) @@ -1743,12 +1837,57 @@ Sets the `controller` `DID` of the `CoreVerificationMethod` object. Returns a copy of the `CoreVerificationMethod` type. **Kind**: instance method of [CoreVerificationMethod](#CoreVerificationMethod) + + +### coreVerificationMethod.setType(type_) +Sets the `CoreVerificationMethod` type. + +**Kind**: instance method of [CoreVerificationMethod](#CoreVerificationMethod) + +| Param | Type | +| --- | --- | +| type_ | [MethodType](#MethodType) | + ### coreVerificationMethod.data() ⇒ [MethodData](#MethodData) Returns a copy of the `CoreVerificationMethod` public key data. **Kind**: instance method of [CoreVerificationMethod](#CoreVerificationMethod) + + +### coreVerificationMethod.setData(data) +Sets `CoreVerificationMethod` public key data. + +**Kind**: instance method of [CoreVerificationMethod](#CoreVerificationMethod) + +| Param | Type | +| --- | --- | +| data | [MethodData](#MethodData) | + + + +### coreVerificationMethod.properties() ⇒ Map.<string, any> +Get custom properties of the Verification Method. + +**Kind**: instance method of [CoreVerificationMethod](#CoreVerificationMethod) + + +### coreVerificationMethod.setPropertyUnchecked(key, value) +Adds a custom property to the Verification Method. +If the value is set to `null`, the custom property will be removed. + +### WARNING +This method can overwrite existing properties like `id` and result +in an invalid Verification Method. + +**Kind**: instance method of [CoreVerificationMethod](#CoreVerificationMethod) + +| Param | Type | +| --- | --- | +| key | string | +| value | any | + ### coreVerificationMethod.toJSON() ⇒ any @@ -4317,6 +4456,117 @@ Deserializes an instance from a JSON object. | --- | --- | | json | any | + + +## MixedResolver +Convenience type for resolving DID documents from different DID methods. + +Also provides methods for resolving DID Documents associated with +verifiable `Credentials` and `Presentations`. + +# Configuration +The resolver will only be able to resolve DID documents for methods it has been configured for in the constructor. + +**Kind**: global class + +* [MixedResolver](#MixedResolver) + * [new MixedResolver(config)](#new_MixedResolver_new) + * [.resolvePresentationIssuers(presentation)](#MixedResolver+resolvePresentationIssuers) ⇒ Promise.<Array.<(StardustDocument\|CoreDocument)>> + * [.resolvePresentationHolder(presentation)](#MixedResolver+resolvePresentationHolder) ⇒ Promise.<(StardustDocument\|CoreDocument)> + * [.verifyPresentation(presentation, options, fail_fast, holder, issuers)](#MixedResolver+verifyPresentation) ⇒ Promise.<void> + * [.resolve(did)](#MixedResolver+resolve) ⇒ Promise.<(StardustDocument\|CoreDocument)> + + + +### new MixedResolver(config) +Constructs a new `MixedResolver`. + +# Errors +If both a `client` is given and the `handlers` map contains the "iota" key the construction process +will throw an error as it is then ambiguous what should be . + + +| Param | Type | +| --- | --- | +| config | ResolverConfig | + + + +### mixedResolver.resolvePresentationIssuers(presentation) ⇒ Promise.<Array.<(StardustDocument\|CoreDocument)>> +Fetches all DID Documents of `Credential` issuers contained in a `Presentation`. +Issuer documents are returned in arbitrary order. + +# Errors +Errors if any issuer URL cannot be parsed to a DID whose associated method is supported by this Resolver, or +resolution fails. + +**Kind**: instance method of [MixedResolver](#MixedResolver) + +| Param | Type | +| --- | --- | +| presentation | [Presentation](#Presentation) | + + + +### mixedResolver.resolvePresentationHolder(presentation) ⇒ Promise.<(StardustDocument\|CoreDocument)> +Fetches the DID Document of the holder of a `Presentation`. + +# Errors +Errors if the holder URL is missing, cannot be parsed to a valid DID whose method is supported by the resolver, or +DID resolution fails. + +**Kind**: instance method of [MixedResolver](#MixedResolver) + +| Param | Type | +| --- | --- | +| presentation | [Presentation](#Presentation) | + + + +### mixedResolver.verifyPresentation(presentation, options, fail_fast, holder, issuers) ⇒ Promise.<void> +Verifies a `Presentation`. + +### Important +See `PresentationValidator::validate` for information about which properties get +validated and what is expected of the optional arguments `holder` and `issuer`. + +### Resolution +The DID Documents for the `holder` and `issuers` are optionally resolved if not given. +If you already have up-to-date versions of these DID Documents, you may want +to use `PresentationValidator::validate`. +See also `Resolver::resolvePresentationIssuers` and `Resolver::resolvePresentationHolder`. + +### Errors +Errors from resolving the holder and issuer DID Documents, if not provided, will be returned immediately. +Otherwise, errors from validating the presentation and its credentials will be returned +according to the `fail_fast` parameter. + +**Kind**: instance method of [MixedResolver](#MixedResolver) + +| Param | Type | +| --- | --- | +| presentation | [Presentation](#Presentation) | +| options | [PresentationValidationOptions](#PresentationValidationOptions) | +| fail_fast | number | +| holder | [StardustDocument](#StardustDocument) \| [CoreDocument](#CoreDocument) \| undefined | +| issuers | Array.<(StardustDocument\|CoreDocument)> \| undefined | + + + +### mixedResolver.resolve(did) ⇒ Promise.<(StardustDocument\|CoreDocument)> +Fetches the DID Document of the given DID. + +### Errors + +Errors if the resolver has not been configured to handle the method +corresponding to the given DID or the resolution process itself fails. + +**Kind**: instance method of [MixedResolver](#MixedResolver) + +| Param | Type | +| --- | --- | +| did | string | + ## Network @@ -5952,7 +6202,7 @@ Returns `true` if the service was added. ### stardustDocument.removeService(did) ⇒ boolean -Remove a [StardustService](#StardustService) identified by the given [DIDUrl](#DIDUrl) from the document. +Remove a [StardustService](#StardustService) identified by the given [StardustDIDUrl](#StardustDIDUrl) from the document. Returns `true` if a service was removed. @@ -6533,10 +6783,15 @@ Deserializes an instance from a JSON object. * [new StardustVerificationMethod(did, keyType, publicKey, fragment)](#new_StardustVerificationMethod_new) * _instance_ * [.id()](#StardustVerificationMethod+id) ⇒ [StardustDIDUrl](#StardustDIDUrl) + * [.setId(id)](#StardustVerificationMethod+setId) * [.controller()](#StardustVerificationMethod+controller) ⇒ [StardustDID](#StardustDID) * [.setController(did)](#StardustVerificationMethod+setController) * [.type()](#StardustVerificationMethod+type) ⇒ [MethodType](#MethodType) + * [.setType(type_)](#StardustVerificationMethod+setType) * [.data()](#StardustVerificationMethod+data) ⇒ [MethodData](#MethodData) + * [.setData(data)](#StardustVerificationMethod+setData) + * [.properties()](#StardustVerificationMethod+properties) ⇒ Map.<string, any> + * [.setPropertyUnchecked(key, value)](#StardustVerificationMethod+setPropertyUnchecked) * [.toJSON()](#StardustVerificationMethod+toJSON) ⇒ any * [.clone()](#StardustVerificationMethod+clone) ⇒ [StardustVerificationMethod](#StardustVerificationMethod) * _static_ @@ -6558,9 +6813,20 @@ Creates a new `StardustVerificationMethod` from the given `did` and public key. ### stardustVerificationMethod.id() ⇒ [StardustDIDUrl](#StardustDIDUrl) -Returns a reference to the `StardustVerificationMethod` id. +Returns a copy of the `StardustVerificationMethod` id. + +**Kind**: instance method of [StardustVerificationMethod](#StardustVerificationMethod) + + +### stardustVerificationMethod.setId(id) +Sets the id of the `StardustVerificationMethod`. **Kind**: instance method of [StardustVerificationMethod](#StardustVerificationMethod) + +| Param | Type | +| --- | --- | +| id | [StardustDIDUrl](#StardustDIDUrl) | + ### stardustVerificationMethod.controller() ⇒ [StardustDID](#StardustDID) @@ -6584,12 +6850,57 @@ Sets the `controller` `DID` of the `StardustVerificationMethod`. Returns a copy of the `StardustVerificationMethod` type. **Kind**: instance method of [StardustVerificationMethod](#StardustVerificationMethod) + + +### stardustVerificationMethod.setType(type_) +Sets the `StardustVerificationMethod` type. + +**Kind**: instance method of [StardustVerificationMethod](#StardustVerificationMethod) + +| Param | Type | +| --- | --- | +| type_ | [MethodType](#MethodType) | + ### stardustVerificationMethod.data() ⇒ [MethodData](#MethodData) Returns a copy of the `StardustVerificationMethod` public key data. **Kind**: instance method of [StardustVerificationMethod](#StardustVerificationMethod) + + +### stardustVerificationMethod.setData(data) +Sets `StardustVerificationMethod` public key data. + +**Kind**: instance method of [StardustVerificationMethod](#StardustVerificationMethod) + +| Param | Type | +| --- | --- | +| data | [MethodData](#MethodData) | + + + +### stardustVerificationMethod.properties() ⇒ Map.<string, any> +Get custom properties of the Verification Method. + +**Kind**: instance method of [StardustVerificationMethod](#StardustVerificationMethod) + + +### stardustVerificationMethod.setPropertyUnchecked(key, value) +Adds a custom property to the Verification Method. +If the value is set to `null`, the custom property will be removed. + +### WARNING +This method can overwrite existing properties like `id` and result +in an invalid Verification Method. + +**Kind**: instance method of [StardustVerificationMethod](#StardustVerificationMethod) + +| Param | Type | +| --- | --- | +| key | string | +| value | any | + ### stardustVerificationMethod.toJSON() ⇒ any @@ -7095,6 +7406,10 @@ Return all errors that occur during validation. ## FirstError Return after the first error occurs. +**Kind**: global variable + + +## MethodRelationship **Kind**: global variable @@ -7106,10 +7421,6 @@ Supported types representing a DID that can be generated by the storage interfac ## KeyType **Kind**: global variable - - -## MethodRelationship -**Kind**: global variable ## DIDMessageEncoding diff --git a/bindings/wasm/examples-stardust/README.md b/bindings/wasm/examples-stardust/README.md index 6594c6597e..e693126871 100644 --- a/bindings/wasm/examples-stardust/README.md +++ b/bindings/wasm/examples-stardust/README.md @@ -32,13 +32,13 @@ For instance, to run the `ex0_create_did` example execute: npm run example:stardust -- ex0_create_did ``` -| # | Name | Details | -|-----|-------------------------------------------------|------------------------------------------------------------------| -| 0 | [ex0_create_did](src/ex0_create_did.ts) | Create a DID Document and publish it in a new Alias Output. | -| 1 | [ex1_update_did](src/ex1_update_did.ts) | Update a DID document in an existing Alias Output. | -| 2 | [ex2_resolve_did](src/ex2_resolve_did.ts) | Resolve an existing DID in an Alias Output. | -| 3 | [ex3_deactivate_did](src/ex3_deactivate_did.ts) | Deactivate a DID in an Alias Output. | -| 4 | [ex4_delete_did](src/ex4_delete_did.ts) | Delete a DID in an Alias Output, reclaiming the storage deposit. | +| # | Name | Details | +|-----|-------------------------------------------------------|------------------------------------------------------------------| +| 0 | [ex0_create_did](src/ex0_create_did.ts) | Create a DID Document and publish it in a new Alias Output. | +| 1 | [ex1_update_did](src/ex1_update_did.ts) | Update a DID document in an existing Alias Output. | +| 2 | [ex2_resolve_did](src/ex2_resolve_did.ts) | Resolve an existing DID in an Alias Output. | +| 3 | [ex3_deactivate_did](src/ex3_deactivate_did.ts) | Deactivate a DID in an Alias Output. | +| 4 | [ex4_delete_did](src/ex4_delete_did.ts) | Delete a DID in an Alias Output, reclaiming the storage deposit. | ## Browser diff --git a/bindings/wasm/src/did/mod.rs b/bindings/wasm/src/did/mod.rs index 00bdcd6ff0..be0323a99d 100644 --- a/bindings/wasm/src/did/mod.rs +++ b/bindings/wasm/src/did/mod.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 pub use self::wasm_core_did::WasmCoreDID; +pub use self::wasm_core_document::WasmCoreDocument; pub use self::wasm_did_url::WasmDIDUrl; pub use self::wasm_diff_message::WasmDiffMessage; pub use self::wasm_document::WasmDocument; diff --git a/bindings/wasm/src/did/wasm_core_document.rs b/bindings/wasm/src/did/wasm_core_document.rs index ac30069e2e..7f0c9a6d62 100644 --- a/bindings/wasm/src/did/wasm_core_document.rs +++ b/bindings/wasm/src/did/wasm_core_document.rs @@ -514,3 +514,9 @@ struct ICoreDocumentHelper { impl_wasm_json!(WasmCoreDocument, CoreDocument); impl_wasm_clone!(WasmCoreDocument, CoreDocument); + +impl From for WasmCoreDocument { + fn from(doc: CoreDocument) -> Self { + Self(doc) + } +} diff --git a/bindings/wasm/src/error.rs b/bindings/wasm/src/error.rs index 4e5fad6931..8fc5ed17d8 100644 --- a/bindings/wasm/src/error.rs +++ b/bindings/wasm/src/error.rs @@ -160,6 +160,21 @@ impl<'a> Display for ErrorMessage<'a, identity_iota::client::Error> { } } +impl<'a> Display for ErrorMessage<'a, identity_resolver::Error> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + error_chain_fmt(self.0, f) + } +} + +impl From for WasmError<'_> { + fn from(error: identity_resolver::Error) -> Self { + Self { + name: Cow::Owned(format!("ResolverError::{}", <&'static str>::from(error.error_cause()))), + message: Cow::Owned(ErrorMessage(&error).to_string()), + } + } +} + impl From for WasmError<'_> { fn from(error: identity_iota::client::Error) -> Self { Self { @@ -214,12 +229,17 @@ impl From for WasmError<'_> { } } -/// Convenience struct to convert Result to an AccountStorageResult<_, AccountStorageError> +/// Convenience struct to convert Result to errors in the Rust library. pub struct JsValueResult(pub(crate) Result); impl JsValueResult { /// Consumes the struct and returns a Result<_, AccountStorageError>, leaving an `Ok` value untouched. pub fn to_account_error(self) -> StdResult { + self.stringify_error().map_err(AccountStorageError::JsError) + } + + // Consumes the struct and returns a Result<_, String>, leaving an `Ok` value untouched. + pub(crate) fn stringify_error(self) -> StdResult { self.0.map_err(|js_value| { let error_string: String = match wasm_bindgen::JsCast::dyn_into::(js_value) { Ok(js_err) => ToString::to_string(&js_err.to_string()), @@ -228,24 +248,13 @@ impl JsValueResult { format!("{js_val:?}") } }; - - AccountStorageError::JsError(error_string) + error_string }) } /// Consumes the struct and returns a Result<_, identity_stardust::Error>, leaving an `Ok` value untouched. pub fn to_stardust_error(self) -> StdResult { - self.0.map_err(|js_value| { - let error_string: String = match wasm_bindgen::JsCast::dyn_into::(js_value) { - Ok(js_err) => ToString::to_string(&js_err.to_string()), - Err(js_val) => { - // Fall back to debug formatting if the error is not a proper JS Error instance. - format!("{js_val:?}") - } - }; - - identity_stardust::Error::JsError(error_string) - }) + self.stringify_error().map_err(identity_stardust::Error::JsError) } } diff --git a/bindings/wasm/src/lib.rs b/bindings/wasm/src/lib.rs index 64fd474c6e..4feb55610d 100644 --- a/bindings/wasm/src/lib.rs +++ b/bindings/wasm/src/lib.rs @@ -28,6 +28,8 @@ pub mod revocation; pub mod stardust; pub mod tangle; +pub mod resolver; + /// Initializes the console error panic hook for better error messages #[wasm_bindgen(start)] pub fn start() -> Result<(), JsValue> { diff --git a/bindings/wasm/src/resolver/constructor_input.rs b/bindings/wasm/src/resolver/constructor_input.rs new file mode 100644 index 0000000000..7eb33b1022 --- /dev/null +++ b/bindings/wasm/src/resolver/constructor_input.rs @@ -0,0 +1,51 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use wasm_bindgen::prelude::*; + +use crate::stardust::WasmStardustIdentityClient; + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(typescript_type = "ResolutionHandlers")] + pub(crate) type MapResolutionHandler; + + #[wasm_bindgen(typescript_type = "ResolverConfig")] + pub type ResolverConfig; + + #[wasm_bindgen(method, getter)] + pub(crate) fn client(this: &ResolverConfig) -> Option; + + #[wasm_bindgen(method, getter)] + pub(crate) fn handlers(this: &ResolverConfig) -> Option; + +} + +// Workaround because JSDocs does not support arrows (=>) while TS does not support the "function" word in type +// definitions (which would be accepted by JSDocs). +#[wasm_bindgen(typescript_custom_section)] +const HANDLERS: &'static str = + "export type ResolutionHandlers = Map Promise>;"; + +#[wasm_bindgen(typescript_custom_section)] +const TS_RESOLVER_CONFIG: &'static str = r#" +/** + * Configurations for the new {@link MixedResolver}. + */ +export type ResolverConfig = { + + /** + * Client for resolving DIDs of the iota method. + */ + client?: IStardustIdentityClient, + + /** + * Handlers for resolving DIDs from arbitrary DID methods. + * + * The keys to the map are expected to match the method name and the values are asynchronous functions returning DID documents. + * + * Note that if a `client` is given the key "iota" may NOT be present in this map. + */ + handlers?: Map Promise> +}; +"#; diff --git a/bindings/wasm/src/resolver/mixed_resolver.rs b/bindings/wasm/src/resolver/mixed_resolver.rs new file mode 100644 index 0000000000..ae35954b3c --- /dev/null +++ b/bindings/wasm/src/resolver/mixed_resolver.rs @@ -0,0 +1,299 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::borrow::Cow; +use std::rc::Rc; + +use identity_iota::credential::AbstractValidatorDocument; +use identity_iota::credential::Presentation; +use identity_iota::credential::PresentationValidationOptions; +use identity_iota::did::CoreDID; +use identity_iota::did::DID; +use identity_resolver::SingleThreadedResolver; +use identity_stardust::StardustDID; +use identity_stardust::StardustDocument; +use identity_stardust::StardustIdentityClientExt; +use js_sys::Function; +use js_sys::Map; +use js_sys::Promise; +use wasm_bindgen_futures::JsFuture; + +use crate::common::PromiseVoid; +use crate::credential::WasmFailFast; +use crate::credential::WasmPresentation; +use crate::credential::WasmPresentationValidationOptions; +use crate::error::JsValueResult; +use crate::error::WasmError; +use crate::resolver::constructor_input::MapResolutionHandler; +use crate::resolver::constructor_input::ResolverConfig; +use crate::resolver::supported_document_types::OptionArraySupportedDocument; +use crate::resolver::supported_document_types::OptionSupportedDocument; +use crate::resolver::supported_document_types::RustSupportedDocument; +use crate::stardust::WasmStardustDID; +use crate::stardust::WasmStardustIdentityClient; + +use super::supported_document_types::PromiseArraySupportedDocument; +use super::supported_document_types::PromiseSupportedDocument; +use crate::error::Result; +use crate::error::WasmResult; +use wasm_bindgen::prelude::*; +use wasm_bindgen::JsCast; +use wasm_bindgen_futures::future_to_promise; + +/// Convenience type for resolving DID documents from different DID methods. +/// +/// Also provides methods for resolving DID Documents associated with +/// verifiable `Credentials` and `Presentations`. +/// +/// # Configuration +/// The resolver will only be able to resolve DID documents for methods it has been configured for in the constructor. +#[wasm_bindgen(js_name = MixedResolver)] +pub struct MixedResolver(Rc); + +#[wasm_bindgen(js_class = MixedResolver)] +impl MixedResolver { + /// Constructs a new `MixedResolver`. + /// + /// # Errors + /// If both a `client` is given and the `handlers` map contains the "iota" key the construction process + /// will throw an error as it is then ambiguous what should be . + #[wasm_bindgen(constructor)] + pub fn new(config: ResolverConfig) -> Result { + let mut resolver: SingleThreadedResolver = SingleThreadedResolver::new(); + + let mut attached_iota_method = false; + let resolution_handlers: Option = config.handlers(); + let client: Option = config.client(); + + if let Some(handlers) = resolution_handlers { + let map: &Map = handlers.dyn_ref::().ok_or_else(|| { + WasmError::new( + Cow::Borrowed("ResolverError::ConstructionError"), + Cow::Borrowed( + "could not construct resolver: the constructor did not receive a map of asynchronous functions", + ), + ) + })?; + + Self::attach_handlers(&mut resolver, map, &mut attached_iota_method)?; + } + + if let Some(wasm_client) = client { + if attached_iota_method { + Err(WasmError::new( + Cow::Borrowed("ResolverError::ConstructionError"), + Cow::Borrowed("could not construct resolver: cannot attach the iota method twice"), + ))?; + } + + let rc_client: Rc = Rc::new(wasm_client); + // Take CoreDID (instead of StardustDID) to avoid inconsistent error messages between the + // cases when the iota handler is attached by passing a client or directly as a handler. + let handler = move |did: CoreDID| { + let rc_client_clone: Rc = rc_client.clone(); + async move { + let stardust_did: StardustDID = StardustDID::parse(did)?; + Self::client_as_handler(rc_client_clone.as_ref(), stardust_did.into()).await + } + }; + resolver.attach_handler(StardustDID::METHOD.to_owned(), handler); + } + + Ok(Self(Rc::new(resolver))) + } + + pub(crate) async fn client_as_handler( + client: &WasmStardustIdentityClient, + did: WasmStardustDID, + ) -> std::result::Result { + client.resolve_did(&did.0).await + } + + /// attempts to extract (method, handler) pairs from the entries of a map and attaches them to the resolver. + fn attach_handlers(resolver: &mut SingleThreadedResolver, map: &Map, attached_iota_method: &mut bool) -> Result<()> { + for key in map.keys() { + if let Ok(js_method) = key { + let js_handler: JsValue = map.get(&js_method); + let did_method: String = js_method.as_string().ok_or_else(|| { + WasmError::new( + Cow::Borrowed("ResolverError::ConstructionError"), + Cow::Borrowed("could not construct resolver: the handler map contains a key which is not a string"), + ) + })?; + let handler: Function = js_handler + .dyn_into::() + .map_err(|_| "could not construct resolver: the handler map contains a value which is not a function")?; + + if did_method == StardustDID::METHOD { + *attached_iota_method = true; + } + + MixedResolver::attach_handler(resolver, did_method, handler); + } else { + Err(WasmError::new( + Cow::Borrowed("ResolverError::ConstructionError"), + Cow::Borrowed( + "could not construct resolver: invalid constructor arguments. Expected a map of asynchronous functions", + ), + ))?; + } + } + + Ok(()) + } + + /// Converts a JS handler to a Rust closure and attaches it to the given `resolver`. + fn attach_handler(resolver: &mut SingleThreadedResolver, method: String, handler: Function) { + let fun = move |input: CoreDID| { + let fun_clone: Function = handler.clone(); + async move { + let closure_output_promise: Promise = Promise::resolve( + &JsValueResult::from(fun_clone.call1(&JsValue::null(), &input.as_str().into())).stringify_error()?, + ); + let awaited_output: JsValue = + JsValueResult::from(JsFuture::from(closure_output_promise).await).stringify_error()?; + + let supported_document: RustSupportedDocument = awaited_output.into_serde().map_err(|error| { + format!( + "unable to convert the resolved document into a supported DID document: {}", + error + ) + })?; + std::result::Result::<_, String>::Ok(AbstractValidatorDocument::from(supported_document)) + } + }; + + resolver.attach_handler(method, fun); + } + + /// Fetches all DID Documents of `Credential` issuers contained in a `Presentation`. + /// Issuer documents are returned in arbitrary order. + /// + /// # Errors + /// Errors if any issuer URL cannot be parsed to a DID whose associated method is supported by this Resolver, or + /// resolution fails. + #[wasm_bindgen(js_name = resolvePresentationIssuers)] + pub fn resolve_presentation_issuers(&self, presentation: &WasmPresentation) -> Result { + let resolver: Rc = self.0.clone(); + let presentation: Presentation = presentation.0.clone(); + + let promise: Promise = future_to_promise(async move { + let supported_documents: Vec = resolver + .resolve_presentation_issuers(&presentation) + .await + .wasm_result() + .and_then(|abstractly_resolved| { + abstractly_resolved + .into_iter() + .map(|abstract_doc| RustSupportedDocument::try_from(abstract_doc).map_err(JsValue::from)) + .collect::>() + })?; + + Ok( + supported_documents + .into_iter() + .map(JsValue::from) + .collect::() + .into(), + ) + }); + Ok(promise.unchecked_into::()) + } + + /// Fetches the DID Document of the holder of a `Presentation`. + /// + /// # Errors + /// Errors if the holder URL is missing, cannot be parsed to a valid DID whose method is supported by the resolver, or + /// DID resolution fails. + #[wasm_bindgen(js_name = resolvePresentationHolder)] + pub fn resolve_presentation_holder(&self, presentation: &WasmPresentation) -> Result { + let resolver: Rc = self.0.clone(); + let presentation: Presentation = presentation.0.clone(); + + let promise: Promise = future_to_promise(async move { + resolver + .resolve_presentation_holder(&presentation) + .await + .wasm_result() + .and_then(|abstract_doc| RustSupportedDocument::try_from(abstract_doc).map_err(JsValue::from)) + .map(JsValue::from) + }); + Ok(promise.unchecked_into::()) + } + + /// Verifies a `Presentation`. + /// + /// ### Important + /// See `PresentationValidator::validate` for information about which properties get + /// validated and what is expected of the optional arguments `holder` and `issuer`. + /// + /// ### Resolution + /// The DID Documents for the `holder` and `issuers` are optionally resolved if not given. + /// If you already have up-to-date versions of these DID Documents, you may want + /// to use `PresentationValidator::validate`. + /// See also `Resolver::resolvePresentationIssuers` and `Resolver::resolvePresentationHolder`. + /// + /// ### Errors + /// Errors from resolving the holder and issuer DID Documents, if not provided, will be returned immediately. + /// Otherwise, errors from validating the presentation and its credentials will be returned + /// according to the `fail_fast` parameter. + #[wasm_bindgen(js_name = verifyPresentation)] + pub fn verify_presentation( + &self, + presentation: &WasmPresentation, + options: &WasmPresentationValidationOptions, + fail_fast: WasmFailFast, + holder: &OptionSupportedDocument, + issuers: &OptionArraySupportedDocument, + ) -> Result { + let resolver: Rc = self.0.clone(); + let presentation: Presentation = presentation.0.clone(); + let options: PresentationValidationOptions = options.0.clone(); + + let holder: Option = holder.into_serde().wasm_result()?; + let holder: Option = holder.map(From::from); + let issuers: Option> = issuers.into_serde().wasm_result()?; + let issuers: Option> = + issuers.map(|vector| vector.into_iter().map(AbstractValidatorDocument::from).collect()); + + let promise: Promise = future_to_promise(async move { + resolver + .verify_presentation( + &presentation, + &options, + fail_fast.into(), + holder.as_ref(), + issuers.as_deref(), + ) + .await + .wasm_result() + .map(|_| JsValue::UNDEFINED) + }); + + Ok(promise.unchecked_into::()) + } + + /// Fetches the DID Document of the given DID. + /// + /// ### Errors + /// + /// Errors if the resolver has not been configured to handle the method + /// corresponding to the given DID or the resolution process itself fails. + #[wasm_bindgen] + pub fn resolve(&self, did: &str) -> Result { + let resolver: Rc = self.0.clone(); + let did: CoreDID = CoreDID::parse(did).wasm_result()?; + + let promise: Promise = future_to_promise(async move { + resolver + .resolve(&did) + .await + .map_err(WasmError::from) + .and_then(RustSupportedDocument::try_from) + .map_err(JsValue::from) + .map(JsValue::from) + }); + + Ok(promise.unchecked_into::()) + } +} diff --git a/bindings/wasm/src/resolver/mod.rs b/bindings/wasm/src/resolver/mod.rs new file mode 100644 index 0000000000..c78fe1fecd --- /dev/null +++ b/bindings/wasm/src/resolver/mod.rs @@ -0,0 +1,7 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +//mod function_transformation; +mod constructor_input; +mod mixed_resolver; +mod supported_document_types; diff --git a/bindings/wasm/src/resolver/supported_document_types.rs b/bindings/wasm/src/resolver/supported_document_types.rs new file mode 100644 index 0000000000..487be3ecff --- /dev/null +++ b/bindings/wasm/src/resolver/supported_document_types.rs @@ -0,0 +1,81 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use crate::did::WasmCoreDocument; +use crate::error::WasmError; +use crate::stardust::WasmStardustDocument; +use identity_iota::credential::AbstractValidatorDocument; +use identity_iota::did::CoreDocument; +use identity_stardust::StardustDocument; +use serde::Deserialize; +use wasm_bindgen::prelude::*; + +#[derive(Deserialize)] +#[serde(untagged)] +/// Temporary type used to convert to and from Box until +/// we port the Document trait to these bindings. +pub(super) enum RustSupportedDocument { + Stardust(StardustDocument), + Core(CoreDocument), +} + +impl From for JsValue { + fn from(document: RustSupportedDocument) -> Self { + match document { + RustSupportedDocument::Core(doc) => JsValue::from(WasmCoreDocument::from(doc)), + RustSupportedDocument::Stardust(doc) => JsValue::from(WasmStardustDocument(doc)), + } + } +} + +impl From for AbstractValidatorDocument { + fn from(document: RustSupportedDocument) -> Self { + match document { + RustSupportedDocument::Core(core_doc) => AbstractValidatorDocument::from(core_doc), + RustSupportedDocument::Stardust(stardust_doc) => AbstractValidatorDocument::from(stardust_doc), + } + } +} + +impl TryFrom for RustSupportedDocument { + type Error = WasmError<'static>; + fn try_from(value: AbstractValidatorDocument) -> std::result::Result { + let upcast = value.into_any(); + let supported_document = match upcast.downcast::() { + Ok(doc) => RustSupportedDocument::Core(*doc), + Err(retry) => { + if let Ok(doc) = retry.downcast::() { + RustSupportedDocument::Stardust(*doc) + } else { + Err(WasmError::new( + "CastingError".into(), + "Failed to cast the resolved did output to the required document type".into(), + ))? + } + } + }; + Ok(supported_document) + } +} + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(typescript_type = "Promise>")] + pub type PromiseArraySupportedDocument; + + #[wasm_bindgen(typescript_type = "Promise")] + pub type PromiseSupportedDocument; + + #[wasm_bindgen(typescript_type = "StardustDocument | CoreDocument")] + pub type SupportedDocument; + + #[wasm_bindgen(typescript_type = "StardustDocument | CoreDocument | undefined")] + pub type OptionSupportedDocument; + + #[wasm_bindgen(typescript_type = "Array")] + pub type ArraySupportedDocument; + + #[wasm_bindgen(typescript_type = "Array | undefined")] + pub type OptionArraySupportedDocument; + +} diff --git a/bindings/wasm/src/stardust/mod.rs b/bindings/wasm/src/stardust/mod.rs index a3c59ed462..30e67d9fae 100644 --- a/bindings/wasm/src/stardust/mod.rs +++ b/bindings/wasm/src/stardust/mod.rs @@ -1,6 +1,7 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 - +pub(crate) use identity_client::WasmStardustIdentityClient; +pub use identity_client_ext::PromiseStardustDocument; pub use stardust_did::WasmStardustDID; pub use stardust_did_url::WasmStardustDIDUrl; pub use stardust_document::WasmStardustDocument; diff --git a/bindings/wasm/tests/resolver.ts b/bindings/wasm/tests/resolver.ts new file mode 100644 index 0000000000..dd22d09f04 --- /dev/null +++ b/bindings/wasm/tests/resolver.ts @@ -0,0 +1,167 @@ +export {}; + +import { + CoreDocument, + StardustDID, + MixedResolver, + Presentation, + StardustDocument, + CoreDID, + FailFast, + PresentationValidationOptions +} from "../node"; +import assert = require("assert"); + +const presentationJSON = require("../../../identity_credential/tests/fixtures/signed_presentation/presentation.json"); +const issuerIotaDocJSON = require("../../../identity_credential/tests/fixtures/signed_presentation/issuer_iota_doc.json"); +const issuerBarDocJSON = require("../../../identity_credential/tests/fixtures/signed_presentation/issuer_bar_doc.json"); +const holderFooDocJSON = require("../../../identity_credential/tests/fixtures/signed_presentation/subject_foo_doc.json"); +const presentation = Presentation.fromJSON(presentationJSON); +const holderFooDoc = CoreDocument.fromJSON(holderFooDocJSON); +const issuerIotaDoc: StardustDocument = StardustDocument.fromJSON(issuerIotaDocJSON); +const issuerBarDoc: CoreDocument = CoreDocument.fromJSON(issuerBarDocJSON); + +describe("Resolver", function () { + describe("#verifyPresentation", function () { + it("should accept a correct presentation when configured correctly", async () => { + // mock method handlers + const resolveDidIota = async function (did_input: string) { + const parsedDid: StardustDID = StardustDID.parse(did_input); + if (issuerIotaDoc.id().toString() == parsedDid.toString()) { + return issuerIotaDoc; + } else { + throw new Error(`could not resolve did ${did_input}`); + } + }; + + const resolveDidFoo = async function (did_input: string) { + const parsedDid: CoreDID = CoreDID.parse(did_input); + if (holderFooDoc.id().toString() == parsedDid.toString()) { + return holderFooDoc; + } else { + throw new Error(`could not resolve did ${did_input}`); + } + }; + + const resolveDidBar = async function (did_input: string) { + const parsedDid: CoreDID = CoreDID.parse(did_input); + if (issuerBarDoc.id().toString() == parsedDid.toString()) { + return issuerBarDoc; + } else { + throw new Error(`could not resolve did ${did_input}`); + } + }; + + let handlerMap: Map Promise> = new Map(); + handlerMap.set("iota", resolveDidIota); + handlerMap.set("foo", resolveDidFoo); + handlerMap.set("bar", resolveDidBar); + + const resolver = new MixedResolver({ + handlers: handlerMap + }); + + const resolvedHolderDoc = await resolver.resolvePresentationHolder(presentation); + assert(resolvedHolderDoc instanceof CoreDocument); + + const resolvedIssuerDocuments = await resolver.resolvePresentationIssuers(presentation); + + assert(resolvedIssuerDocuments instanceof Array); + + let verificationResultPassingHolderDoc = await resolver.verifyPresentation( + presentation, + PresentationValidationOptions.default(), + FailFast.FirstError, + resolvedHolderDoc, + undefined + ); + assert.equal(verificationResultPassingHolderDoc, undefined); + + let verificationResultPassingHolderAndIssuerDocuments = await resolver.verifyPresentation( + presentation, + PresentationValidationOptions.default(), + FailFast.FirstError, + resolvedHolderDoc, + resolvedIssuerDocuments + ); + assert.equal(verificationResultPassingHolderAndIssuerDocuments, undefined); + + let verificationResultPassingIssuerDocuments = await resolver.verifyPresentation( + presentation, + PresentationValidationOptions.default(), + FailFast.FirstError, + undefined, + resolvedIssuerDocuments + ); + assert.equal(verificationResultPassingIssuerDocuments, undefined); + + let verificationResultPassingNoDocuments = await resolver.verifyPresentation( + presentation, + PresentationValidationOptions.default(), + FailFast.FirstError, + undefined, + undefined + ); + assert.equal(verificationResultPassingNoDocuments, undefined); + + // passing the wrong document should throw an error + assert.notEqual(resolvedHolderDoc, resolvedIssuerDocuments[0]); + + try { + let result = await resolver.verifyPresentation( + presentation, + PresentationValidationOptions.default(), + FailFast.FirstError, + resolvedIssuerDocuments[0], + undefined + ); + } catch (e) { + return; + } + throw new Error("no error thrown when passing incorrect holder"); + }); + + it("should fail presentation validation when configured incorrectly", async () => { + // setup mock handlers returning DID documents from other methods + const resolveDidIotaMisconfigured = async function (_did_input: string) { + return holderFooDoc; + }; + + const resolveDidFooMisconfigured = async function (_did_input: string) { + return issuerBarDoc; + }; + + const resolveDidBarMisconfigured = async function (did_input: string) { + return issuerIotaDoc; + }; + + let handlerMap: Map Promise> = new Map(); + handlerMap.set("iota", resolveDidIotaMisconfigured); + handlerMap.set("foo", resolveDidFooMisconfigured); + handlerMap.set("bar", resolveDidBarMisconfigured); + + const resolver = new MixedResolver({ + handlers: handlerMap + }); + + try { + await resolver.verifyPresentation( + presentation, + PresentationValidationOptions.default(), + FailFast.FirstError, + undefined, + undefined + ); + } catch (e) { + if (e instanceof Error) { + assert.equal("ResolverError::PresentationValidationError", e.name); + return; + } + } + + throw new Error( + "the incorrectly configured resolver did not throw the expected error when validating the presentation" + ); + }); + }); +}); diff --git a/examples/0_basic/2_resolve_did.rs b/examples/0_basic/2_resolve_did.rs index aa034abc43..155a90c29e 100644 --- a/examples/0_basic/2_resolve_did.rs +++ b/examples/0_basic/2_resolve_did.rs @@ -4,6 +4,7 @@ use examples::create_did; use examples::random_stronghold_path; use examples::NETWORK_ENDPOINT; +use identity_resolver::Resolver; use identity_stardust::block::address::Address; use identity_stardust::StardustDID; use identity_stardust::StardustDocument; @@ -26,12 +27,26 @@ async fn main() -> anyhow::Result<()> { .build(random_stronghold_path())?, ); - // Create a new DID in an Alias Output for us to modify. + // Create a new DID in an Alias Output for us to resolve. let (_, did): (Address, StardustDID) = create_did(&client, &mut secret_manager).await?; + // We can resolve a `StardustDID` with the client itself. // Resolve the associated Alias Output and extract the DID document from it. - let resolved: StardustDocument = client.resolve_did(&did).await?; - println!("Resolved DID Document: {:#}", resolved); + let client_document: StardustDocument = client.resolve_did(&did).await?; + println!("Client resolved DID Document: {:#}", client_document); + + // We can also create a `Resolver` that has additional convenience methods, + // for example to resolve presentation issuers or to verify presentations. + let mut resolver = Resolver::::new(); + + // We need to register a handler that can resolve IOTA DIDs. + // This convenience method only requires us to provide a client. + resolver.attach_iota_handler(client.clone()); + + let resolver_document: StardustDocument = resolver.resolve(&did).await.unwrap(); + + // Client and Resolver resolve to the same document in this case. + assert_eq!(client_document, resolver_document); // We can also resolve the Alias Output directly. let alias_output: AliasOutput = client.resolve_did_output(&did).await?; diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 863a1e157a..28bc00a478 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -9,6 +9,7 @@ publish = false anyhow = "1.0.62" identity_core = { path = "../identity_core" } identity_did = { path = "../identity_did" } +identity_resolver = { path = "../identity_resolver" } identity_stardust = { path = "../identity_stardust" } iota-client = { version = "2.0.0-beta.3", default-features = false, features = ["tls", "stronghold"] } primitive-types = "0.11.1" diff --git a/identity_credential/src/validator/mod.rs b/identity_credential/src/validator/mod.rs index f494dc2bdf..bb7800b05f 100644 --- a/identity_credential/src/validator/mod.rs +++ b/identity_credential/src/validator/mod.rs @@ -14,6 +14,8 @@ pub use self::validation_options::FailFast; pub use self::validation_options::PresentationValidationOptions; pub use self::validation_options::StatusCheck; pub use self::validation_options::SubjectHolderRelationship; +pub use self::validator_document::AbstractThreadSafeValidatorDocument; +pub use self::validator_document::AbstractValidatorDocument; pub use self::validator_document::ValidatorDocument; mod credential_validator; diff --git a/identity_credential/src/validator/validator_document.rs b/identity_credential/src/validator/validator_document.rs index 01ff3d4abe..a40a28bd71 100644 --- a/identity_credential/src/validator/validator_document.rs +++ b/identity_credential/src/validator/validator_document.rs @@ -1,6 +1,8 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use std::any::Any; + use identity_core::crypto::GetSignature; use identity_did::did::DID; use identity_did::document::Document; @@ -14,9 +16,13 @@ use self::private::Verifiable; /// Abstraction over DID Documents for validating presentations and credentials. /// /// NOTE: this is a sealed trait and not intended to be used externally or implemented manually. -/// A blanket implementation is provided for the [`Document`] trait, which can be implemented -/// instead to be compatible. Any changes to this trait will be considered non-breaking. -pub trait ValidatorDocument: Sealed { +/// A blanket implementation is provided for all types implementing the [`Document`] and [`Debug`](core::fmt::Debug) +/// traits, which is recommended to be implemented instead to be compatible. +/// +/// # Warning +/// +/// Any changes to this trait will be considered non-breaking. +pub trait ValidatorDocument: Sealed + core::fmt::Debug { /// Convenience function for casting self to the trait. /// /// Equivalent to `self as &dyn ValidatorDocument`. @@ -27,9 +33,16 @@ pub trait ValidatorDocument: Sealed { self as &dyn ValidatorDocument } + #[doc(hidden)] + fn upcast(self: Box) -> Box + where + Self: 'static; + + #[doc(hidden)] /// Returns the string identifier of the DID Document. fn did_str(&self) -> &str; + #[doc(hidden)] /// Verifies the signature of the provided data against the DID Document. /// /// # Errors @@ -38,6 +51,7 @@ pub trait ValidatorDocument: Sealed { /// serialization fails, or the verification operation fails. fn verify_data(&self, data: &dyn Verifiable, options: &VerifierOptions) -> identity_did::Result<()>; + #[doc(hidden)] /// Extracts the `RevocationBitmap` from the referenced service in the DID Document. /// /// # Errors @@ -58,6 +72,8 @@ mod private { impl Sealed for T where T: Document {} impl Sealed for &dyn ValidatorDocument {} + impl Sealed for AbstractValidatorDocument {} + impl Sealed for AbstractThreadSafeValidatorDocument {} /// Object-safe trait workaround to satisfy the trait bounds /// [`serde::Serialize`] + [`GetSignature`]. @@ -83,6 +99,12 @@ impl ValidatorDocument for &dyn ValidatorDocument { fn verify_data(&self, data: &dyn Verifiable, options: &VerifierOptions) -> identity_did::Result<()> { (*self).verify_data(data, options) } + fn upcast(self: Box) -> Box + where + Self: 'static, + { + self + } #[cfg(feature = "revocation-bitmap")] fn resolve_revocation_bitmap( @@ -95,7 +117,7 @@ impl ValidatorDocument for &dyn ValidatorDocument { impl ValidatorDocument for DOC where - DOC: Document, + DOC: Document + core::fmt::Debug, { fn did_str(&self) -> &str { self.id().as_str() @@ -105,6 +127,13 @@ where self.verify_data(data, options).map_err(Into::into) } + fn upcast(self: Box) -> Box + where + Self: 'static, + { + self + } + #[cfg(feature = "revocation-bitmap")] fn resolve_revocation_bitmap( &self, @@ -118,3 +147,125 @@ where .and_then(RevocationBitmap::try_from) } } + +/// An abstract implementer of [`ValidatorDocument`] that all implementers of [`Document`] can be converted to. +/// +/// By calling [`Self::into_any`](Self::into_any()) one obtains a type that one may +/// attempt to convert to a concrete DID Document representation. +#[derive(Debug)] +pub struct AbstractValidatorDocument(Box); + +impl AbstractValidatorDocument { + /// See [AbstractThreadSafeValidatorDocument::into_any](AbstractThreadSafeValidatorDocument::into_any()). + pub fn into_any(self) -> Box { + self.0.upcast() + } +} + +impl From for AbstractValidatorDocument { + fn from(doc: DOC) -> Self { + AbstractValidatorDocument(Box::new(doc) as Box) + } +} + +impl ValidatorDocument for AbstractValidatorDocument { + fn did_str(&self) -> &str { + self.0.did_str() + } + + fn verify_data(&self, data: &dyn Verifiable, options: &VerifierOptions) -> identity_did::Result<()> { + self.0.verify_data(data, options) + } + + fn upcast(self: Box) -> Box + where + Self: 'static, + { + self.into_any() + } + + #[cfg(feature = "revocation-bitmap")] + fn resolve_revocation_bitmap( + &self, + query: identity_did::utils::DIDUrlQuery<'_>, + ) -> identity_did::Result { + self.0.resolve_revocation_bitmap(query) + } +} + +trait ThreadSafeValidatorDocument: ValidatorDocument + Send + Sync { + fn thread_safe_upcast(self: Box) -> Box + where + Self: 'static; +} + +impl ThreadSafeValidatorDocument for DOC +where + DOC: ValidatorDocument + Send + Sync + 'static, +{ + fn thread_safe_upcast(self: Box) -> Box + where + Self: 'static, + { + self + } +} + +/// Thread safe variant of [`AbstractValidatorDocument`]. +/// +/// By calling [`Self::into_any`](Self::into_any()) one obtains a type that one may +/// attempt to downcast to a concrete DID Document representation. +#[derive(Debug)] +pub struct AbstractThreadSafeValidatorDocument(Box); + +impl From for AbstractThreadSafeValidatorDocument +where + DOC: Document + core::fmt::Debug + Send + Sync + 'static, +{ + fn from(doc: DOC) -> Self { + Self(Box::new(doc) as Box) + } +} + +impl AbstractThreadSafeValidatorDocument { + /// Convert the abstract document into [`Any`] which one may then attempt to cast to a concrete type. + /// + /// # Example + /// ``` + /// # use identity_did::document::CoreDocument; + /// # use identity_credential::validator::AbstractValidatorDocument; + /// + /// fn round_trip(doc: CoreDocument) -> CoreDocument { + /// let abstract_doc = AbstractValidatorDocument::from(doc); + /// *abstract_doc.into_any().downcast::().unwrap() + /// } + /// ``` + pub fn into_any(self) -> Box { + self.0.thread_safe_upcast() + } +} + +impl ValidatorDocument for AbstractThreadSafeValidatorDocument { + fn verify_data(&self, data: &dyn Verifiable, options: &VerifierOptions) -> identity_did::Result<()> { + self.0.verify_data(data, options) + } + + fn upcast(self: Box) -> Box + where + Self: 'static, + { + self.0.upcast() + } + + fn did_str(&self) -> &str { + self.0.did_str() + } + + #[cfg(feature = "revocation-bitmap")] + fn resolve_revocation_bitmap( + &self, + query: identity_did::utils::DIDUrlQuery<'_>, + ) -> identity_did::Result { + self.0.resolve_revocation_bitmap(query) + } +} diff --git a/identity_credential/tests/fixtures/signed_presentation/README.md b/identity_credential/tests/fixtures/signed_presentation/README.md new file mode 100644 index 0000000000..32fbd6b334 --- /dev/null +++ b/identity_credential/tests/fixtures/signed_presentation/README.md @@ -0,0 +1,3 @@ +This directory contains a serialized representation of a valid signed presentation and also serialized DID documents of the presentation holder and credential issuers. + +The intention is that the serialized data can be used to test (valid) presentation validation in multiple languages without having to write a full setup for each test. \ No newline at end of file diff --git a/identity_credential/tests/fixtures/signed_presentation/issuer_bar_doc.json b/identity_credential/tests/fixtures/signed_presentation/issuer_bar_doc.json new file mode 100644 index 0000000000..8869d26bc4 --- /dev/null +++ b/identity_credential/tests/fixtures/signed_presentation/issuer_bar_doc.json @@ -0,0 +1,11 @@ +{ + "id": "did:bar:Hyx62wPQGyvXCoihZq1BrbUjBRh2LuNxWiiqMkfAuSZr", + "verificationMethod": [ + { + "id": "did:bar:Hyx62wPQGyvXCoihZq1BrbUjBRh2LuNxWiiqMkfAuSZr#root", + "controller": "did:bar:Hyx62wPQGyvXCoihZq1BrbUjBRh2LuNxWiiqMkfAuSZr", + "type": "Ed25519VerificationKey2018", + "publicKeyMultibase": "zHyx62wPQGyvXCoihZq1BrbUjBRh2LuNxWiiqMkfAuSZr" + } + ] + } \ No newline at end of file diff --git a/identity_credential/tests/fixtures/signed_presentation/issuer_iota_doc.json b/identity_credential/tests/fixtures/signed_presentation/issuer_iota_doc.json new file mode 100644 index 0000000000..7ff91ad52d --- /dev/null +++ b/identity_credential/tests/fixtures/signed_presentation/issuer_iota_doc.json @@ -0,0 +1,17 @@ +{ + "doc": { + "id": "did:iota:0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "verificationMethod": [ + { + "id": "did:iota:0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA#issuerKey", + "controller": "did:iota:0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "type": "Ed25519VerificationKey2018", + "publicKeyMultibase": "zFVen3X669xLzsi6N2V91DoiyzHzg1uAgqiT8jZ9nS96Z" + } + ] + }, + "meta": { + "created": "2022-08-31T09:33:31Z", + "updated": "2022-08-31T09:33:31Z" + } + } \ No newline at end of file diff --git a/identity_credential/tests/fixtures/signed_presentation/presentation.json b/identity_credential/tests/fixtures/signed_presentation/presentation.json new file mode 100644 index 0000000000..a379ebcf2d --- /dev/null +++ b/identity_credential/tests/fixtures/signed_presentation/presentation.json @@ -0,0 +1,64 @@ +{ + "@context": "https://www.w3.org/2018/credentials/v1", + "id": "https://example.org/credentials/3732", + "type": "VerifiablePresentation", + "verifiableCredential": [ + { + "@context": "https://www.w3.org/2018/credentials/v1", + "id": "https://example.edu/credentials/3732", + "type": [ + "VerifiableCredential", + "UniversityDegreeCredential" + ], + "credentialSubject": { + "id": "did:foo:586Z7H2vpX9qNhN2T4e9Utugie3ogjbxzGaMtM3E6HR5", + "GPA": "4.0", + "degree": { + "name": "Bachelor of Science and Arts", + "type": "BachelorDegree" + }, + "name": "Alice" + }, + "issuer": "did:iota:0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "issuanceDate": "2022-08-31T08:35:44Z", + "expirationDate": "2050-09-01T08:35:44Z", + "proof": { + "type": "JcsEd25519Signature2020", + "verificationMethod": "did:iota:0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA#issuerKey", + "signatureValue": "3d2aAPqjzaSQ2XbFtqLsauv2Ukdn4Hcevz2grNuJn4q4JbBmDHZpAvekVG12A3ZKRRTeKaBPguxXqcDaqujckWWz" + } + }, + { + "@context": "https://www.w3.org/2018/credentials/v1", + "id": "https://example.edu/credentials/3732", + "type": [ + "VerifiableCredential", + "UniversityDegreeCredential" + ], + "credentialSubject": { + "id": "did:foo:586Z7H2vpX9qNhN2T4e9Utugie3ogjbxzGaMtM3E6HR5", + "GPA": "4.0", + "degree": { + "name": "Bachelor of Science and Arts", + "type": "BachelorDegree" + }, + "name": "Alice" + }, + "issuer": "did:bar:Hyx62wPQGyvXCoihZq1BrbUjBRh2LuNxWiiqMkfAuSZr", + "issuanceDate": "2022-08-31T08:35:44Z", + "expirationDate": "2050-09-01T08:35:44Z", + "proof": { + "type": "JcsEd25519Signature2020", + "verificationMethod": "did:bar:Hyx62wPQGyvXCoihZq1BrbUjBRh2LuNxWiiqMkfAuSZr#root", + "signatureValue": "2iAYujqHLXP5csZzabdkfurpHaKT3Q8dnJDA4TL7pSJ7gjXLCb2tN7CF4ztKkCKmvY6VYG3pTuN1PeLGEFiQvuQr" + } + } + ], + "holder": "did:foo:586Z7H2vpX9qNhN2T4e9Utugie3ogjbxzGaMtM3E6HR5", + "proof": { + "type": "JcsEd25519Signature2020", + "verificationMethod": "did:foo:586Z7H2vpX9qNhN2T4e9Utugie3ogjbxzGaMtM3E6HR5#root", + "signatureValue": "3tVeoKjftrEAQvV3MgpKgiydRHU6i8mYVRnPc6C85upo1TDBEdN94gyW1RzbgPESaZCGeDa582BxAUHVE4rVjaAd", + "challenge": "475a7984-1bb5-4c4c-a56f-822bccd46441" + } + } \ No newline at end of file diff --git a/identity_credential/tests/fixtures/signed_presentation/secret_keys.rs b/identity_credential/tests/fixtures/signed_presentation/secret_keys.rs new file mode 100644 index 0000000000..a29af1508b --- /dev/null +++ b/identity_credential/tests/fixtures/signed_presentation/secret_keys.rs @@ -0,0 +1,18 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// Not used, but can be useful for maintenance purposes. +const HOLDER_PRIVATE_KEY: [u8; 32] = [ + 76, 205, 8, 155, 40, 255, 150, 218, 157, 182, 195, 70, 236, 17, 78, 15, 91, 138, 49, 159, 53, 171, 166, 36, 218, 140, + 246, 237, 79, 184, 166, 251, +]; + +const ISSUER_IOTA_PRIVATE_KEY: [u8; 32] = [ + 157, 97, 177, 157, 239, 253, 90, 96, 186, 132, 74, 244, 146, 236, 44, 196, 68, 73, 197, 105, 123, 50, 105, 25, 112, + 59, 172, 3, 28, 174, 127, 96, +]; + +const ISSUER_BAR_PRIVATE_KEY: [u8; 32] = [ + 197, 170, 141, 244, 63, 159, 131, 123, 237, 183, 68, 47, 49, 220, 183, 177, 102, 211, 133, 53, 7, 111, 9, 75, 133, + 206, 58, 46, 11, 68, 88, 247, +]; \ No newline at end of file diff --git a/identity_credential/tests/fixtures/signed_presentation/subject_foo_doc.json b/identity_credential/tests/fixtures/signed_presentation/subject_foo_doc.json new file mode 100644 index 0000000000..e47ed33034 --- /dev/null +++ b/identity_credential/tests/fixtures/signed_presentation/subject_foo_doc.json @@ -0,0 +1,11 @@ +{ + "id": "did:foo:586Z7H2vpX9qNhN2T4e9Utugie3ogjbxzGaMtM3E6HR5", + "verificationMethod": [ + { + "id": "did:foo:586Z7H2vpX9qNhN2T4e9Utugie3ogjbxzGaMtM3E6HR5#root", + "controller": "did:foo:586Z7H2vpX9qNhN2T4e9Utugie3ogjbxzGaMtM3E6HR5", + "type": "Ed25519VerificationKey2018", + "publicKeyMultibase": "z586Z7H2vpX9qNhN2T4e9Utugie3ogjbxzGaMtM3E6HR5" + } + ] + } \ No newline at end of file diff --git a/identity_did/src/did/did.rs b/identity_did/src/did/did.rs index c4f2bc4360..021db62863 100644 --- a/identity_did/src/did/did.rs +++ b/identity_did/src/did/did.rs @@ -17,7 +17,7 @@ use identity_core::diff::DiffString; use crate::did::DIDError; use crate::did::DIDUrl; -pub trait DID: Clone + PartialEq + Eq + PartialOrd + Ord + Hash + FromStr + TryFrom { +pub trait DID: Clone + PartialEq + Eq + PartialOrd + Ord + Hash + FromStr + TryFrom + Into { const SCHEME: &'static str = BaseDIDUrl::SCHEME; /// Returns the [`DID`] scheme. See [`DID::SCHEME`]. @@ -164,11 +164,11 @@ impl DID for CoreDID { } fn into_string(self) -> String { - self.0.into_string() + >::into(self) } fn join(self, value: impl AsRef) -> Result, DIDError> { - DIDUrl::new(self, None).join(value) + self.into_url().join(value) } fn to_url(&self) -> DIDUrl { @@ -238,7 +238,7 @@ impl TryFrom for CoreDID { impl From for String { fn from(did: CoreDID) -> Self { - did.into_string() + did.0.into_string() } } @@ -300,6 +300,48 @@ pub(crate) const fn is_char_method_id(ch: char) -> bool { matches!(ch, '0'..='9' | 'a'..='z' | 'A'..='Z' | '.' | '-' | '_' | ':') } +impl DID for D +where + D: AsRef + Into + FromStr + Hash + Ord + Eq + PartialEq + Clone + TryFrom, +{ + fn as_str(&self) -> &str { + self.as_ref().as_str() + } + + fn authority(&self) -> &str { + self.as_ref().authority() + } + + fn into_string(self) -> String { + >::into(self) + } + + fn into_url(self) -> DIDUrl { + DIDUrl::new(self, None) + } + + fn method(&self) -> &str { + self.as_ref().method() + } + + fn method_id(&self) -> &str { + self.as_ref().method_id() + } + fn scheme(&self) -> &'static str { + self.as_ref().scheme() + } + + fn to_url(&self) -> DIDUrl { + DIDUrl::new(self.clone(), None) + } + fn join(self, value: impl AsRef) -> Result, DIDError> + where + Self: Sized, + { + self.into_url().join(value) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/identity_iota/Cargo.toml b/identity_iota/Cargo.toml index a86366d2a7..8d594979da 100644 --- a/identity_iota/Cargo.toml +++ b/identity_iota/Cargo.toml @@ -21,6 +21,7 @@ identity_credential = { version = "=0.6.0", path = "../identity_credential", def identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } identity_iota_client = { version = "=0.6.0", path = "../identity_iota_client", default-features = false } identity_iota_core = { version = "=0.6.0", path = "../identity_iota_core", default-features = false } +identity_resolver = { version = "=0.6.0", path = "../identity_resolver", default-features = false } [dev-dependencies] criterion = { version = "0.3" } @@ -42,6 +43,8 @@ stronghold = ["identity_account/stronghold", "identity_account_storage/stronghol # Enables support for DID Communication # comm = ["identity_comm"] +# TODO: After merging the refactor (#1000), add "identity_resolver/iota" to the `iota-client` feature. + # Enables `Send` + `Sync` bounds for the Storage trait. send-sync-storage = ["identity_account_storage/send-sync-storage"] @@ -51,6 +54,7 @@ storage-test-suite = ["identity_account_storage/storage-test-suite"] # Enables revocation with `RevocationBitmap2022`. revocation-bitmap = [ "identity_account?/revocation-bitmap", + "identity_resolver/revocation-bitmap", "identity_iota_client/revocation-bitmap", "identity_credential/revocation-bitmap", ] diff --git a/identity_iota/src/lib.rs b/identity_iota/src/lib.rs index 9b79dc78e2..8aeb22d067 100644 --- a/identity_iota/src/lib.rs +++ b/identity_iota/src/lib.rs @@ -139,6 +139,13 @@ pub mod prelude { pub use identity_iota_client::tangle::Client; pub use identity_iota_client::Result; pub use identity_iota_core::document::IotaDocument; + pub use identity_resolver::Resolver; +} + +pub mod resolver { + //! DID resolution utilities + + pub use identity_resolver::*; } #[cfg(feature = "unstable-agent")] diff --git a/identity_iota_core/src/did/iota_did.rs b/identity_iota_core/src/did/iota_did.rs index c8a456e0d6..9f2ccd6b86 100644 --- a/identity_iota_core/src/did/iota_did.rs +++ b/identity_iota_core/src/did/iota_did.rs @@ -209,57 +209,6 @@ impl IotaDID { } } -impl DID for IotaDID { - /// Returns the [`IotaDID`] scheme. See [`DID::SCHEME`]. - fn scheme(&self) -> &'static str { - self.0.scheme() - } - - /// Returns the [`IotaDID`] authority. - fn authority(&self) -> &str { - self.0.authority() - } - - /// Returns the [`IotaDID`] method name. - fn method(&self) -> &str { - self.0.method() - } - - /// Returns the [`IotaDID`] method-specific ID. - fn method_id(&self) -> &str { - self.0.method_id() - } - - /// Returns the serialized [`IotaDID`]. - /// - /// This is fast since the serialized value is stored in the [`DID`]. - fn as_str(&self) -> &str { - self.0.as_str() - } - - /// Consumes the [`IotaDID`] and returns the serialization. - fn into_string(self) -> String { - self.0.into_string() - } - - /// Creates a new [`IotaDIDUrl`] by joining with a relative DID Url string. - /// - /// # Errors - /// - /// Returns `Err` if any base or relative DID segments are invalid. - fn join(self, segment: impl AsRef) -> std::result::Result, DIDError> { - self.into_url().join(segment) - } - - fn to_url(&self) -> DIDUrl { - DIDUrl::new(self.clone(), None) - } - - fn into_url(self) -> DIDUrl { - DIDUrl::new(self, None) - } -} - impl Display for IotaDID { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { write!(f, "{}", self.0) diff --git a/identity_resolver/Cargo.toml b/identity_resolver/Cargo.toml new file mode 100644 index 0000000000..ca8f1ca365 --- /dev/null +++ b/identity_resolver/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "identity_resolver" +version = "0.6.0" +authors = ["IOTA Stiftung"] +edition = "2021" +homepage = "https://www.iota.org" +keywords = ["iota", "did", "identity", "resolver", "resolution"] +license = "Apache-2.0" +readme = "../README.md" +repository = "https://github.com/iotaledger/identity.rs" +rust-version = "1.62" +description = "DID Resolution utilities for the identity.rs library." + +[dependencies] +# This is currently necessary for the ResolutionHandler trait. This can be made an optional dependency if alternative ways of attaching handlers are introduced. +async-trait = { version = "0.1", default-features = false } +futures = { version = "0.3" } +identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } +identity_credential = { version = "=0.6.0", path = "../identity_credential", default-features = false, features = ["validator"] } +identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } +serde = { version = "1.0", default-features = false, features = ["std", "derive"] } +strum = { version = "0.21", features = ["derive"] } +thiserror = { version = "1.0", default-features = false } + +[dependencies.identity_stardust] +version = "=0.6.0" +path = "../identity_stardust" +default-features = false +features = ["send-sync-client-ext", "iota-client"] +optional = true + +[dev-dependencies] +iota-client = { version = "2.0.0-beta.3", default-features = false, features = ["tls"] } +tokio = { version = "1.17.0", default-features = false, features = ["rt-multi-thread", "macros"] } + +[features] +default = ["revocation-bitmap", "iota"] +revocation-bitmap = ["identity_did/revocation-bitmap", "identity_stardust?/revocation-bitmap"] +# Enables the IOTA integration for the resolver. +iota = ["dep:identity_stardust"] diff --git a/identity_resolver/src/error.rs b/identity_resolver/src/error.rs new file mode 100644 index 0000000000..45090e0d14 --- /dev/null +++ b/identity_resolver/src/error.rs @@ -0,0 +1,129 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::borrow::Cow; + +pub type Result = core::result::Result; + +/// Error returned from the [Resolver's](crate::Resolver) methods. +/// +/// The [`Self::error_cause`](Self::error_cause()) method provides information about the cause of the error, +/// while [`Self::action`](Self::action()) provides more context about the action the resolver was carrying out when the +/// error occurred. +#[derive(Debug)] +pub struct Error { + error_cause: ErrorCause, + action: Option, +} + +impl Error { + pub(crate) fn new(cause: ErrorCause) -> Self { + Self { + error_cause: cause, + action: None, + } + } + + /// Returns the cause of the error. + pub fn error_cause(&self) -> &ErrorCause { + &self.error_cause + } + + /// Converts the error into [`ErrorCause`]. + pub fn into_error_cause(self) -> ErrorCause { + self.error_cause + } + + /// Returns more context regarding the action the [`Resolver`](crate::Resolver) was performing when the error occurred + /// if available. + /// + /// This is mainly useful when the error originated from calling + /// [Resolver::verify_presentation](crate::Resolver::verify_presentation()) as one may then want to know answers to + /// questions of the form: did the problem occur when attempting to resolve the holder's DID, or was there perhaps a + /// problem when resolving the DID of a certain credential issuer?. + pub fn action(&self) -> Option { + self.action + } + + /// Replaces the value of the [`ResolutionAction`], but leaves the category untouched. + pub(crate) fn resolution_action(mut self, action: ResolutionAction) -> Self { + self.action = Some(action); + self + } +} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(action) = self.action { + write!(f, "{}: {}", self.error_cause, action) + } else { + write!(f, "{}", self.error_cause) + } + } +} + +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + self.error_cause.source() + } +} + +/// Error failure modes associated with the methods on the [Resolver's](crate::Resolver). +/// +/// NOTE: This is a "read only error" in the sense that it can only be constructed by the methods in this crate. +#[derive(Debug, thiserror::Error, strum::IntoStaticStr)] +#[non_exhaustive] +pub enum ErrorCause { + /// Caused by one or more failures when validating a presentation. + #[error("presentation validation failed")] + #[non_exhaustive] + PresentationValidationError { + source: identity_credential::validator::CompoundPresentationValidationError, + }, + /// Caused by a failure to parse a DID string during DID resolution. + #[error("did resolution failed: could not parse the given did")] + #[non_exhaustive] + DIDParsingError { + source: Box, + }, + /// A handler attached to the [`Resolver`](crate::resolution::Resolver) attempted to resolve the DID, but the + /// resolution did not succeed. + #[error("did resolution failed: the attached handler failed")] + #[non_exhaustive] + HandlerError { + source: Box, + }, + /// Caused by attempting to resolve a DID whose method does not have a corresponding handler attached to the + /// [`Resolver`](crate::resolution::Resolver). + #[error("did resolution failed: the DID method \"{method}\" is not supported by the resolver")] + UnsupportedMethodError { method: String }, +} + +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +#[non_exhaustive] +/// Indicates the action the [`Resolver`](crate::resolution::Resolver) was performing when an error ocurred. +pub enum ResolutionAction { + /// Errored while attempting to resolve a presentation holder's DID. + PresentationHolderResolution, + /// Errored while attempting to resolve the DIDs of the credential issuers of the given presentation. + /// + /// The wrapped `usize` indicates the position of a credential whose issuer's DID could not be resolved. + PresentationIssuersResolution(usize), +} + +impl std::fmt::Display for ResolutionAction { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let message: Cow = match self { + ResolutionAction::PresentationHolderResolution => { + "attempt to resolve the presentation holder's DID failed".into() + } + ResolutionAction::PresentationIssuersResolution(idx) => format!( + "attempt to resolve the credential issuer's DID of credential no. {} in the presentation failed", + idx + ) + .into(), + }; + + write!(f, "{}", message) + } +} diff --git a/identity_resolver/src/lib.rs b/identity_resolver/src/lib.rs new file mode 100644 index 0000000000..b3511c6cb6 --- /dev/null +++ b/identity_resolver/src/lib.rs @@ -0,0 +1,11 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +mod error; +mod resolution; + +pub use self::error::Error; +pub use self::error::ErrorCause; +pub use self::error::ResolutionAction; +pub use self::error::Result; +pub use resolution::*; diff --git a/identity_resolver/src/resolution/commands.rs b/identity_resolver/src/resolution/commands.rs new file mode 100644 index 0000000000..8a53e1d261 --- /dev/null +++ b/identity_resolver/src/resolution/commands.rs @@ -0,0 +1,139 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use core::future::Future; +use identity_credential::validator::ValidatorDocument; +use identity_did::did::DID; + +use crate::Error; +use crate::ErrorCause; +use crate::Result; +use std::pin::Pin; + +/// Internal trait used by the resolver to apply the command pattern. +/// +/// The resolver is generic over the type of command which enables +/// support for both multi-threaded and single threaded use cases. +pub trait Command<'a, T>: std::fmt::Debug + private::Sealed { + type Output: Future + 'a; + fn apply(&self, input: &'a str) -> Self::Output; +} + +mod private { + use super::SendSyncCommand; + use super::SingleThreadedCommand; + use identity_credential::validator::ValidatorDocument; + pub trait Sealed {} + impl Sealed for SendSyncCommand {} + impl Sealed for SingleThreadedCommand {} +} + +impl std::fmt::Debug for SendSyncCommand { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("") + } +} + +impl std::fmt::Debug for SingleThreadedCommand { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("") + } +} + +/// Internal representation of a thread safe handler. +type SendSyncCallback = + Box Fn(&'r str) -> Pin> + 'r + Send>> + Send + Sync>; + +/// Wrapper around a thread safe callback. +pub struct SendSyncCommand { + fun: SendSyncCallback, +} + +impl<'a, DOC: ValidatorDocument + 'static> Command<'a, Result> for SendSyncCommand { + type Output = Pin> + 'a + Send>>; + fn apply(&self, input: &'a str) -> Self::Output { + (self.fun)(input) + } +} + +impl SendSyncCommand { + /// Converts a handler represented as a closure to a command. + /// + /// This is achieved by first producing a callback represented as a dynamic asynchronous function pointer + /// which is invoked by the [Resolver](crate::Resolver) at a later point. + /// When the callback is invoked the `Resolver` will then pass a DID represented as a string slice which is then + /// converted to the DID type required by the handler and then the handler is called. + pub(super) fn new(handler: F) -> Self + where + D: DID + Send + for<'r> TryFrom<&'r str, Error = DIDERR> + 'static, + DOCUMENT: 'static + Into, + F: Fn(D) -> Fut + 'static + Clone + Send + Sync, + Fut: Future> + Send, + E: Into>, + DIDERR: Into>, + { + let fun: SendSyncCallback = Box::new(move |input: &str| { + let handler_clone = handler.clone(); + let did_parse_attempt = D::try_from(input) + .map_err(|error| ErrorCause::DIDParsingError { source: error.into() }) + .map_err(Error::new); + Box::pin(async move { + let did: D = did_parse_attempt?; + handler_clone(did) + .await + .map(Into::into) + .map_err(|error| ErrorCause::HandlerError { source: error.into() }) + .map_err(Error::new) + }) + }); + Self { fun } + } +} + +// =========================================================================== +// Single threaded commands +// =========================================================================== + +/// Internal representation of a single threaded handler. +pub(super) type SingleThreadedCallback = + Box Fn(&'r str) -> Pin> + 'r>>>; + +/// Wrapper around a single threaded callback. +pub struct SingleThreadedCommand { + fun: SingleThreadedCallback, +} +impl<'a, DOC: ValidatorDocument + 'static> Command<'a, Result> for SingleThreadedCommand { + type Output = Pin> + 'a>>; + fn apply(&self, input: &'a str) -> Self::Output { + (self.fun)(input) + } +} + +impl SingleThreadedCommand { + /// Equivalent to [`SendSyncCommand::new`](SendSyncCommand::new()), but with less `Send` + `Sync` bounds. + pub(super) fn new(handler: F) -> Self + where + D: DID + for<'r> TryFrom<&'r str, Error = DIDERR> + 'static, + DOCUMENT: 'static + Into, + F: Fn(D) -> Fut + 'static + Clone, + Fut: Future>, + E: Into>, + DIDERR: Into>, + { + let fun: SingleThreadedCallback = Box::new(move |input: &str| { + let handler_clone = handler.clone(); + let did_parse_attempt = D::try_from(input) + .map_err(|error| ErrorCause::DIDParsingError { source: error.into() }) + .map_err(Error::new); + Box::pin(async move { + let did: D = did_parse_attempt?; + handler_clone(did) + .await + .map(Into::into) + .map_err(|error| ErrorCause::HandlerError { source: error.into() }) + .map_err(Error::new) + }) + }); + Self { fun } + } +} diff --git a/identity_resolver/src/resolution/mod.rs b/identity_resolver/src/resolution/mod.rs new file mode 100644 index 0000000000..5cdca3efd3 --- /dev/null +++ b/identity_resolver/src/resolution/mod.rs @@ -0,0 +1,12 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 +mod commands; +mod resolver; +#[cfg(test)] +mod tests; + +use self::commands::SingleThreadedCommand; +use identity_credential::validator::AbstractValidatorDocument; +pub use resolver::Resolver; +/// Alias for a [`Resolver`] that is not [`Send`] + [`Sync`]. +pub type SingleThreadedResolver = Resolver>; diff --git a/identity_resolver/src/resolution/resolver.rs b/identity_resolver/src/resolution/resolver.rs new file mode 100644 index 0000000000..12fb9928fd --- /dev/null +++ b/identity_resolver/src/resolution/resolver.rs @@ -0,0 +1,449 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use core::future::Future; +use std::collections::HashMap; +use std::marker::PhantomData; + +use futures::TryFutureExt; +use identity_credential::credential::Credential; +use identity_credential::presentation::Presentation; +use identity_credential::validator::AbstractThreadSafeValidatorDocument; +use identity_credential::validator::CredentialValidator; +use identity_credential::validator::FailFast; +use identity_credential::validator::PresentationValidationOptions; +use identity_credential::validator::PresentationValidator; +use identity_credential::validator::ValidatorDocument; +use identity_did::did::CoreDID; +use identity_did::did::DID; + +use serde::Serialize; + +use crate::Error; +use crate::ErrorCause; + +use crate::Result; + +use super::commands::Command; +use super::commands::SendSyncCommand; +use super::commands::SingleThreadedCommand; + +/// Convenience type for resolving DID documents from different DID methods. +/// +/// Also provides methods for resolving DID Documents associated with +/// verifiable [`Credentials`][Credential] and [`Presentations`][Presentation]. +/// +/// # Configuration +/// The resolver will only be able to resolve DID documents for methods it has been configured for. This is done by +/// attaching method specific handlers with [`Self::attach_handler`](Self::attach_handler()). +pub struct Resolver> +where + CMD: for<'r> Command<'r, Result>, + DOC: ValidatorDocument, +{ + command_map: HashMap, + _required: PhantomData, +} + +impl Resolver +where + M: for<'r> Command<'r, Result>, + DOC: ValidatorDocument, +{ + /// Constructs a new [`Resolver`]. + /// + /// # Example + /// Construct a `Resolver` that resolves DID documents of type + /// [`CoreDocument`](::identity_did::document::CoreDocument). + /// ``` + /// # use identity_resolver::Resolver; + /// # use identity_did::document::CoreDocument; + /// + /// let mut resolver = Resolver::::new(); + /// // Now attach some handlers whose output can be converted to a `CoreDocument`. + /// ``` + /// + /// # Example + /// Construct a `Resolver` that is agnostic about DID Document types. + /// ``` + /// # use identity_resolver::Resolver; + /// let mut resolver: Resolver = Resolver::new(); + /// // Now attach some handlers whose output type implements the `Document` trait. + /// ``` + pub fn new() -> Self { + Self { + command_map: HashMap::new(), + _required: PhantomData::, + } + } + + /// Fetches the DID Document of the given DID. + /// + /// # Errors + /// Errors if the resolver has not been configured to handle the method corresponding to the given DID or the + /// resolution process itself fails. + /// + /// # When the return type is abstract + /// If the resolver has been constructed without specifying the return type (in [`Self::new`](Self::new())) + /// one can call [`into_any`](AbstractThreadSafeValidatorDocument::into_any()) on the resolved output to obtain a + /// [`Box`] which one may then attempt to downcast to a concrete type. The downcast will succeed if the + /// specified type matches the return type of the attached handler responsible for resolving the given DID. + /// + /// ## Example + /// ``` + /// # use identity_resolver::Resolver; + /// # use identity_did::did::CoreDID; + /// # use identity_did::did::DID; + /// # use identity_did::document::CoreDocument; + /// + /// async fn resolve_and_cast( + /// did: CoreDID, + /// ) -> std::result::Result, Box> { + /// let resolver: Resolver = configure_resolver(Resolver::new()); + /// let abstract_doc = resolver.resolve(&did).await?; + /// let document: Option = abstract_doc + /// .into_any() + /// .downcast::() + /// .ok() + /// .map(|boxed| *boxed); + /// if did.method() == "foo" { + /// assert!(document.is_some()); + /// } + /// return Ok(document); + /// } + /// + /// fn configure_resolver(mut resolver: Resolver) -> Resolver { + /// resolver.attach_handler("foo".to_owned(), resolve_foo); + /// // Attach handlers for other DID methods we are interested in. + /// resolver + /// } + /// + /// async fn resolve_foo(did: CoreDID) -> std::result::Result { + /// todo!() + /// } + /// ``` + pub async fn resolve(&self, did: &D) -> Result { + let method = did.method(); + let delegate = self + .command_map + .get(method) + .ok_or_else(|| ErrorCause::UnsupportedMethodError { + method: method.to_owned(), + }) + .map_err(Error::new)?; + + delegate.apply(did.as_str()).await + } + + /// Fetches all DID Documents of [`Credential`] issuers contained in a [`Presentation`]. + /// Issuer documents are returned in arbitrary order. + /// + /// # Errors + /// + /// Errors if any issuer URL cannot be parsed to a DID whose associated method is supported by this Resolver, or + /// resolution fails. + pub async fn resolve_presentation_issuers(&self, presentation: &Presentation) -> Result> { + // Extract unique issuers, but keep the position of one credential issued by said DID for each unique issuer. + // The credential positions help us provide better errors. + let issuers: HashMap = presentation + .verifiable_credential + .iter() + .enumerate() + .map(|(idx, credential)| { + CredentialValidator::extract_issuer::(credential) + .map_err(|error| ErrorCause::DIDParsingError { source: error.into() }) + .map_err(Error::new) + .map_err(|error| { + Error::resolution_action( + error, + crate::error::ResolutionAction::PresentationIssuersResolution(idx), + ) + }) + .map(|did| (did, idx)) + }) + .collect::>()?; + + // Resolve issuers concurrently. + futures::future::try_join_all( + issuers + .iter() + .map(|(issuer, cred_idx)| { + self.resolve(issuer).map_err(|error| { + Error::resolution_action( + error, + crate::error::ResolutionAction::PresentationIssuersResolution(*cred_idx), + ) + }) + }) + .collect::>(), + ) + .await + } + + /// Fetches the DID Document of the holder of a [`Presentation`]. + /// + /// # Errors + /// + /// Errors if the holder URL is missing, cannot be parsed to a valid DID whose method is supported by the resolver, or + /// DID resolution fails. + pub async fn resolve_presentation_holder(&self, presentation: &Presentation) -> Result { + let holder: CoreDID = PresentationValidator::extract_holder(presentation) + .map_err(|error| ErrorCause::DIDParsingError { source: error.into() }) + .map_err(Error::new) + .map_err(|error| Error::resolution_action(error, crate::error::ResolutionAction::PresentationHolderResolution))?; + self + .resolve(&holder) + .await + .map_err(|error| Error::resolution_action(error, crate::error::ResolutionAction::PresentationHolderResolution)) + } + + /// Fetches the DID Document of the issuer of a [`Credential`]. + /// + /// # Errors + /// + /// Errors if the issuer URL cannot be parsed to a DID whose associated method is supported by the resolver, or + /// resolution fails. + pub async fn resolve_credential_issuer(&self, credential: &Credential) -> Result { + let issuer_did: CoreDID = CredentialValidator::extract_issuer(credential) + .map_err(|error| ErrorCause::DIDParsingError { source: error.into() }) + .map_err(Error::new)?; + self.resolve(&issuer_did).await + } + + /// Verifies a [`Presentation`]. + /// + /// # Important + /// See [`PresentationValidator::validate`](PresentationValidator::validate()) for information about which properties + /// get validated and what is expected of the optional arguments `holder` and `issuer`. + /// + /// # Resolution + /// The DID Documents for the `holder` and `issuers` are optionally resolved if not given. + /// If you already have up-to-date versions of these DID Documents, you may want + /// to use [`PresentationValidator::validate`]. + /// See also [`Resolver::resolve_presentation_issuers`] and [`Resolver::resolve_presentation_holder`]. Note that + /// DID Documents of a certain method can only be resolved if the resolver has been configured handle this method. + /// See [`Self::attach_handler`]. + /// + /// # Errors + /// Errors from resolving the holder and issuer DID Documents, if not provided, will be returned immediately. + /// Otherwise, errors from validating the presentation and its credentials will be returned + /// according to the `fail_fast` parameter. + pub async fn verify_presentation( + &self, + presentation: &Presentation, + options: &PresentationValidationOptions, + fail_fast: FailFast, + holder: Option<&DOC>, + issuers: Option<&[DOC]>, + ) -> Result<()> + where + U: Serialize, + V: Serialize, + { + match (holder, issuers) { + (Some(holder), Some(issuers)) => { + PresentationValidator::validate(presentation, holder, issuers, options, fail_fast) + .map_err(|error| ErrorCause::PresentationValidationError { source: error }) + .map_err(Error::new) + } + (Some(holder), None) => { + let issuers: Vec = self.resolve_presentation_issuers(presentation).await?; + PresentationValidator::validate(presentation, holder, issuers.as_slice(), options, fail_fast) + .map_err(|error| ErrorCause::PresentationValidationError { source: error }) + .map_err(Error::new) + } + (None, Some(issuers)) => { + let holder = self.resolve_presentation_holder(presentation).await?; + PresentationValidator::validate(presentation, &holder, issuers, options, fail_fast) + .map_err(|error| ErrorCause::PresentationValidationError { source: error }) + .map_err(Error::new) + } + (None, None) => { + let (holder, issuers): (DOC, Vec) = futures::future::try_join( + self.resolve_presentation_holder(presentation), + self.resolve_presentation_issuers(presentation), + ) + .await?; + + PresentationValidator::validate(presentation, &holder, &issuers, options, fail_fast) + .map_err(|error| ErrorCause::PresentationValidationError { source: error }) + .map_err(Error::new) + } + } + .map_err(Into::into) + } +} + +impl Resolver> { + /// Attach a new handler responsible for resolving DIDs of the given DID method. + /// + /// The `handler` is expected to be a closure taking an owned DID and asynchronously returning a DID Document + /// which can be converted to the type this [`Resolver`] is parametrized over. The `handler` is required to be + /// [`Clone`], [`Send`], [`Sync`] and `'static` hence all captured variables must satisfy these bounds. In this regard + /// the `move` keyword and (possibly) wrapping values in an [`Arc`](std::sync::Arc) may come in handy (see the example + /// below). + /// + /// NOTE: If there already exists a handler for this method then it will be replaced with the new handler. + /// In the case where one would like to have a "backup handler" for the same DID method, one can achieve this with + /// composition. + /// + /// # Example + /// ``` + /// # use identity_resolver::Resolver; + /// # use identity_did::did::CoreDID; + /// # use identity_did::document::CoreDocument; + /// + /// // A client that can resolve DIDs of our invented "foo" method. + /// struct Client; + /// + /// impl Client { + /// // Resolves some of the DIDs we are interested in. + /// async fn resolve(&self, _did: &CoreDID) -> std::result::Result { + /// todo!() + /// } + /// } + /// + /// // This way we can essentially produce (cheap) clones of our client. + /// let client = std::sync::Arc::new(Client {}); + /// + /// // Get a clone we can move into a handler. + /// let client_clone = client.clone(); + /// + /// // Construct a resolver that resolves documents of type `CoreDocument`. + /// let mut resolver = Resolver::::new(); + /// + /// // Now we want to attach a handler that uses the client to resolve DIDs whose method is "foo". + /// resolver.attach_handler("foo".to_owned(), move |did: CoreDID| { + /// // We want to resolve the did asynchronously, but since we do not know when it will be awaited we + /// // let the future take ownership of the client by moving a clone into the asynchronous block. + /// let future_client = client_clone.clone(); + /// async move { future_client.resolve(&did).await } + /// }); + /// ``` + pub fn attach_handler(&mut self, method: String, handler: F) + where + D: DID + Send + for<'r> TryFrom<&'r str, Error = DIDERR> + 'static, + DOCUMENT: 'static + Into, + F: Fn(D) -> Fut + 'static + Clone + Send + Sync, + Fut: Future> + Send, + E: Into>, + DIDERR: Into>, + { + let command = SendSyncCommand::new(handler); + self.command_map.insert(method, command); + } +} + +impl Resolver> { + /// Attach a new handler responsible for resolving DIDs of the given DID method. + /// + /// The `handler` is expected to be a closure taking an owned DID and asynchronously returning a DID Document + /// which can be converted to the type this [`Resolver`] is parametrized over. The `handler` is required to be + /// [`Clone`] and `'static` hence all captured variables must satisfy these bounds. In this regard the + /// `move` keyword and (possibly) wrapping values in an [`std::rc::Rc`] may come in handy (see the example below). + /// + /// NOTE: If there already exists a handler for this method then it will be replaced with the new handler. + /// In the case where one would like to have a "backup handler" for the same DID method, one can achieve this with + /// composition. + /// + /// # Example + /// ``` + /// # use identity_resolver::SingleThreadedResolver; + /// # use identity_did::did::CoreDID; + /// # use identity_did::document::CoreDocument; + /// + /// // A client that can resolve DIDs of our invented "foo" method. + /// struct Client; + /// + /// impl Client { + /// // Resolves some of the DIDs we are interested in. + /// async fn resolve(&self, _did: &CoreDID) -> std::result::Result { + /// todo!() + /// } + /// } + /// + /// // This way we can essentially produce (cheap) clones of our client. + /// let client = std::rc::Rc::new(Client {}); + /// + /// // Get a clone we can move into a handler. + /// let client_clone = client.clone(); + /// + /// // Construct a resolver that resolves documents of type `CoreDocument`. + /// let mut resolver = SingleThreadedResolver::::new(); + /// + /// // Now we want to attach a handler that uses the client to resolve DIDs whose method is "foo". + /// resolver.attach_handler("foo".to_owned(), move |did: CoreDID| { + /// // We want to resolve the did asynchronously, but since we do not know when it will be awaited we + /// // let the future take ownership of the client by moving a clone into the asynchronous block. + /// let future_client = client_clone.clone(); + /// async move { future_client.resolve(&did).await } + /// }); + /// ``` + pub fn attach_handler(&mut self, method: String, handler: F) + where + D: DID + for<'r> TryFrom<&'r str, Error = DIDERR> + 'static, + DOCUMENT: 'static + Into, + F: Fn(D) -> Fut + 'static + Clone, + Fut: Future>, + E: Into>, + DIDERR: Into>, + { + let command = SingleThreadedCommand::new(handler); + self.command_map.insert(method, command); + } +} + +#[cfg(feature = "iota")] +mod iota_handler { + use super::Resolver; + use identity_credential::validator::ValidatorDocument; + use identity_stardust::StardustClientExt; + use identity_stardust::StardustDID; + use identity_stardust::StardustDocument; + use identity_stardust::StardustIdentityClientExt; + use std::sync::Arc; + + impl Resolver + where + DOC: From + ValidatorDocument + 'static, + { + /// Convenience method for attaching a new handler responsible for resolving IOTA DIDs. + /// + /// See also [`attach_handler`](Self::attach_handler). + pub fn attach_iota_handler(&mut self, client: CLI) + where + CLI: StardustClientExt + Send + Sync + 'static, + { + let arc_client: Arc = Arc::new(client); + + let handler = move |did: StardustDID| { + let future_client = arc_client.clone(); + async move { future_client.resolve_did(&did).await } + }; + + self.attach_handler(StardustDID::METHOD.to_owned(), handler); + } + } +} + +impl Default for Resolver +where + CMD: for<'r> Command<'r, Result>, + DOC: ValidatorDocument, +{ + fn default() -> Self { + Self::new() + } +} + +impl std::fmt::Debug for Resolver +where + CMD: for<'r> Command<'r, Result>, + DOC: ValidatorDocument, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Resolver") + .field("command_map", &self.command_map) + .finish() + } +} diff --git a/identity_resolver/src/resolution/tests/mod.rs b/identity_resolver/src/resolution/tests/mod.rs new file mode 100644 index 0000000000..5e5ac389b5 --- /dev/null +++ b/identity_resolver/src/resolution/tests/mod.rs @@ -0,0 +1,14 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use super::resolver::*; +mod resolution_errors; +mod send_sync; + +#[cfg(feature = "iota")] +mod successful_presentation_validation; +#[cfg(feature = "iota")] +mod valid_presentation_data; + +#[cfg(feature = "iota")] +mod presentation_validation_errors; diff --git a/identity_resolver/src/resolution/tests/presentation_validation_errors.rs b/identity_resolver/src/resolution/tests/presentation_validation_errors.rs new file mode 100644 index 0000000000..a5ed876530 --- /dev/null +++ b/identity_resolver/src/resolution/tests/presentation_validation_errors.rs @@ -0,0 +1,122 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use crate::ErrorCause; +use crate::Resolver; +use identity_core::convert::FromJson; +use identity_credential::presentation::Presentation; +use identity_credential::validator::FailFast; +use identity_credential::validator::PresentationValidationOptions; +use identity_credential::validator::ValidatorDocument; +use identity_did::did::CoreDID; +use identity_did::document::CoreDocument; +use identity_stardust::StardustDID; +use identity_stardust::StardustDocument; + +use super::valid_presentation_data::HOLDER_FOO_DOC_JSON; +use super::valid_presentation_data::ISSUER_BAR_DOC_JSON; +use super::valid_presentation_data::ISSUER_IOTA_DOC_JSON; +use super::valid_presentation_data::PRESENTATION_JSON; + +type DynamicError = Box; +async fn misconfigured_iota_resolver(_did: StardustDID) -> Result { + Ok(CoreDocument::from_json(HOLDER_FOO_DOC_JSON).unwrap()) +} + +async fn misconfigured_bar_resolver(_did: CoreDID) -> Result { + Ok(StardustDocument::from_json(ISSUER_IOTA_DOC_JSON).unwrap()) +} + +async fn misconfigured_foo_resolver(_did: CoreDID) -> Result { + Ok(CoreDocument::from_json(ISSUER_BAR_DOC_JSON).unwrap()) +} + +/// checks that `Resolver::verify_presentation` fails when the resolver is misconfigured. +async fn check_verify_presentation(mut resolver: Resolver) +where + DOC: ValidatorDocument + From + From + Send + Sync, +{ + let correct_iota_issuer: StardustDocument = StardustDocument::from_json(ISSUER_IOTA_DOC_JSON).unwrap(); + let correct_bar_issuer: CoreDocument = CoreDocument::from_json(ISSUER_BAR_DOC_JSON).unwrap(); + let correct_issuers: [DOC; 2] = [correct_bar_issuer.into(), correct_iota_issuer.into()]; + let correct_holder: DOC = CoreDocument::from_json(HOLDER_FOO_DOC_JSON).unwrap().into(); + + resolver.attach_handler("iota".to_owned(), misconfigured_iota_resolver); + resolver.attach_handler("bar".to_owned(), misconfigured_bar_resolver); + resolver.attach_handler("foo".to_owned(), misconfigured_foo_resolver); + + let presentation: Presentation = Presentation::from_json(PRESENTATION_JSON).unwrap(); + + let resolved_holder: DOC = resolver.resolve_presentation_holder(&presentation).await.unwrap(); + let resolved_issuers: Vec = resolver.resolve_presentation_issuers(&presentation).await.unwrap(); + + // Make sure that verification passes when all correct arguments are passed + let validation_options: PresentationValidationOptions = PresentationValidationOptions::default(); + let fail_fast: FailFast = FailFast::FirstError; + assert!(resolver + .verify_presentation( + &presentation, + &validation_options, + fail_fast, + Some(&correct_holder), + Some(&correct_issuers) + ) + .await + .is_ok()); + + // Fails when the holder argument is correct, but the issuers get resolved with a misconfigured handler + for use_resolved_issuers in [true, false] { + let issuers: Option<&[DOC]> = (use_resolved_issuers).then_some(&resolved_issuers); + assert!(matches!( + resolver + .verify_presentation( + &presentation, + &validation_options, + fail_fast, + Some(&correct_holder), + issuers + ) + .await + .unwrap_err() + .into_error_cause(), + ErrorCause::PresentationValidationError { .. } + )); + } + + // Fails when the issuer argument is correct , but the holder gets resolved with a misconfigured handler + for use_resolved_holder in [true, false] { + let holder: Option<&DOC> = (use_resolved_holder).then_some(&resolved_holder); + assert!(matches!( + resolver + .verify_presentation( + &presentation, + &validation_options, + fail_fast, + holder, + Some(&correct_issuers) + ) + .await + .unwrap_err() + .into_error_cause(), + ErrorCause::PresentationValidationError { .. } + )); + } + + // Fails when no arguments are given when using a misconfigured resolver + assert!(matches!( + resolver + .verify_presentation(&presentation, &validation_options, fail_fast, None, None) + .await + .unwrap_err() + .into_error_cause(), + ErrorCause::PresentationValidationError { .. } + )); +} + +#[tokio::test] +async fn misconfigured_resolvers_verify_incorrectly() { + let resolver_core: Resolver = Resolver::new(); + let resolver: Resolver = Resolver::new(); + check_verify_presentation(resolver_core).await; + check_verify_presentation(resolver).await; +} diff --git a/identity_resolver/src/resolution/tests/resolution_errors.rs b/identity_resolver/src/resolution/tests/resolution_errors.rs new file mode 100644 index 0000000000..f2ca074d44 --- /dev/null +++ b/identity_resolver/src/resolution/tests/resolution_errors.rs @@ -0,0 +1,296 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::error::Error; +use std::str::FromStr; + +use identity_credential::credential::Credential; +use identity_credential::credential::CredentialBuilder; +use identity_credential::credential::Subject; +use identity_credential::presentation::Presentation; +use identity_credential::presentation::PresentationBuilder; +use identity_credential::validator::FailFast; +use identity_credential::validator::PresentationValidationOptions; +use identity_credential::validator::ValidatorDocument; +use identity_did::did::BaseDIDUrl; +use identity_did::did::CoreDID; +use identity_did::did::DIDError; +use identity_did::did::DID; +use identity_did::document::CoreDocument; +use identity_did::document::DocumentBuilder; + +use crate::Error as ResolverError; +use crate::ErrorCause; +use crate::ResolutionAction; +use crate::Resolver; + +/// A very simple handler +async fn mock_handler(did: CoreDID) -> std::result::Result { + Ok(core_document(did)) +} + +/// Create a [`CoreDocument`] +fn core_document(did: CoreDID) -> CoreDocument { + DocumentBuilder::default().id(did).build().unwrap() +} + +// make a simple self issued credential with the given did +fn make_credential(did: impl DID) -> Credential { + CredentialBuilder::default() + .issuer(did.to_url()) + .subject(Subject::with_id(did.to_url().into())) + .build() + .unwrap() +} + +// make a simple presentation consisting of the given credentials +fn make_presentation(credentials: impl Iterator, holder: CoreDID) -> Presentation { + let mut builder = PresentationBuilder::default().holder(holder.to_url().into()); + for credential in credentials { + builder = builder.credential(credential); + } + builder.build().unwrap() +} + +/// Checks that all methods on the resolver involving resolution fail under the assumption that +/// the resolver is set up in such a way that the `resolve` method must fail for this DID (because of a specific +/// cause), but succeed with `good_did`. The `assertions` argument is a function or closure that asserts that the +/// [`ErrorCause`] is of the expected value. +async fn check_failure_for_all_methods(resolver: Resolver, bad_did: CoreDID, good_did: D, assertions: F) +where + F: Fn(ErrorCause), + D: DID, + DOC: ValidatorDocument + Send + Sync + 'static + From, +{ + // resolving bad_did fails + let err: ResolverError = resolver.resolve(&bad_did).await.unwrap_err(); + assertions(err.into_error_cause()); + + // resolving the issuer of the bad credential fails + let cred: Credential = make_credential(bad_did.clone()); + + let err: ResolverError = resolver.resolve_credential_issuer(&cred).await.unwrap_err(); + assertions(err.into_error_cause()); + + // set up a presentation of the form: holder: bad_did , verifiableCredential: [good_credential, bad_credential, + // good_credential] , other stuff irrelevant for this test. + let other_credential: Credential = make_credential(good_did.clone()); + let presentation: Presentation = make_presentation( + [other_credential.clone(), cred, other_credential.clone()].into_iter(), + bad_did.clone(), + ); + + // resolving the holder of the presentation fails + let err: ResolverError = resolver.resolve_presentation_holder(&presentation).await.unwrap_err(); + assert!(err.action().unwrap() == ResolutionAction::PresentationHolderResolution); + assertions(err.into_error_cause()); + + //resolving the presentation issuers will fail because of the bad credential at position 1. + let err: ResolverError = resolver.resolve_presentation_issuers(&presentation).await.unwrap_err(); + assert!(err.action().unwrap() == ResolutionAction::PresentationIssuersResolution(1)); + assertions(err.into_error_cause()); + // check that our expectations are also matched when calling `verify_presentation`. + + // this can be passed as an argument to `verify_presentation` to avoid resolution of the holder or issuers. + let mock_document: DOC = core_document(bad_did.clone()).into(); + let err: ResolverError = resolver + .verify_presentation( + &presentation, + &PresentationValidationOptions::default(), + FailFast::FirstError, + Some(&mock_document), + None, + ) + .await + .unwrap_err(); + + assert!(err.action().unwrap() == ResolutionAction::PresentationIssuersResolution(1)); + assertions(err.into_error_cause()); + + let err: ResolverError = resolver + .verify_presentation( + &presentation, + &PresentationValidationOptions::default(), + FailFast::FirstError, + None, + Some(std::slice::from_ref(&mock_document)), + ) + .await + .unwrap_err(); + + assert!(err.action().unwrap() == ResolutionAction::PresentationHolderResolution); + assertions(err.into_error_cause()); + + // finally when both holder and issuer needs to be resolved we check that resolution fails + let err: ResolverError = resolver + .verify_presentation( + &presentation, + &PresentationValidationOptions::default(), + FailFast::FirstError, + None, + None, + ) + .await + .unwrap_err(); + + assert!(matches!( + err.action().unwrap(), + ResolutionAction::PresentationIssuersResolution(..) | ResolutionAction::PresentationHolderResolution + )); + + assertions(err.into_error_cause()); +} + +// =========================================================================== +// Missing handler for DID method failure tests +// =========================================================================== +#[tokio::test] +async fn missing_handler_errors() { + let method_name: String = "foo".to_owned(); + let bad_did: CoreDID = CoreDID::parse(&format!("did:{method_name}:1234")).unwrap(); + let other_method: String = "bar".to_owned(); + let good_did: CoreDID = CoreDID::parse(format!("did:{other_method}:1234")).unwrap(); + // configure `resolver` to resolve the "bar" method + let mut resolver: Resolver = Resolver::new(); + let mut resolver_core: Resolver = Resolver::new(); + resolver.attach_handler(other_method.clone(), mock_handler); + resolver_core.attach_handler(other_method, mock_handler); + + // to avoid boiler plate + let check_match = move |err| match err { + ErrorCause::UnsupportedMethodError { method } => { + assert_eq!(method_name, method); + } + _ => unreachable!(), + }; + + check_failure_for_all_methods(resolver, bad_did.clone(), good_did.clone(), check_match.clone()).await; + check_failure_for_all_methods(resolver_core, bad_did, good_did, check_match).await; +} + +// =========================================================================== +// DID Parsing failure tests +// =========================================================================== + +// Implement the DID trait for a new type +#[derive(Hash, Ord, PartialOrd, Eq, PartialEq, Clone)] +struct FooDID(CoreDID); + +impl FooDID { + const METHOD_ID_LENGTH: usize = 5; + + fn try_from_core(did: CoreDID) -> std::result::Result { + Some(did) + .filter(|did| did.method() == "foo" && did.method_id().len() == FooDID::METHOD_ID_LENGTH) + .map(Self) + .ok_or(DIDError::InvalidMethodName) + } +} + +impl AsRef for FooDID { + fn as_ref(&self) -> &CoreDID { + &self.0 + } +} + +impl From for String { + fn from(did: FooDID) -> Self { + String::from(did.0) + } +} + +impl FromStr for FooDID { + type Err = DIDError; + fn from_str(s: &str) -> Result { + CoreDID::from_str(s).and_then(Self::try_from_core) + } +} + +impl TryFrom for FooDID { + type Error = DIDError; + fn try_from(value: BaseDIDUrl) -> Result { + CoreDID::try_from(value).and_then(Self::try_from_core) + } +} + +impl<'a> TryFrom<&'a str> for FooDID { + type Error = DIDError; + fn try_from(value: &'a str) -> Result { + CoreDID::try_from(value).and_then(Self::try_from_core) + } +} + +#[tokio::test] +async fn resolve_unparsable() { + let mut resolver: Resolver = Resolver::new(); + let mut resolver_core: Resolver = Resolver::new(); + + // register a handler that wants `did` to be of type `FooDID`. + async fn handler(did: FooDID) -> std::result::Result { + mock_handler(did.as_ref().clone()).await + } + + resolver.attach_handler("foo".to_owned(), handler); + resolver_core.attach_handler("foo".to_owned(), handler); + + let bad_did: CoreDID = CoreDID::parse("did:foo:1234").unwrap(); + // ensure that the DID we created does not satisfy the requirements of the "foo" method + assert!(bad_did.method_id().len() < FooDID::METHOD_ID_LENGTH); + + let good_did: FooDID = FooDID::try_from("did:foo:12345").unwrap(); + + let error_matcher = |err: ErrorCause| { + assert!(matches!( + err + .source() + .unwrap() + .downcast_ref::() + .unwrap_or_else(|| panic!("{:?}", &err)), + &DIDError::InvalidMethodName + )); + + match err { + ErrorCause::DIDParsingError { .. } => {} + _ => unreachable!(), + } + }; + + check_failure_for_all_methods(resolver, bad_did.clone(), good_did.clone(), error_matcher).await; + check_failure_for_all_methods(resolver_core, bad_did, good_did, error_matcher).await; +} + +// =========================================================================== +// Failing handler tests +// =========================================================================== + +#[tokio::test] +async fn handler_failure() { + #[derive(Debug, thiserror::Error)] + #[error("resolution failed")] + struct ResolutionError; + async fn failing_handler(_did: CoreDID) -> std::result::Result { + Err(ResolutionError) + } + + let mut resolver: Resolver = Resolver::new(); + let mut resolver_core: Resolver = Resolver::new(); + resolver.attach_handler("foo".to_owned(), failing_handler); + resolver_core.attach_handler("foo".to_owned(), failing_handler); + + let bad_did: CoreDID = CoreDID::parse("did:foo:1234").unwrap(); + let good_did: CoreDID = CoreDID::parse("did:bar:1234").unwrap(); + resolver.attach_handler(good_did.method().to_owned(), mock_handler); + resolver_core.attach_handler(good_did.method().to_owned(), mock_handler); + + // to avoid boiler plate + let error_matcher = |err: ErrorCause| { + assert!(err.source().unwrap().downcast_ref::().is_some()); + match err { + ErrorCause::HandlerError { .. } => {} + _ => unreachable!(), + } + }; + + check_failure_for_all_methods(resolver, bad_did.clone(), good_did.clone(), error_matcher).await; + check_failure_for_all_methods(resolver_core, bad_did, good_did, error_matcher).await; +} diff --git a/identity_resolver/src/resolution/tests/send_sync.rs b/identity_resolver/src/resolution/tests/send_sync.rs new file mode 100644 index 0000000000..e727d92e64 --- /dev/null +++ b/identity_resolver/src/resolution/tests/send_sync.rs @@ -0,0 +1,51 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use super::*; + +use identity_credential::credential::Credential; +use identity_credential::presentation::Presentation; +use identity_credential::validator::FailFast; +use identity_credential::validator::PresentationValidationOptions; +use identity_credential::validator::ValidatorDocument; +use identity_did::did::DID; +use serde::Serialize; + +fn is_send(_t: T) {} +fn is_send_sync(_t: T) {} + +#[allow(dead_code)] +fn default_resolver_is_send_sync() { + let resolver = Resolver::::new(); + is_send_sync(resolver); +} + +#[allow(dead_code)] +fn resolver_methods_give_send_futures( + did: D, + credential: Credential, + presentation: Presentation, +) where + DOC: ValidatorDocument + Send + Sync + 'static, + D: DID + Send + Sync + 'static, + T: Send + Sync + Serialize, + U: Send + Sync + Serialize, + V: Send + Sync + Serialize, +{ + let resolver = Resolver::::new(); + is_send(resolver.resolve(&did)); + + is_send(resolver.resolve_credential_issuer(&credential)); + + is_send(resolver.resolve_presentation_holder(&presentation)); + + is_send(resolver.resolve_presentation_issuers(&presentation)); + + is_send(resolver.verify_presentation( + &presentation, + &PresentationValidationOptions::default(), + FailFast::FirstError, + Option::<&DOC>::None, + Option::<&[DOC]>::None, + )); +} diff --git a/identity_resolver/src/resolution/tests/successful_presentation_validation.rs b/identity_resolver/src/resolution/tests/successful_presentation_validation.rs new file mode 100644 index 0000000000..8110f1efdd --- /dev/null +++ b/identity_resolver/src/resolution/tests/successful_presentation_validation.rs @@ -0,0 +1,97 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use identity_core::convert::FromJson; +use identity_credential::presentation::Presentation; +use identity_credential::validator::FailFast; +use identity_credential::validator::PresentationValidationOptions; +use identity_credential::validator::SubjectHolderRelationship; +use identity_credential::validator::ValidatorDocument; +use identity_did::did::CoreDID; +use identity_did::did::DID; +use identity_did::document::CoreDocument; +use identity_did::document::Document; +use identity_did::verifiable::VerifierOptions; +use identity_stardust::StardustDID; +use identity_stardust::StardustDocument; +use serde::de::DeserializeOwned; + +use crate::Resolver; + +use super::valid_presentation_data::HOLDER_FOO_DOC_JSON; +use super::valid_presentation_data::ISSUER_BAR_DOC_JSON; +use super::valid_presentation_data::ISSUER_IOTA_DOC_JSON; +use super::valid_presentation_data::PRESENTATION_JSON; + +// Setup mock handlers: +#[derive(Debug, thiserror::Error)] +#[error("the document could not be resolved")] +struct ResolutionError; +// returns the deserialization of JSON (if the did matches) otherwise an error. +async fn resolve(did: D, json: &str) -> Result +where + D: DID + Send + Sync + 'static + Eq, + DOC: Document + Send + Sync + 'static + DeserializeOwned, + ::D: PartialEq, +{ + let doc: DOC = DOC::from_json(json).unwrap(); + (doc.id() == &did).then_some(doc).ok_or(ResolutionError) +} + +async fn resolve_foo(did: CoreDID) -> Result { + resolve(did, HOLDER_FOO_DOC_JSON).await +} + +async fn resolve_iota(did: StardustDID) -> Result { + resolve(did, ISSUER_IOTA_DOC_JSON).await +} + +async fn resolve_bar(did: CoreDID) -> Result { + resolve(did, ISSUER_BAR_DOC_JSON).await +} + +async fn check_verify_presentation(mut resolver: Resolver) +where + DOC: ValidatorDocument + From + From + Send + Sync, +{ + let presentation: Presentation = Presentation::from_json(PRESENTATION_JSON).unwrap(); + + resolver.attach_handler(StardustDID::METHOD.to_owned(), resolve_iota); + resolver.attach_handler("foo".to_owned(), resolve_foo); + resolver.attach_handler("bar".to_owned(), resolve_bar); + + // resolve the DID documents of the presentation's holder and credential issuers. + let holder_doc = resolver.resolve_presentation_holder(&presentation).await.unwrap(); + let issuer_docs = resolver.resolve_presentation_issuers(&presentation).await.unwrap(); + + // check that verification works regardless of whether we first resolve and then pass holder/issuers to the method or + // if resolution of missing documents is done internally. + for pass_holder_as_arg in [true, false] { + for pass_issuers_as_arg in [true, false] { + let holder: Option<&DOC> = pass_holder_as_arg.then_some(&holder_doc); + let issuers: Option<&[DOC]> = pass_issuers_as_arg.then_some(&issuer_docs); + assert!(resolver + .verify_presentation( + &presentation, + &PresentationValidationOptions::new() + .presentation_verifier_options( + VerifierOptions::new().challenge(presentation.proof.clone().unwrap().challenge.unwrap()) + ) + .subject_holder_relationship(SubjectHolderRelationship::AlwaysSubject), + FailFast::FirstError, + holder, + issuers + ) + .await + .is_ok()); + } + } +} + +#[tokio::test] +async fn correct_presentation_validation() { + let core_resolver: Resolver = Resolver::new(); + let dynamic_resolver: Resolver = Resolver::new(); + check_verify_presentation(core_resolver).await; + check_verify_presentation(dynamic_resolver).await; +} diff --git a/identity_resolver/src/resolution/tests/valid_presentation_data.rs b/identity_resolver/src/resolution/tests/valid_presentation_data.rs new file mode 100644 index 0000000000..b77f545aaa --- /dev/null +++ b/identity_resolver/src/resolution/tests/valid_presentation_data.rs @@ -0,0 +1,14 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// This module contains JSON strings of a valid presentation together with the DID Documents of the holder and +// credential issuers. + +pub(super) const PRESENTATION_JSON: &str = + include_str!("../../../../identity_credential/tests/fixtures/signed_presentation/presentation.json"); +pub(super) const HOLDER_FOO_DOC_JSON: &str = + include_str!("../../../../identity_credential/tests/fixtures/signed_presentation/subject_foo_doc.json"); +pub(super) const ISSUER_IOTA_DOC_JSON: &str = + include_str!("../../../../identity_credential/tests/fixtures/signed_presentation/issuer_iota_doc.json"); +pub(super) const ISSUER_BAR_DOC_JSON: &str = + include_str!("../../../../identity_credential/tests/fixtures/signed_presentation/issuer_bar_doc.json"); diff --git a/identity_stardust/Cargo.toml b/identity_stardust/Cargo.toml index 20dee67a33..66a591f731 100644 --- a/identity_stardust/Cargo.toml +++ b/identity_stardust/Cargo.toml @@ -15,10 +15,11 @@ description = "An IOTA Ledger integration for the identity.rs library." # Ensure bee-block always matches the version used by iota-client. bee-block = { version = "1.0.0-beta.7", default-features = false, features = ["std"], optional = true } identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } -identity_credential = { version = "=0.6.0", path = "../identity_credential", default-features = false } +identity_credential = { version = "=0.6.0", path = "../identity_credential", default-features = false, features = ["validator"] } identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } async-trait = { version = "0.1.56", default-features = false, optional = true } +futures = { version = "0.3" } iota-client = { version = "2.0.0-beta.3", default-features = false, features = ["tls"], optional = true } num-derive = { version = "0.3", default-features = false } num-traits = { version = "0.2", default-features = false, features = ["std"] } @@ -41,10 +42,12 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [features] -default = ["client", "iota-client", "revocation-bitmap"] +default = ["client", "iota-client", "revocation-bitmap", "send-sync-client-ext"] # Exposes the `StardustIdentityClient` and `StardustIdentityClientExt` traits. client = ["dep:async-trait", "dep:bee-block"] # Enables the iota-client dependency, the client trait implementations for it, and the `StardustClientExt` trait. iota-client = ["dep:iota-client", "client"] # Enables revocation with `RevocationBitmap2022`. revocation-bitmap = ["identity_did/revocation-bitmap"] +# Adds Send bounds on the futures produces by the client extension traits. +send-sync-client-ext = [] diff --git a/identity_stardust/src/client/identity_client.rs b/identity_stardust/src/client/identity_client.rs index cbed527097..ba88e918f8 100644 --- a/identity_stardust/src/client/identity_client.rs +++ b/identity_stardust/src/client/identity_client.rs @@ -19,7 +19,8 @@ use crate::StardustDID; use crate::StardustDocument; /// Helper functions necessary for the [`StardustIdentityClientExt`] trait. -#[async_trait::async_trait(? Send)] +#[cfg_attr(feature = "send-sync-client-ext", async_trait::async_trait)] +#[cfg_attr(not(feature = "send-sync-client-ext"), async_trait::async_trait(?Send))] pub trait StardustIdentityClient { /// Return the Bech32 human-readable part (HRP) of the network. /// @@ -38,7 +39,9 @@ pub trait StardustIdentityClient { /// /// This trait is not intended to be implemented directly, a blanket implementation is /// provided for [`StardustIdentityClient`] implementers. -#[async_trait::async_trait(? Send)] + +#[cfg_attr(feature = "send-sync-client-ext", async_trait::async_trait)] +#[cfg_attr(not(feature = "send-sync-client-ext"), async_trait::async_trait(?Send))] pub trait StardustIdentityClientExt: StardustIdentityClient { /// Create a DID with a new Alias Output containing the given `document`. /// diff --git a/identity_stardust/src/client/iota_client.rs b/identity_stardust/src/client/iota_client.rs index d3c6dcd80d..1fdf33d881 100644 --- a/identity_stardust/src/client/iota_client.rs +++ b/identity_stardust/src/client/iota_client.rs @@ -26,7 +26,8 @@ use crate::StardustIdentityClientExt; /// An extension trait for [`Client`] that provides helper functions for publication /// and deletion of DID documents in Alias Outputs. -#[async_trait::async_trait(?Send)] +#[cfg_attr(feature = "send-sync-client-ext", async_trait::async_trait)] +#[cfg_attr(not(feature = "send-sync-client-ext"), async_trait::async_trait(?Send))] pub trait StardustClientExt: StardustIdentityClient { /// Publish the given `alias_output` with the provided `secret_manager`, and returns /// the DID document extracted from the published block. @@ -55,7 +56,8 @@ pub trait StardustClientExt: StardustIdentityClient { /// An extension trait for [`Client`] that provides helper functions for publication /// and deletion of DID documents in Alias Outputs. -#[async_trait::async_trait(?Send)] +#[cfg_attr(feature = "send-sync-client-ext", async_trait::async_trait)] +#[cfg_attr(not(feature = "send-sync-client-ext"), async_trait::async_trait(?Send))] impl StardustClientExt for Client { async fn publish_did_output( &self, @@ -108,7 +110,8 @@ impl StardustClientExt for Client { } } -#[async_trait::async_trait(?Send)] +#[cfg_attr(feature = "send-sync-client-ext", async_trait::async_trait)] +#[cfg_attr(not(feature = "send-sync-client-ext"), async_trait::async_trait(?Send))] impl StardustIdentityClient for Client { async fn get_network_hrp(&self) -> Result { self.get_bech32_hrp().await.map_err(Error::DIDResolutionError) diff --git a/identity_stardust/src/did/stardust_did.rs b/identity_stardust/src/did/stardust_did.rs index edad3ffe1b..0e82b45fa8 100644 --- a/identity_stardust/src/did/stardust_did.rs +++ b/identity_stardust/src/did/stardust_did.rs @@ -217,57 +217,6 @@ impl StardustDID { } } -impl DID for StardustDID { - /// Returns the [`StardustDID`] scheme. See [`DID::SCHEME`]. - fn scheme(&self) -> &'static str { - self.0.scheme() - } - - /// Returns the [`StardustDID`] authority. - fn authority(&self) -> &str { - self.0.authority() - } - - /// Returns the [`StardustDID`] method name. - fn method(&self) -> &str { - self.0.method() - } - - /// Returns the [`StardustDID`] method-specific ID. - fn method_id(&self) -> &str { - self.0.method_id() - } - - /// Returns the serialized [`StardustDID`]. - /// - /// This is fast since the serialized value is stored in the [`DID`]. - fn as_str(&self) -> &str { - self.0.as_str() - } - - /// Consumes the [`StardustDID`] and returns the serialization. - fn into_string(self) -> String { - self.0.into_string() - } - - /// Creates a new [`DIDUrl`](crate::StardustDIDUrl) by joining with a relative DID Url string. - /// - /// # Errors - /// - /// Returns `Err` if any base or relative DID segments are invalid. - fn join(self, segment: impl AsRef) -> std::result::Result, DIDError> { - self.into_url().join(segment) - } - - fn to_url(&self) -> DIDUrl { - DIDUrl::new(self.clone(), None) - } - - fn into_url(self) -> DIDUrl { - DIDUrl::new(self, None) - } -} - impl FromStr for StardustDID { type Err = DIDError; diff --git a/identity_stardust/src/document/stardust_document.rs b/identity_stardust/src/document/stardust_document.rs index 020cae3b7b..8dc1ce1405 100644 --- a/identity_stardust/src/document/stardust_document.rs +++ b/identity_stardust/src/document/stardust_document.rs @@ -434,6 +434,12 @@ impl From for StardustCoreDocument { } } +impl From for CoreDocument { + fn from(document: StardustDocument) -> Self { + document.document.map(Into::into, |id| id) + } +} + impl From<(StardustCoreDocument, StardustDocumentMetadata)> for StardustDocument { fn from((document, metadata): (StardustCoreDocument, StardustDocumentMetadata)) -> Self { Self { document, metadata } From 771d81e02102ad5c78af6020e7a940e2f8e382ff Mon Sep 17 00:00:00 2001 From: cycraig Date: Fri, 9 Sep 2022 13:00:16 +0200 Subject: [PATCH 52/89] Update Wasm credential, presentation validators for Stardust (#1004) * Update WasmPresentationValidator with new resolver structs * Update WasmCredentialValidator to use new resolver types * Comment out old examples instead of porting them * Update Wasm validator unit tests * Expand Wasm credential, presentation validator unit test --- bindings/wasm/docs/api-reference.md | 112 +++++++++--------- .../wasm/examples-account/src/create_vc.ts | 13 +- .../wasm/examples-account/src/create_vp.ts | 11 +- .../wasm/examples-account/src/revoke_vc.ts | 26 ++-- .../src/credential/credential_validator.rs | 52 ++++---- .../src/credential/presentation_validator.rs | 34 +++--- bindings/wasm/src/credential/types.rs | 2 +- bindings/wasm/src/resolver/mod.rs | 2 + .../src/resolver/supported_document_types.rs | 26 +++- bindings/wasm/tests/credentials.ts | 29 +++-- 10 files changed, 185 insertions(+), 122 deletions(-) diff --git a/bindings/wasm/docs/api-reference.md b/bindings/wasm/docs/api-reference.md index 9e51cd572e..9ef2da25c1 100644 --- a/bindings/wasm/docs/api-reference.md +++ b/bindings/wasm/docs/api-reference.md @@ -200,6 +200,11 @@ See IVerifierOptions.

StateMetadataEncoding
+
MethodRelationship
+
+
DIDType
+

Supported types representing a DID that can be generated by the storage interface.

+
StatusCheck

Controls validation behaviour when checking whether or not a credential has been revoked by its credentialStatus.

@@ -242,11 +247,6 @@ This variant is the default used if no other variant is specified when construct
FirstError

Return after the first error occurs.

-
MethodRelationship
-
-
DIDType
-

Supported types representing a DID that can be generated by the storage interface.

-
KeyType
DIDMessageEncoding
@@ -273,12 +273,11 @@ publishing to the Tangle. * [Account](#Account) * [.createService(options)](#Account+createService) ⇒ Promise.<void> - * [.setAlsoKnownAs(options)](#Account+setAlsoKnownAs) ⇒ Promise.<void> * [.deleteMethod(options)](#Account+deleteMethod) ⇒ Promise.<void> * [.deleteService(options)](#Account+deleteService) ⇒ Promise.<void> * [.setController(options)](#Account+setController) ⇒ Promise.<void> - * [.createMethod(options)](#Account+createMethod) ⇒ Promise.<void> * [.attachMethodRelationships(options)](#Account+attachMethodRelationships) ⇒ Promise.<void> + * [.createMethod(options)](#Account+createMethod) ⇒ Promise.<void> * [.detachMethodRelationships(options)](#Account+detachMethodRelationships) ⇒ Promise.<void> * [.did()](#Account+did) ⇒ [IotaDID](#IotaDID) * [.autopublish()](#Account+autopublish) ⇒ boolean @@ -297,6 +296,7 @@ publishing to the Tangle. * [.unrevokeCredentials(fragment, indices)](#Account+unrevokeCredentials) ⇒ Promise.<void> * [.encryptData(plaintext, associated_data, encryption_algorithm, cek_algorithm, public_key)](#Account+encryptData) ⇒ [Promise.<EncryptedData>](#EncryptedData) * [.decryptData(data, encryption_algorithm, cek_algorithm, fragment)](#Account+decryptData) ⇒ Promise.<Uint8Array> + * [.setAlsoKnownAs(options)](#Account+setAlsoKnownAs) ⇒ Promise.<void> @@ -309,17 +309,6 @@ Adds a new Service to the DID Document. | --- | --- | | options | CreateServiceOptions | - - -### account.setAlsoKnownAs(options) ⇒ Promise.<void> -Sets the `alsoKnownAs` property in the DID document. - -**Kind**: instance method of [Account](#Account) - -| Param | Type | -| --- | --- | -| options | SetAlsoKnownAsOptions | - ### account.deleteMethod(options) ⇒ Promise.<void> @@ -353,17 +342,6 @@ Sets the controllers of the DID document. | --- | --- | | options | SetControllerOptions | - - -### account.createMethod(options) ⇒ Promise.<void> -Adds a new verification method to the DID document. - -**Kind**: instance method of [Account](#Account) - -| Param | Type | -| --- | --- | -| options | CreateMethodOptions | - ### account.attachMethodRelationships(options) ⇒ Promise.<void> @@ -378,6 +356,17 @@ it cannot be an embedded method. | --- | --- | | options | AttachMethodRelationshipOptions | + + +### account.createMethod(options) ⇒ Promise.<void> +Adds a new verification method to the DID document. + +**Kind**: instance method of [Account](#Account) + +| Param | Type | +| --- | --- | +| options | CreateMethodOptions | + ### account.detachMethodRelationships(options) ⇒ Promise.<void> @@ -580,6 +569,17 @@ Returns the decrypted text. | cek_algorithm | [CekAlgorithm](#CekAlgorithm) | | fragment | string | + + +### account.setAlsoKnownAs(options) ⇒ Promise.<void> +Sets the `alsoKnownAs` property in the DID document. + +**Kind**: instance method of [Account](#Account) + +| Param | Type | +| --- | --- | +| options | SetAlsoKnownAsOptions | + ## AccountBuilder @@ -2144,10 +2144,10 @@ Deserializes an instance from a JSON object. * [.checkStructure(credential)](#CredentialValidator.checkStructure) * [.checkExpiresOnOrAfter(credential, timestamp)](#CredentialValidator.checkExpiresOnOrAfter) * [.checkIssuedOnOrBefore(credential, timestamp)](#CredentialValidator.checkIssuedOnOrBefore) - * [.verifySignature(credential, trusted_issuers, options)](#CredentialValidator.verifySignature) - * [.check_subject_holder_relationship(credential, holder_url, relationship)](#CredentialValidator.check_subject_holder_relationship) + * [.verifySignature(credential, trustedIssuers, options)](#CredentialValidator.verifySignature) + * [.checkSubjectHolderRelationship(credential, holder, relationship)](#CredentialValidator.checkSubjectHolderRelationship) * [.checkStatus(credential, trustedIssuers, statusCheck)](#CredentialValidator.checkStatus) - * [.extractIssuer(credential)](#CredentialValidator.extractIssuer) ⇒ [IotaDID](#IotaDID) + * [.extractIssuer(credential)](#CredentialValidator.extractIssuer) ⇒ [CoreDID](#CoreDID) \| [StardustDID](#StardustDID) @@ -2182,7 +2182,7 @@ An error is returned whenever a validated condition is not satisfied. | Param | Type | | --- | --- | | credential | [Credential](#Credential) | -| issuer | [Document](#Document) \| [ResolvedDocument](#ResolvedDocument) | +| issuer | [StardustDocument](#StardustDocument) \| [CoreDocument](#CoreDocument) | | options | [CredentialValidationOptions](#CredentialValidationOptions) | | fail_fast | number | @@ -2226,7 +2226,7 @@ Validate that the credential is issued on or before the specified timestamp. -### CredentialValidator.verifySignature(credential, trusted_issuers, options) +### CredentialValidator.verifySignature(credential, trustedIssuers, options) Verify the signature using the DID Document of a trusted issuer. # Warning @@ -2241,21 +2241,21 @@ to verify the credential's signature will be made and an error is returned upon | Param | Type | | --- | --- | | credential | [Credential](#Credential) | -| trusted_issuers | Array.<(Document\|ResolvedDocument)> | +| trustedIssuers | Array.<(StardustDocument\|CoreDocument)> | | options | [VerifierOptions](#VerifierOptions) | - + -### CredentialValidator.check\_subject\_holder\_relationship(credential, holder_url, relationship) +### CredentialValidator.checkSubjectHolderRelationship(credential, holder, relationship) Validate that the relationship between the `holder` and the credential subjects is in accordance with -`relationship`. The `holder_url` parameter is expected to be the URL of the holder. +`relationship`. The `holder` parameter is expected to be the URL of the holder. **Kind**: static method of [CredentialValidator](#CredentialValidator) | Param | Type | | --- | --- | | credential | [Credential](#Credential) | -| holder_url | string | +| holder | string | | relationship | number | @@ -2270,12 +2270,12 @@ Only supports `BitmapRevocation2022`. | Param | Type | | --- | --- | | credential | [Credential](#Credential) | -| trustedIssuers | Array.<(Document\|ResolvedDocument)> | +| trustedIssuers | Array.<(StardustDocument\|CoreDocument)> | | statusCheck | number | -### CredentialValidator.extractIssuer(credential) ⇒ [IotaDID](#IotaDID) +### CredentialValidator.extractIssuer(credential) ⇒ [CoreDID](#CoreDID) \| [StardustDID](#StardustDID) Utility for extracting the issuer field of a `Credential` as a DID. ### Errors @@ -4834,7 +4834,7 @@ Deserializes an instance from a JSON object. * [.validate(presentation, holder, issuers, options, fail_fast)](#PresentationValidator.validate) * [.verifyPresentationSignature(presentation, holder, options)](#PresentationValidator.verifyPresentationSignature) * [.checkStructure(presentation)](#PresentationValidator.checkStructure) - * [.extractHolder(presentation)](#PresentationValidator.extractHolder) ⇒ [IotaDID](#IotaDID) + * [.extractHolder(presentation)](#PresentationValidator.extractHolder) ⇒ [CoreDID](#CoreDID) \| [StardustDID](#StardustDID) @@ -4871,8 +4871,8 @@ An error is returned whenever a validated condition is not satisfied. | Param | Type | | --- | --- | | presentation | [Presentation](#Presentation) | -| holder | [Document](#Document) \| [ResolvedDocument](#ResolvedDocument) | -| issuers | Array.<(Document\|ResolvedDocument)> | +| holder | [StardustDocument](#StardustDocument) \| [CoreDocument](#CoreDocument) | +| issuers | Array.<(StardustDocument\|CoreDocument)> | | options | [PresentationValidationOptions](#PresentationValidationOptions) | | fail_fast | number | @@ -4893,7 +4893,7 @@ Fails if signature verification against the holder document fails. | Param | Type | | --- | --- | | presentation | [Presentation](#Presentation) | -| holder | [Document](#Document) \| [ResolvedDocument](#ResolvedDocument) | +| holder | [StardustDocument](#StardustDocument) \| [CoreDocument](#CoreDocument) | | options | [VerifierOptions](#VerifierOptions) | @@ -4909,7 +4909,7 @@ Validates the semantic structure of the `Presentation`. -### PresentationValidator.extractHolder(presentation) ⇒ [IotaDID](#IotaDID) +### PresentationValidator.extractHolder(presentation) ⇒ [CoreDID](#CoreDID) \| [StardustDID](#StardustDID) Utility for extracting the holder field of a `Presentation` as a DID. ### Errors @@ -7328,6 +7328,16 @@ This is possible because Ed25519 is birationally equivalent to Curve25519 used b ## StateMetadataEncoding +**Kind**: global variable + + +## MethodRelationship +**Kind**: global variable + + +## DIDType +Supported types representing a DID that can be generated by the storage interface. + **Kind**: global variable @@ -7406,16 +7416,6 @@ Return all errors that occur during validation. ## FirstError Return after the first error occurs. -**Kind**: global variable - - -## MethodRelationship -**Kind**: global variable - - -## DIDType -Supported types representing a DID that can be generated by the storage interface. - **Kind**: global variable diff --git a/bindings/wasm/examples-account/src/create_vc.ts b/bindings/wasm/examples-account/src/create_vc.ts index 6d3443c0c6..6d78543bcd 100644 --- a/bindings/wasm/examples-account/src/create_vc.ts +++ b/bindings/wasm/examples-account/src/create_vc.ts @@ -65,12 +65,13 @@ async function createVC(storage?: Storage) { // Validate the credential's signature, the credential's semantic structure, // check that the issuance date is not in the future and that the expiration date is not in the past. - CredentialValidator.validate( - signedVc, - issuer.document(), - CredentialValidationOptions.default(), - FailFast.AllErrors - ); + // TODO: uncomment when ported to Stardust. + // CredentialValidator.validate( + // signedVc, + // issuer.document(), + // CredentialValidationOptions.default(), + // FailFast.AllErrors + // ); // Since `validate` did not throw any errors we know that the credential was successfully validated. console.log(`VC successfully validated`); diff --git a/bindings/wasm/examples-account/src/create_vp.ts b/bindings/wasm/examples-account/src/create_vp.ts index 1cd7e1b4f5..ebb4cc378a 100644 --- a/bindings/wasm/examples-account/src/create_vp.ts +++ b/bindings/wasm/examples-account/src/create_vp.ts @@ -172,11 +172,12 @@ async function createVP(storage?: Storage) { const resolver = new Resolver(); // Validate the presentation and all the credentials included in it according to the validation options - await resolver.verifyPresentation( - presentation, - presentationValidationOptions, - FailFast.FirstError - ); + // TODO: uncomment when ported to Stardust. + // await resolver.verifyPresentation( + // presentation, + // presentationValidationOptions, + // FailFast.FirstError + // ); // Since no errors were thrown by `verifyPresentation` we know that the validation was successful. console.log(`VP successfully validated`); diff --git a/bindings/wasm/examples-account/src/revoke_vc.ts b/bindings/wasm/examples-account/src/revoke_vc.ts index 59fb034ede..52454b803f 100644 --- a/bindings/wasm/examples-account/src/revoke_vc.ts +++ b/bindings/wasm/examples-account/src/revoke_vc.ts @@ -90,12 +90,13 @@ async function revokeVC(storage?: Storage) { // Credential verification now fails. try { - CredentialValidator.validate( - signedVc, - issuer.document(), - CredentialValidationOptions.default(), - FailFast.FirstError - ); + // TODO: uncomment when ported to Stardust. + // CredentialValidator.validate( + // signedVc, + // issuer.document(), + // CredentialValidationOptions.default(), + // FailFast.FirstError + // ); } catch (e) { console.log(`Error during validation: ${e}`); } @@ -117,12 +118,13 @@ async function revokeVC(storage?: Storage) { const resolvedIssuerDoc = await resolver.resolveCredentialIssuer( signedVc ); - CredentialValidator.validate( - signedVc, - resolvedIssuerDoc, - CredentialValidationOptions.default(), - FailFast.FirstError - ); + // TODO: uncomment when ported to Stardust. + // CredentialValidator.validate( + // signedVc, + // resolvedIssuerDoc, + // CredentialValidationOptions.default(), + // FailFast.FirstError + // ); // `CredentialValidator.validate` will throw an error, hence this will not be reached. console.log("Revocation failed!"); diff --git a/bindings/wasm/src/credential/credential_validator.rs b/bindings/wasm/src/credential/credential_validator.rs index d9da532841..8dbcb2ebe9 100644 --- a/bindings/wasm/src/credential/credential_validator.rs +++ b/bindings/wasm/src/credential/credential_validator.rs @@ -1,24 +1,24 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use identity_iota::client::ResolvedIotaDocument; use identity_iota::core::Url; +use identity_iota::credential::AbstractValidatorDocument; use identity_iota::credential::CredentialValidator; use identity_iota::credential::StatusCheck; use identity_iota::credential::ValidationError; -use identity_iota::iota_core::IotaDID; -use identity_iota::iota_core::IotaDocument; +use identity_iota::did::CoreDID; use wasm_bindgen::prelude::*; use crate::common::WasmTimestamp; use crate::credential::validation_options::WasmFailFast; use crate::credential::validation_options::WasmStatusCheck; -use crate::did::ArrayDocumentOrResolvedDocument; -use crate::did::DocumentOrResolvedDocument; -use crate::did::WasmIotaDID; use crate::did::WasmVerifierOptions; use crate::error::Result; use crate::error::WasmResult; +use crate::resolver::ArraySupportedDocument; +use crate::resolver::RustSupportedDocument; +use crate::resolver::SupportedDID; +use crate::resolver::SupportedDocument; use super::WasmCredential; use super::WasmCredentialValidationOptions; @@ -56,12 +56,12 @@ impl WasmCredentialValidator { #[wasm_bindgen] pub fn validate( credential: &WasmCredential, - issuer: &DocumentOrResolvedDocument, + issuer: &SupportedDocument, options: &WasmCredentialValidationOptions, fail_fast: WasmFailFast, ) -> Result<()> { - let issuer_doc: ResolvedIotaDocument = issuer.into_serde().wasm_result()?; - CredentialValidator::validate(&credential.0, &issuer_doc.document, &options.0, fail_fast.into()).wasm_result() + let issuer: AbstractValidatorDocument = issuer.into_serde::().wasm_result()?.into(); + CredentialValidator::validate(&credential.0, &issuer, &options.0, fail_fast.into()).wasm_result() } /// Validates the semantic structure of the `Credential`. @@ -98,23 +98,30 @@ impl WasmCredentialValidator { /// the credential issuer' url cannot be parsed to a DID belonging to one of the trusted issuers. Otherwise an attempt /// to verify the credential's signature will be made and an error is returned upon failure. #[wasm_bindgen(js_name = verifySignature)] + #[allow(non_snake_case)] pub fn verify_signature( credential: &WasmCredential, - trusted_issuers: &ArrayDocumentOrResolvedDocument, + trustedIssuers: &ArraySupportedDocument, options: &WasmVerifierOptions, ) -> Result<()> { - let issuers: Vec = trusted_issuers.into_serde().wasm_result()?; - CredentialValidator::verify_signature(&credential.0, &issuers, &options.0).wasm_result() + let trusted_issuers: Vec = trustedIssuers + .into_serde::>() + .wasm_result()? + .into_iter() + .map(Into::into) + .collect(); + CredentialValidator::verify_signature(&credential.0, &trusted_issuers, &options.0).wasm_result() } /// Validate that the relationship between the `holder` and the credential subjects is in accordance with - /// `relationship`. The `holder_url` parameter is expected to be the URL of the holder. + /// `relationship`. The `holder` parameter is expected to be the URL of the holder. + #[wasm_bindgen(js_name = checkSubjectHolderRelationship)] pub fn check_subject_holder_relationship( credential: &WasmCredential, - holder_url: &str, + holder: &str, relationship: WasmSubjectHolderRelationship, ) -> Result<()> { - let holder: Url = Url::parse(holder_url).wasm_result()?; + let holder: Url = Url::parse(holder).wasm_result()?; CredentialValidator::check_subject_holder_relationship(&credential.0, &holder, relationship.into()).wasm_result() } @@ -125,10 +132,15 @@ impl WasmCredentialValidator { #[allow(non_snake_case)] pub fn check_status( credential: &WasmCredential, - trustedIssuers: &ArrayDocumentOrResolvedDocument, + trustedIssuers: &ArraySupportedDocument, statusCheck: WasmStatusCheck, ) -> Result<()> { - let trusted_issuers: Vec = trustedIssuers.into_serde().wasm_result()?; + let trusted_issuers: Vec = trustedIssuers + .into_serde::>() + .wasm_result()? + .into_iter() + .map(Into::into) + .collect(); let status_check: StatusCheck = StatusCheck::from(statusCheck); CredentialValidator::check_status(&credential.0, &trusted_issuers, status_check).wasm_result() } @@ -139,8 +151,8 @@ impl WasmCredentialValidator { /// /// Fails if the issuer field is not a valid DID. #[wasm_bindgen(js_name = extractIssuer)] - pub fn extract_issuer(credential: &WasmCredential) -> Result { - let did: IotaDID = CredentialValidator::extract_issuer(&credential.0).wasm_result()?; - Ok(WasmIotaDID::from(did)) + pub fn extract_issuer(credential: &WasmCredential) -> Result { + let did: CoreDID = CredentialValidator::extract_issuer(&credential.0).wasm_result()?; + SupportedDID::try_from(did) } } diff --git a/bindings/wasm/src/credential/presentation_validator.rs b/bindings/wasm/src/credential/presentation_validator.rs index 946cf96335..c86bc4fdae 100644 --- a/bindings/wasm/src/credential/presentation_validator.rs +++ b/bindings/wasm/src/credential/presentation_validator.rs @@ -1,20 +1,21 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use identity_iota::credential::AbstractValidatorDocument; use identity_iota::credential::PresentationValidator; -use identity_iota::iota_core::IotaDID; -use identity_iota::iota_core::IotaDocument; +use identity_iota::did::CoreDID; use wasm_bindgen::prelude::*; use crate::credential::WasmFailFast; use crate::credential::WasmPresentation; use crate::credential::WasmPresentationValidationOptions; -use crate::did::ArrayDocumentOrResolvedDocument; -use crate::did::DocumentOrResolvedDocument; -use crate::did::WasmIotaDID; use crate::did::WasmVerifierOptions; use crate::error::Result; use crate::error::WasmResult; +use crate::resolver::ArraySupportedDocument; +use crate::resolver::RustSupportedDocument; +use crate::resolver::SupportedDID; +use crate::resolver::SupportedDocument; #[wasm_bindgen(js_name = PresentationValidator, inspectable)] pub struct WasmPresentationValidator; @@ -50,13 +51,18 @@ impl WasmPresentationValidator { #[wasm_bindgen] pub fn validate( presentation: &WasmPresentation, - holder: &DocumentOrResolvedDocument, - issuers: &ArrayDocumentOrResolvedDocument, + holder: &SupportedDocument, + issuers: &ArraySupportedDocument, options: &WasmPresentationValidationOptions, fail_fast: WasmFailFast, ) -> Result<()> { - let holder: IotaDocument = holder.into_serde().wasm_result()?; - let issuers: Vec = issuers.into_serde().wasm_result()?; + let holder: AbstractValidatorDocument = holder.into_serde::().wasm_result()?.into(); + let issuers: Vec = issuers + .into_serde::>() + .wasm_result()? + .into_iter() + .map(Into::into) + .collect(); PresentationValidator::validate(&presentation.0, &holder, &issuers, &options.0, fail_fast.into()).wasm_result() } @@ -71,10 +77,10 @@ impl WasmPresentationValidator { #[wasm_bindgen(js_name = verifyPresentationSignature)] pub fn verify_presentation_signature( presentation: &WasmPresentation, - holder: &DocumentOrResolvedDocument, + holder: &SupportedDocument, options: &WasmVerifierOptions, ) -> Result<()> { - let holder: IotaDocument = holder.into_serde().wasm_result()?; + let holder: AbstractValidatorDocument = holder.into_serde::().wasm_result()?.into(); PresentationValidator::verify_presentation_signature(&presentation.0, &holder, &options.0).wasm_result() } @@ -90,8 +96,8 @@ impl WasmPresentationValidator { /// /// Fails if the holder field is missing or not a valid DID. #[wasm_bindgen(js_name = extractHolder)] - pub fn extract_holder(presentation: &WasmPresentation) -> Result { - let did: IotaDID = PresentationValidator::extract_holder(&presentation.0).wasm_result()?; - Ok(WasmIotaDID::from(did)) + pub fn extract_holder(presentation: &WasmPresentation) -> Result { + let did: CoreDID = PresentationValidator::extract_holder(&presentation.0).wasm_result()?; + SupportedDID::try_from(did) } } diff --git a/bindings/wasm/src/credential/types.rs b/bindings/wasm/src/credential/types.rs index 5e4b476f51..719ce1b38d 100644 --- a/bindings/wasm/src/credential/types.rs +++ b/bindings/wasm/src/credential/types.rs @@ -119,7 +119,7 @@ const I_SUBJECT: &'static str = r#" [More Info](https://www.w3.org/TR/vc-data-model/#credential-subject) */ interface Subject { /** A URI identifying the credential subject. */ - readonly id?: string | CoreDID | IotaDID | StardustDID; + readonly id?: string | CoreDID | StardustDID; /** Additional properties of the credential subject. */ readonly [properties: string]: unknown; }"#; diff --git a/bindings/wasm/src/resolver/mod.rs b/bindings/wasm/src/resolver/mod.rs index c78fe1fecd..54d851436c 100644 --- a/bindings/wasm/src/resolver/mod.rs +++ b/bindings/wasm/src/resolver/mod.rs @@ -5,3 +5,5 @@ mod constructor_input; mod mixed_resolver; mod supported_document_types; + +pub use supported_document_types::*; diff --git a/bindings/wasm/src/resolver/supported_document_types.rs b/bindings/wasm/src/resolver/supported_document_types.rs index 487be3ecff..909078321e 100644 --- a/bindings/wasm/src/resolver/supported_document_types.rs +++ b/bindings/wasm/src/resolver/supported_document_types.rs @@ -1,20 +1,27 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use crate::did::WasmCoreDID; use crate::did::WasmCoreDocument; use crate::error::WasmError; +use crate::error::WasmResult; +use crate::stardust::WasmStardustDID; use crate::stardust::WasmStardustDocument; use identity_iota::credential::AbstractValidatorDocument; +use identity_iota::did::CoreDID; use identity_iota::did::CoreDocument; +use identity_iota::did::DID; +use identity_stardust::StardustDID; use identity_stardust::StardustDocument; use serde::Deserialize; use wasm_bindgen::prelude::*; +use wasm_bindgen::JsCast; #[derive(Deserialize)] #[serde(untagged)] /// Temporary type used to convert to and from Box until /// we port the Document trait to these bindings. -pub(super) enum RustSupportedDocument { +pub enum RustSupportedDocument { Stardust(StardustDocument), Core(CoreDocument), } @@ -58,6 +65,21 @@ impl TryFrom for RustSupportedDocument { } } +impl TryFrom for SupportedDID { + type Error = JsValue; + + fn try_from(did: CoreDID) -> Result { + let js: JsValue = if did.method() == StardustDID::METHOD { + let ret: StardustDID = StardustDID::try_from_core(did).wasm_result()?; + JsValue::from(WasmStardustDID::from(ret)) + } else { + JsValue::from(WasmCoreDID::from(did)) + }; + + Ok(js.unchecked_into::()) + } +} + #[wasm_bindgen] extern "C" { #[wasm_bindgen(typescript_type = "Promise>")] @@ -78,4 +100,6 @@ extern "C" { #[wasm_bindgen(typescript_type = "Array | undefined")] pub type OptionArraySupportedDocument; + #[wasm_bindgen(typescript_type = "CoreDID | StardustDID")] + pub type SupportedDID; } diff --git a/bindings/wasm/tests/credentials.ts b/bindings/wasm/tests/credentials.ts index 9b155f712a..b56d57d075 100644 --- a/bindings/wasm/tests/credentials.ts +++ b/bindings/wasm/tests/credentials.ts @@ -6,12 +6,17 @@ const { CredentialValidator, CredentialValidationOptions, FailFast, - Document, KeyType, + MethodScope, Presentation, ProofOptions, RevocationBitmap, - Service, + StardustDocument, + StardustService, + StardustVerificationMethod, + StatusCheck, + SubjectHolderRelationship, + Timestamp, KeyPair, VerifierOptions, PresentationValidator, @@ -158,19 +163,21 @@ describe('CredentialValidator, PresentationValidator', function () { describe('#validate()', function () { it('should work', async () => { // Set up issuer & subject DID documents. + const issuerDoc = new StardustDocument("iota"); const issuerKeys = new KeyPair(KeyType.Ed25519); - const issuerDoc = new Document(issuerKeys); + issuerDoc.insertMethod(new StardustVerificationMethod(issuerDoc.id(), KeyType.Ed25519, issuerKeys.public(), "#iss-0"), MethodScope.VerificationMethod()); // Add RevocationBitmap service. const revocationBitmap = new RevocationBitmap(); - issuerDoc.insertService(new Service({ + issuerDoc.insertService(new StardustService({ id: issuerDoc.id().join("#my-revocation-service"), type: RevocationBitmap.type(), serviceEndpoint: revocationBitmap.toEndpoint() })) + const subjectDoc = new StardustDocument("iota"); const subjectKeys = new KeyPair(KeyType.Ed25519); - const subjectDoc = new Document(subjectKeys); + subjectDoc.insertMethod(new StardustVerificationMethod(subjectDoc.id(), KeyType.Ed25519, subjectKeys.public(), "#sub-0"), MethodScope.VerificationMethod()); const subjectDID = subjectDoc.id(); const issuerDID = issuerDoc.id(); @@ -194,11 +201,17 @@ describe('CredentialValidator, PresentationValidator', function () { }); // Sign the credential with the issuer's DID Document. - const signedCredential = issuerDoc.signCredential(credential, issuerKeys.private(), "#sign-0", ProofOptions.default()); + const signedCredential = issuerDoc.signCredential(credential, issuerKeys.private(), "#iss-0", ProofOptions.default()); // Validate the credential. + assert.doesNotThrow(() => CredentialValidator.checkStructure(signedCredential)); + assert.doesNotThrow(() => CredentialValidator.checkExpiresOnOrAfter(signedCredential, Timestamp.nowUTC())); + assert.doesNotThrow(() => CredentialValidator.checkIssuedOnOrBefore(signedCredential, Timestamp.nowUTC())); + assert.doesNotThrow(() => CredentialValidator.checkSubjectHolderRelationship(signedCredential, subjectDID.toString(), SubjectHolderRelationship.AlwaysSubject)); + assert.doesNotThrow(() => CredentialValidator.checkStatus(signedCredential, [issuerDoc], StatusCheck.Strict)); assert.doesNotThrow(() => CredentialValidator.verifySignature(signedCredential, [issuerDoc, subjectDoc], VerifierOptions.default())); assert.doesNotThrow(() => CredentialValidator.validate(signedCredential, issuerDoc, CredentialValidationOptions.default(), FailFast.FirstError)); + assert.deepStrictEqual(CredentialValidator.extractIssuer(signedCredential).toString(), issuerDID.toString()); // Construct a presentation. const presentation = new Presentation({ @@ -206,11 +219,13 @@ describe('CredentialValidator, PresentationValidator', function () { holder: subjectDID.toString(), verifiableCredential: signedCredential }); - const signedPresentation = subjectDoc.signPresentation(presentation, subjectKeys.private(), "#sign-0", ProofOptions.default()); + const signedPresentation = subjectDoc.signPresentation(presentation, subjectKeys.private(), "#sub-0", ProofOptions.default()); // Validate the presentation. + assert.doesNotThrow(() => PresentationValidator.checkStructure(signedPresentation)); assert.doesNotThrow(() => PresentationValidator.verifyPresentationSignature(signedPresentation, subjectDoc, VerifierOptions.default())); assert.doesNotThrow(() => PresentationValidator.validate(signedPresentation, subjectDoc, [issuerDoc], PresentationValidationOptions.default(), FailFast.FirstError)); + assert.deepStrictEqual(PresentationValidator.extractHolder(signedPresentation).toString(), subjectDID.toString()); }); }); }); From 67e8c8c380ee9d3bd7250b772ee80afefc85f26f Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Mon, 12 Sep 2022 10:48:33 +0200 Subject: [PATCH 53/89] Stardust Refactor (#1000) * Stardust Refactor (Part 1) (#990) * Don't export iota client, core & account crates * Remove `identity_iota` benchmarks * identity_iota_core -> identity_iota_core_legacy * identity_iota_client > identity_iota_client_legacy * Rename identity_account_storage * Rename `identity_account` * Apply rustfmt * Fix examples_legacy * Don't publish legacy packages * Apply rustfmt * Don't depend on `identity_credential` in examples * Revert "Rename `identity_account`" This reverts commit 1f29cd789d40c56842c76ae2ba5a2282d2edb25f. * Revert "Rename identity_account_storage" This reverts commit cedde7c2b0aec171417c9da3b6ad58df270746ef. * Revert legacy example account changes * Run dprint fmt * Commit something to re-trigger GitHub compare * Revert "Commit something to re-trigger GitHub compare" This reverts commit 995e984924d619ec1d6e07f5ed3d012c306e1c5e. * Remove direct agent dependency on legacy crates (#1005) * Stardust Refactor (Part 2) (#991) * identity_iota_core -> identity_iota_core_legacy * identity_iota_client > identity_iota_client_legacy * Rename identity_account_storage * Rename `identity_account` * Apply rustfmt * Fix examples_legacy * Don't publish legacy packages * Apply rustfmt * Revert "Rename `identity_account`" This reverts commit 1f29cd789d40c56842c76ae2ba5a2282d2edb25f. * Revert "Rename identity_account_storage" This reverts commit cedde7c2b0aec171417c9da3b6ad58df270746ef. * Run dprint fmt * Rename `identity_stardust` * Reexport `iota_core` from `identity_iota` * Rename client ext traits & DID types * Rename default network name to `iota` * Rename `StardustDocument` * Rename remaining occurences of "stardust" * Rename Stardust -> to Iota in Wasm * Rename stardust mod to iota * Replace last remaining "stardust" occurences * Update TS parts * Rename stardust to iota in examples * Remove Stardust Doc Build Step * Silence dead-code warning temporarily * Ignore doc test for now * Remove legacy Iota modules * Fix default network documentation * Fix wasm-bindgen tests * Move legacy/deactivated test scripts * Delete Wasm examples * Rename stardust examples to examples * Fix crate path in stronghold-nodejs * Remove Wasm example build step in CI * Fix iota-crypto temporarily * Update iota-crypto fix * Deactivate parts of private tangle wiki page * Remove temporary crypto fix * Remove Wasm chain mod * Remove Wasm tangle mod * Bump `rust-version` of `identity_iota` * Use `identity_iota` imports in examples * Update READMEs * Add commonly used types to prelude * Pin build-wasm workflow to _this_ branch * Fix resolve.mdx temporarily * Fix prelude * Fix mdx comments * Fix README so it compiles * Run rustfmt * Add REFACTOR-TODO about wasm build change * Update actor to agent in Readme * Commit something to re-trigger GitHub compare * Fix workflow names, add keywords * Use anyhow in README example * Revert resolve.mdx changes * Migrate resolver to `Iota*` types * Migrate Wasm resolver to `Iota*` types * Comment out credential dependency of resolver * More `Stardust` -> `Iota` renaming * Rename test file * Add back `set_id` * Fix example instructions * Rename `IOTA UTXO` to just `IOTA` * Run readme test * Update Wasm README * Reactivate commented credential fns * Migrate credential test to `Iota` types * Expose `identity_resolver/iota` feature * Expose new crate as `iota` rather than `iota_core` * Regenerate api-reference * Remove private tangle wiki page * Rename `examples-stardust` -> `examples` in Wiki * Update `identity_iota_core` Readme * Update Wiki pages with `Iota` types * Update broken doc links * Add all features as defaults in `identity_iota` * Update CI build instructions; fix README * Fix examples action in CI * Add link to examples after Wasm README.md examples * Fixed Rust create example * Fix example imports * Try fix Wasm test:readme hanging * Remove concurrently timings to try fix hanging * Remove concurrently to try fix CI hanging * Update README example * Revert build-wasm workflow to `@dev` * Remove the TODO also * Revert `1_update_did` to earlier state Co-authored-by: Craig Bester --- .../actions/release/bump-versions/action.yml | 2 +- .github/workflows/build-and-test.yml | 8 +- .github/workflows/shared-build-wasm.yml | 3 - .github/workflows/test-docs-build.yml | 9 - Cargo.toml | 10 +- README.md | 173 +- bindings/stronghold-nodejs/Cargo.lock | 6 +- bindings/stronghold-nodejs/Cargo.toml | 2 +- bindings/stronghold-nodejs/src/error.rs | 2 +- bindings/stronghold-nodejs/src/stronghold.rs | 2 +- .../stronghold-nodejs/src/types/derive.rs | 2 +- bindings/wasm/Cargo.toml | 12 +- bindings/wasm/README.md | 232 +- bindings/wasm/docs/api-reference.md | 8021 +++++------------ bindings/wasm/examples-stardust/README.md | 45 - bindings/wasm/examples/.gitignore | 2 - bindings/wasm/examples/README.md | 78 +- bindings/wasm/examples/src/config.js | 15 - bindings/wasm/examples/src/create_did.js | 40 - .../src/ex0_create_did.ts | 34 +- .../src/ex1_update_did.ts | 14 +- .../src/ex2_resolve_did.ts | 10 +- .../src/ex3_deactivate_did.ts | 14 +- .../src/ex4_delete_did.ts | 0 bindings/wasm/examples/src/index.html | 62 - bindings/wasm/examples/src/key_exchange.js | 105 - .../src/main.ts | 0 bindings/wasm/examples/src/manipulate_did.js | 75 - bindings/wasm/examples/src/node.js | 55 - bindings/wasm/examples/src/private_tangle.js | 54 - bindings/wasm/examples/src/resolve_did.js | 29 - bindings/wasm/examples/src/resolve_history.js | 143 - .../src/tests/ex0_create_did.ts | 0 .../src/tests/ex1_update_did.ts | 0 .../src/tests/ex2_resolve_did.ts | 0 .../src/tests/ex3_deactivate_did.ts | 0 .../src/tests/ex4_delete_did.ts | 0 .../examples/src/tests/node/createIdentity.js | 9 - .../examples/src/tests/node/keyExchange.js | 9 - .../src/tests/node/manipulateIdentity.js | 9 - .../examples/src/tests/node/privateTangle.js | 16 - .../examples/src/tests/node/resolveHistory.js | 9 - .../src/tests/node/resolveIdentity.js | 9 - bindings/wasm/examples/src/utils_web.js | 78 - bindings/wasm/examples/src/web.js | 69 - bindings/wasm/examples/webpack.config.js | 87 - bindings/wasm/lib/index.ts | 2 +- ...tity_client.ts => iota_identity_client.ts} | 36 +- bindings/wasm/package.json | 9 +- bindings/wasm/src/account/storage/traits.rs | 2 +- .../wasm/src/account/wasm_account/account.rs | 4 +- .../account/wasm_account/account_builder.rs | 2 +- .../wasm_account/update/set_controller.rs | 2 +- bindings/wasm/src/chain/document_history.rs | 207 - bindings/wasm/src/chain/mod.rs | 6 - .../wasm/src/credential/credential_builder.rs | 2 +- .../src/credential/presentation_builder.rs | 2 +- bindings/wasm/src/credential/types.rs | 2 +- bindings/wasm/src/did/mod.rs | 27 +- bindings/wasm/src/did/service_common.rs | 90 + bindings/wasm/src/did/wasm_core_did.rs | 8 - bindings/wasm/src/did/wasm_core_document.rs | 18 +- bindings/wasm/src/did/wasm_diff_message.rs | 115 - bindings/wasm/src/did/wasm_document.rs | 703 -- .../wasm/src/did/wasm_document_metadata.rs | 54 - .../wasm/src/did/wasm_resolved_document.rs | 133 - bindings/wasm/src/did/wasm_service.rs | 191 - .../wasm/src/did/wasm_verification_method.rs | 75 - bindings/wasm/src/error.rs | 114 +- bindings/wasm/src/iota/identity_client.rs | 113 + .../{stardust => iota}/identity_client_ext.rs | 80 +- .../wasm_iota_did.rs => iota/iota_did.rs} | 83 +- .../wasm_did_url.rs => iota/iota_did_url.rs} | 54 +- .../iota_document.rs} | 166 +- .../iota_document_metadata.rs} | 18 +- .../iota_metadata_encoding.rs} | 2 +- .../iota_service.rs} | 50 +- .../iota_verification_method.rs} | 71 +- bindings/wasm/src/iota/mod.rs | 22 + bindings/wasm/src/lib.rs | 8 +- .../wasm/src/resolver/constructor_input.rs | 10 +- bindings/wasm/src/resolver/mixed_resolver.rs | 32 +- .../src/resolver/supported_document_types.rs | 38 +- bindings/wasm/src/stardust/identity_client.rs | 114 - bindings/wasm/src/stardust/mod.rs | 21 - bindings/wasm/src/stardust/stardust_did.rs | 160 - .../wasm/src/stardust/stardust_did_url.rs | 106 - bindings/wasm/src/tangle/client.rs | 258 - bindings/wasm/src/tangle/client_config.rs | 365 - bindings/wasm/src/tangle/explorer.rs | 80 - bindings/wasm/src/tangle/message.rs | 33 - bindings/wasm/src/tangle/mod.rs | 18 - bindings/wasm/src/tangle/network.rs | 65 - bindings/wasm/src/tangle/receipt.rs | 61 - bindings/wasm/src/tangle/resolver.rs | 350 - bindings/wasm/tests/credentials.ts | 16 +- bindings/wasm/tests/document.ts | 50 - bindings/wasm/tests/{stardust.ts => iota.ts} | 54 +- bindings/wasm/tests/{ => legacy}/account.ts | 4 +- bindings/wasm/tests/resolver.ts | 12 +- bindings/wasm/tests/wasm.rs | 285 +- .../decentralized_identifiers/create.mdx | 4 +- .../decentralized_identifiers/delete.mdx | 8 +- .../private_tangle.mdx | 46 - .../decentralized_identifiers/resolve.mdx | 69 +- documentation/sidebars.js | 1 - examples/0_basic/0_create_did.rs | 33 +- examples/0_basic/1_update_did.rs | 34 +- examples/0_basic/2_resolve_did.rs | 20 +- examples/0_basic/3_deactivate_did.rs | 18 +- examples/0_basic/4_delete_did.rs | 12 +- examples/1_advanced/0_did_controls_did.rs | 36 +- examples/1_advanced/1_did_issues_nft.rs | 16 +- examples/1_advanced/2_nft_owns_did.rs | 16 +- examples/1_advanced/3_did_issues_tokens.rs | 32 +- examples/1_advanced/4_key_exchange.rs | 54 +- examples/Cargo.toml | 5 +- examples/utils/utils.rs | 34 +- examples_legacy/Cargo.toml | 4 + examples_legacy/account/config.rs | 20 +- examples_legacy/account/create_did.rs | 12 +- examples_legacy/account/create_vc.rs | 10 +- examples_legacy/account/create_vp.rs | 12 +- examples_legacy/account/encryption.rs | 26 +- examples_legacy/account/lazy.rs | 12 +- examples_legacy/account/manipulate_did.rs | 14 +- .../account/multiple_identities.rs | 4 +- examples_legacy/account/revoke_vc.rs | 14 +- examples_legacy/account/signing.rs | 16 +- examples_legacy/account/unchecked.rs | 14 +- examples_legacy/getting_started.rs | 2 +- examples_legacy/low-level-api/common.rs | 4 +- examples_legacy/low-level-api/create_did.rs | 7 +- examples_legacy/low-level-api/key_exchange.rs | 13 +- .../low-level-api/manipulate_did.rs | 11 +- .../low-level-api/private_tangle.rs | 13 +- examples_legacy/low-level-api/resolve_did.rs | 10 +- .../low-level-api/resolve_history.rs | 14 +- identity_account/Cargo.toml | 7 +- identity_account/src/account/account.rs | 38 +- identity_account/src/account/builder.rs | 12 +- identity_account/src/account/config.rs | 4 +- .../src/account/publish_options.rs | 2 +- identity_account/src/error.rs | 8 +- identity_account/src/tests/account.rs | 26 +- identity_account/src/tests/updates.rs | 16 +- identity_account/src/tests/util.rs | 4 +- identity_account/src/types/identity_state.rs | 2 +- .../src/types/identity_updater.rs | 4 +- identity_account/src/updates/macros.rs | 8 +- identity_account/src/updates/update.rs | 16 +- identity_account_storage/Cargo.toml | 16 +- identity_account_storage/src/error.rs | 2 +- .../src/identity/chain_state.rs | 4 +- .../src/storage/memstore.rs | 4 +- .../src/storage/stronghold.rs | 4 +- .../src/storage/test_suite.rs | 12 +- .../src/storage/traits.rs | 2 +- .../src/stronghold/client_path.rs | 2 +- .../src/stronghold/test_util.rs | 4 +- .../src/stronghold/tests.rs | 2 +- .../src/types/did_type.rs | 2 +- .../src/types/key_location.rs | 2 +- identity_agent/Cargo.toml | 3 +- identity_agent/benches/agent.rs | 4 +- identity_agent/benches/didcomm.rs | 11 +- identity_agent/src/didcomm/agent.rs | 11 +- identity_agent/src/tests/handler.rs | 2 +- identity_agent/src/tests/mod.rs | 8 +- .../src/tests/remote_account/handler.rs | 4 +- .../src/tests/remote_account/requests.rs | 4 +- .../src/tests/remote_account/tests.rs | 2 +- identity_comm/Cargo.toml | 2 +- identity_comm/src/error.rs | 2 +- identity_did/src/verification/mod.rs | 2 +- identity_iota/Cargo.toml | 45 +- identity_iota/README.md | 177 +- identity_iota/benches/benchmark.rs | 96 - identity_iota/benches/diff_chain.rs | 107 - identity_iota/src/lib.rs | 56 +- .../Cargo.toml | 10 +- .../README.md | 0 .../src/chain/diff_chain.rs | 18 +- .../src/chain/document_chain.rs | 12 +- .../src/chain/document_history.rs | 10 +- .../src/chain/integration_chain.rs | 10 +- .../src/chain/milestone.rs | 4 +- .../src/chain/mod.rs | 0 .../src/document/mod.rs | 0 .../src/document/resolved_iota_document.rs | 10 +- .../src/error.rs | 2 +- .../src/lib.rs | 0 .../src/tangle/client.rs | 12 +- .../src/tangle/client_builder.rs | 2 +- .../src/tangle/explorer.rs | 14 +- .../src/tangle/message/compression_brotli.rs | 2 +- .../src/tangle/message/message_encoding.rs | 0 .../src/tangle/message/message_ext.rs | 14 +- .../src/tangle/message/message_index.rs | 4 +- .../src/tangle/message/message_version.rs | 0 .../src/tangle/message/mod.rs | 0 .../src/tangle/mod.rs | 0 .../src/tangle/publish.rs | 8 +- .../src/tangle/receipt.rs | 6 +- .../src/tangle/resolver.rs | 14 +- .../src/tangle/traits.rs | 6 +- identity_iota_core/Cargo.toml | 39 +- identity_iota_core/README.md | 6 +- .../src/client/identity_client.rs | 32 +- .../src/client/iota_client.rs | 29 +- .../src/client/mod.rs | 6 +- identity_iota_core/src/did/iota_did.rs | 931 +- identity_iota_core/src/did/mod.rs | 9 +- .../src/document/iota_document.rs | 1524 +--- .../src/document/iota_document_metadata.rs | 24 +- identity_iota_core/src/document/mod.rs | 12 +- identity_iota_core/src/error.rs | 52 +- identity_iota_core/src/lib.rs | 40 +- .../src/network/mod.rs | 0 .../src/network/network_name.rs | 4 +- .../src/state_metadata/document.rs | 62 +- .../src/state_metadata/encoding.rs | 0 .../src/state_metadata/mod.rs | 0 .../src/state_metadata/version.rs | 0 identity_iota_core_legacy/Cargo.toml | 37 + identity_iota_core_legacy/README.md | 4 + identity_iota_core_legacy/src/did/iota_did.rs | 442 + .../src/did/macros.rs | 4 +- identity_iota_core_legacy/src/did/mod.rs | 12 + .../src/did/segments.rs | 0 .../src/diff/diff_iota_did.rs | 0 .../src/diff/diff_iota_document.rs | 0 .../src/diff/diff_iota_document_metadata.rs | 0 .../src/diff/diff_message.rs | 0 .../src/diff/mod.rs | 0 .../src/document/iota_document.rs | 1680 ++++ .../src/document/iota_document_metadata.rs | 32 +- identity_iota_core_legacy/src/document/mod.rs | 13 + identity_iota_core_legacy/src/error.rs | 32 + identity_iota_core_legacy/src/lib.rs | 30 + .../src/tangle/message_id.rs | 0 .../src/tangle/mod.rs | 0 .../src/tangle/network.rs | 0 identity_resolver/Cargo.toml | 8 +- identity_resolver/src/resolution/resolver.rs | 16 +- .../tests/presentation_validation_errors.rs | 14 +- .../successful_presentation_validation.rs | 10 +- identity_stardust/Cargo.toml | 53 - identity_stardust/README.md | 5 - identity_stardust/src/did/mod.rs | 7 - identity_stardust/src/did/stardust_did.rs | 859 -- identity_stardust/src/document/mod.rs | 11 - .../src/document/stardust_document.rs | 764 -- identity_stardust/src/error.rs | 46 - identity_stardust/src/lib.rs | 30 - 255 files changed, 7519 insertions(+), 15084 deletions(-) delete mode 100644 bindings/wasm/examples-stardust/README.md delete mode 100644 bindings/wasm/examples/.gitignore delete mode 100644 bindings/wasm/examples/src/config.js delete mode 100644 bindings/wasm/examples/src/create_did.js rename bindings/wasm/{examples-stardust => examples}/src/ex0_create_did.ts (85%) rename bindings/wasm/{examples-stardust => examples}/src/ex1_update_did.ts (72%) rename bindings/wasm/{examples-stardust => examples}/src/ex2_resolve_did.ts (69%) rename bindings/wasm/{examples-stardust => examples}/src/ex3_deactivate_did.ts (81%) rename bindings/wasm/{examples-stardust => examples}/src/ex4_delete_did.ts (100%) delete mode 100644 bindings/wasm/examples/src/index.html delete mode 100644 bindings/wasm/examples/src/key_exchange.js rename bindings/wasm/{examples-stardust => examples}/src/main.ts (100%) delete mode 100644 bindings/wasm/examples/src/manipulate_did.js delete mode 100644 bindings/wasm/examples/src/node.js delete mode 100644 bindings/wasm/examples/src/private_tangle.js delete mode 100644 bindings/wasm/examples/src/resolve_did.js delete mode 100644 bindings/wasm/examples/src/resolve_history.js rename bindings/wasm/{examples-stardust => examples}/src/tests/ex0_create_did.ts (100%) rename bindings/wasm/{examples-stardust => examples}/src/tests/ex1_update_did.ts (100%) rename bindings/wasm/{examples-stardust => examples}/src/tests/ex2_resolve_did.ts (100%) rename bindings/wasm/{examples-stardust => examples}/src/tests/ex3_deactivate_did.ts (100%) rename bindings/wasm/{examples-stardust => examples}/src/tests/ex4_delete_did.ts (100%) delete mode 100644 bindings/wasm/examples/src/tests/node/createIdentity.js delete mode 100644 bindings/wasm/examples/src/tests/node/keyExchange.js delete mode 100644 bindings/wasm/examples/src/tests/node/manipulateIdentity.js delete mode 100644 bindings/wasm/examples/src/tests/node/privateTangle.js delete mode 100644 bindings/wasm/examples/src/tests/node/resolveHistory.js delete mode 100644 bindings/wasm/examples/src/tests/node/resolveIdentity.js delete mode 100644 bindings/wasm/examples/src/utils_web.js delete mode 100644 bindings/wasm/examples/src/web.js delete mode 100644 bindings/wasm/examples/webpack.config.js rename bindings/wasm/lib/{stardust_identity_client.ts => iota_identity_client.ts} (78%) delete mode 100644 bindings/wasm/src/chain/document_history.rs delete mode 100644 bindings/wasm/src/chain/mod.rs create mode 100644 bindings/wasm/src/did/service_common.rs delete mode 100644 bindings/wasm/src/did/wasm_diff_message.rs delete mode 100644 bindings/wasm/src/did/wasm_document.rs delete mode 100644 bindings/wasm/src/did/wasm_document_metadata.rs delete mode 100644 bindings/wasm/src/did/wasm_resolved_document.rs delete mode 100644 bindings/wasm/src/did/wasm_service.rs delete mode 100644 bindings/wasm/src/did/wasm_verification_method.rs create mode 100644 bindings/wasm/src/iota/identity_client.rs rename bindings/wasm/src/{stardust => iota}/identity_client_ext.rs (66%) rename bindings/wasm/src/{did/wasm_iota_did.rs => iota/iota_did.rs} (64%) rename bindings/wasm/src/{did/wasm_did_url.rs => iota/iota_did_url.rs} (61%) rename bindings/wasm/src/{stardust/stardust_document.rs => iota/iota_document.rs} (76%) rename bindings/wasm/src/{stardust/stardust_document_metadata.rs => iota/iota_document_metadata.rs} (68%) rename bindings/wasm/src/{stardust/state_metadata_encoding.rs => iota/iota_metadata_encoding.rs} (93%) rename bindings/wasm/src/{stardust/stardust_service.rs => iota/iota_service.rs} (60%) rename bindings/wasm/src/{stardust/stardust_verification_method.rs => iota/iota_verification_method.rs} (55%) create mode 100644 bindings/wasm/src/iota/mod.rs delete mode 100644 bindings/wasm/src/stardust/identity_client.rs delete mode 100644 bindings/wasm/src/stardust/mod.rs delete mode 100644 bindings/wasm/src/stardust/stardust_did.rs delete mode 100644 bindings/wasm/src/stardust/stardust_did_url.rs delete mode 100644 bindings/wasm/src/tangle/client.rs delete mode 100644 bindings/wasm/src/tangle/client_config.rs delete mode 100644 bindings/wasm/src/tangle/explorer.rs delete mode 100644 bindings/wasm/src/tangle/message.rs delete mode 100644 bindings/wasm/src/tangle/mod.rs delete mode 100644 bindings/wasm/src/tangle/network.rs delete mode 100644 bindings/wasm/src/tangle/receipt.rs delete mode 100644 bindings/wasm/src/tangle/resolver.rs delete mode 100644 bindings/wasm/tests/document.ts rename bindings/wasm/tests/{stardust.ts => iota.ts} (83%) rename bindings/wasm/tests/{ => legacy}/account.ts (97%) delete mode 100644 documentation/docs/concepts/decentralized_identifiers/private_tangle.mdx delete mode 100644 identity_iota/benches/benchmark.rs delete mode 100644 identity_iota/benches/diff_chain.rs rename {identity_iota_client => identity_iota_client_legacy}/Cargo.toml (88%) rename {identity_iota_client => identity_iota_client_legacy}/README.md (100%) rename {identity_iota_client => identity_iota_client_legacy}/src/chain/diff_chain.rs (96%) rename {identity_iota_client => identity_iota_client_legacy}/src/chain/document_chain.rs (98%) rename {identity_iota_client => identity_iota_client_legacy}/src/chain/document_history.rs (95%) rename {identity_iota_client => identity_iota_client_legacy}/src/chain/integration_chain.rs (96%) rename {identity_iota_client => identity_iota_client_legacy}/src/chain/milestone.rs (97%) rename {identity_iota_client => identity_iota_client_legacy}/src/chain/mod.rs (100%) rename {identity_iota_client => identity_iota_client_legacy}/src/document/mod.rs (100%) rename {identity_iota_client => identity_iota_client_legacy}/src/document/resolved_iota_document.rs (93%) rename {identity_iota_client => identity_iota_client_legacy}/src/error.rs (96%) rename {identity_iota_client => identity_iota_client_legacy}/src/lib.rs (100%) rename {identity_iota_client => identity_iota_client_legacy}/src/tangle/client.rs (97%) rename {identity_iota_client => identity_iota_client_legacy}/src/tangle/client_builder.rs (99%) rename {identity_iota_client => identity_iota_client_legacy}/src/tangle/explorer.rs (94%) rename {identity_iota_client => identity_iota_client_legacy}/src/tangle/message/compression_brotli.rs (96%) rename {identity_iota_client => identity_iota_client_legacy}/src/tangle/message/message_encoding.rs (100%) rename {identity_iota_client => identity_iota_client_legacy}/src/tangle/message/message_ext.rs (94%) rename {identity_iota_client => identity_iota_client_legacy}/src/tangle/message/message_index.rs (98%) rename {identity_iota_client => identity_iota_client_legacy}/src/tangle/message/message_version.rs (100%) rename {identity_iota_client => identity_iota_client_legacy}/src/tangle/message/mod.rs (100%) rename {identity_iota_client => identity_iota_client_legacy}/src/tangle/mod.rs (100%) rename {identity_iota_client => identity_iota_client_legacy}/src/tangle/publish.rs (96%) rename {identity_iota_client => identity_iota_client_legacy}/src/tangle/receipt.rs (88%) rename {identity_iota_client => identity_iota_client_legacy}/src/tangle/resolver.rs (98%) rename {identity_iota_client => identity_iota_client_legacy}/src/tangle/traits.rs (92%) rename {identity_stardust => identity_iota_core}/src/client/identity_client.rs (86%) rename {identity_stardust => identity_iota_core}/src/client/iota_client.rs (90%) rename {identity_stardust => identity_iota_core}/src/client/mod.rs (55%) rename {identity_stardust => identity_iota_core}/src/network/mod.rs (100%) rename {identity_stardust => identity_iota_core}/src/network/network_name.rs (94%) rename {identity_stardust => identity_iota_core}/src/state_metadata/document.rs (83%) rename {identity_stardust => identity_iota_core}/src/state_metadata/encoding.rs (100%) rename {identity_stardust => identity_iota_core}/src/state_metadata/mod.rs (100%) rename {identity_stardust => identity_iota_core}/src/state_metadata/version.rs (100%) create mode 100644 identity_iota_core_legacy/Cargo.toml create mode 100644 identity_iota_core_legacy/README.md create mode 100644 identity_iota_core_legacy/src/did/iota_did.rs rename {identity_iota_core => identity_iota_core_legacy}/src/did/macros.rs (91%) create mode 100644 identity_iota_core_legacy/src/did/mod.rs rename {identity_iota_core => identity_iota_core_legacy}/src/did/segments.rs (100%) rename {identity_iota_core => identity_iota_core_legacy}/src/diff/diff_iota_did.rs (100%) rename {identity_iota_core => identity_iota_core_legacy}/src/diff/diff_iota_document.rs (100%) rename {identity_iota_core => identity_iota_core_legacy}/src/diff/diff_iota_document_metadata.rs (100%) rename {identity_iota_core => identity_iota_core_legacy}/src/diff/diff_message.rs (100%) rename {identity_iota_core => identity_iota_core_legacy}/src/diff/mod.rs (100%) create mode 100644 identity_iota_core_legacy/src/document/iota_document.rs rename identity_stardust/src/document/stardust_document_metadata.rs => identity_iota_core_legacy/src/document/iota_document_metadata.rs (55%) create mode 100644 identity_iota_core_legacy/src/document/mod.rs create mode 100644 identity_iota_core_legacy/src/error.rs create mode 100644 identity_iota_core_legacy/src/lib.rs rename {identity_iota_core => identity_iota_core_legacy}/src/tangle/message_id.rs (100%) rename {identity_iota_core => identity_iota_core_legacy}/src/tangle/mod.rs (100%) rename {identity_iota_core => identity_iota_core_legacy}/src/tangle/network.rs (100%) delete mode 100644 identity_stardust/Cargo.toml delete mode 100644 identity_stardust/README.md delete mode 100644 identity_stardust/src/did/mod.rs delete mode 100644 identity_stardust/src/did/stardust_did.rs delete mode 100644 identity_stardust/src/document/mod.rs delete mode 100644 identity_stardust/src/document/stardust_document.rs delete mode 100644 identity_stardust/src/error.rs delete mode 100644 identity_stardust/src/lib.rs diff --git a/.github/actions/release/bump-versions/action.yml b/.github/actions/release/bump-versions/action.yml index 46641368fe..0243bed1ac 100644 --- a/.github/actions/release/bump-versions/action.yml +++ b/.github/actions/release/bump-versions/action.yml @@ -44,7 +44,7 @@ runs: working-directory: bindings/stronghold-nodejs run: | cargo add identity_core@=${{ inputs.version }} --path=../../identity_core - cargo add identity_iota_core@=${{ inputs.version }} --path=../../identity_iota_core + cargo add identity_iota_core_legacy@=${{ inputs.version }} --path=../../identity_iota_core_legacy cargo add identity_account_storage@=${{ inputs.version }} --path=../../identity_account_storage - name: Bump Rust crate versions diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index fb700cc3ba..5429d1759a 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -110,20 +110,20 @@ jobs: command: test args: --workspace --all-features --release - - name: Run Rust examples + - name: Run legacy examples # run examples only on ubuntu for now if: matrix.os == 'ubuntu-latest' run: | cargo metadata --format-version 1 --manifest-path ./examples_legacy/Cargo.toml | \ - jq -r '.packages[] | select(.name == "examples_legacy") | .targets[].name' + jq -r '.packages[] | select(.name == "examples_legacy") | .targets[].name' | \ parallel -k -j 4 --retries 3 ./target/release/examples/{} - - name: Run Stardust Rust examples + - name: Run Rust examples # run examples only on ubuntu for now if: matrix.os == 'ubuntu-latest' run: | cargo metadata --format-version 1 --manifest-path ./examples/Cargo.toml | \ - jq -r '.packages[] | select(.name == "examples") | .targets[].name' + jq -r '.packages[] | select(.name == "examples") | .targets[].name' | \ awk '$1 ~ /[0-9].*/' | \ parallel -k -j 4 --retries 3 ./target/release/examples/{} diff --git a/.github/workflows/shared-build-wasm.yml b/.github/workflows/shared-build-wasm.yml index 7112c9945b..f4c73527f5 100644 --- a/.github/workflows/shared-build-wasm.yml +++ b/.github/workflows/shared-build-wasm.yml @@ -80,9 +80,6 @@ jobs: if: ${{ inputs.run-unit-tests }} run: npm run test:unit:node - - name: Build Wasm examples - run: npm run build:examples - - name: Stop sccache uses: './.github/actions/rust/sccache/stop-sccache' with: diff --git a/.github/workflows/test-docs-build.yml b/.github/workflows/test-docs-build.yml index a93ae0d687..0596d19ca2 100644 --- a/.github/workflows/test-docs-build.yml +++ b/.github/workflows/test-docs-build.yml @@ -42,12 +42,3 @@ jobs: command: doc toolchain: nightly args: --all-features --no-deps --workspace - - - name: Test Stardust Rust Documentation - uses: actions-rs/cargo@v1 - env: - RUSTDOCFLAGS: "-D warnings --cfg docsrs" - with: - command: doc - toolchain: nightly - args: --manifest-path ./identity_stardust/Cargo.toml --all-features --no-deps --workspace diff --git a/Cargo.toml b/Cargo.toml index 5036e8a798..fafbf00c3e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,20 +10,16 @@ members = [ "identity_did", "identity_diff", "identity_iota", - "identity_iota_client", + "identity_iota_client_legacy", + "identity_iota_core_legacy", "identity_iota_core", "identity_resolver", - "identity_stardust", "examples_legacy", "examples", ] -exclude = [ - "bindings/stronghold-nodejs", - "bindings/wasm", - "libjose", -] +exclude = ["bindings/stronghold-nodejs", "bindings/wasm", "libjose"] [profile.dev] split-debuginfo = "unpacked" diff --git a/README.md b/README.md index 135b9856b6..0ed6ba4a4b 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ The individual libraries are developed to be agnostic about the utilized [Distri [Foreign Function Interface (FFI)](https://en.wikipedia.org/wiki/Foreign_function_interface) Bindings of this [Rust](https://www.rust-lang.org/) library to other programming languages are a work in progress (see Roadmap below). Currently available bindings are: -* [Web Assembly](https://github.com/iotaledger/identity.rs/blob/HEAD/bindings/wasm/) (JavaScript/TypeScript) +- [Web Assembly](https://github.com/iotaledger/identity.rs/blob/HEAD/bindings/wasm/) (JavaScript/TypeScript) ## Documentation and Resources @@ -55,6 +55,7 @@ The individual libraries are developed to be agnostic about the utilized [Distri ## Getting Started If you want to include IOTA Identity in your project, simply add it as a dependency in your `Cargo.toml`: + ```toml [dependencies] identity_iota = { version = "0.6" } @@ -68,9 +69,10 @@ To try out the [examples](https://github.com/iotaledger/identity.rs/blob/HEAD/ex ## Example: Creating an Identity -The following code creates and publishes a new IOTA DID Document to the Tangle Mainnet. +The following code creates and publishes a new IOTA DID Document to the Shimmer Testnet. + +_Cargo.toml_ -*Cargo.toml* ```toml [package] name = "iota_identity_example" @@ -78,82 +80,108 @@ version = "1.0.0" edition = "2021" [dependencies] -identity_iota = { version = "0.6" } +identity_iota = { version = "0.7" } +iota-client = { version = "2.0.0-beta.3", default-features = false, features = ["tls", "stronghold"] } tokio = { version = "1", features = ["full"] } ``` -*main.**rs* + +_main.__rs_ + ```rust,no_run -use identity_iota::account::Account; -use identity_iota::account::IdentitySetup; -use identity_iota::account::Result; -use identity_iota::account_storage::Stronghold; use identity_iota::core::ToJson; -use identity_iota::client::ExplorerUrl; -use identity_iota::client::ResolvedIotaDocument; - +use identity_iota::crypto::KeyPair; +use identity_iota::crypto::KeyType; +use identity_iota::did::MethodScope; +use identity_iota::iota::IotaClientExt; +use identity_iota::iota::IotaDocument; +use identity_iota::iota::IotaIdentityClientExt; +use identity_iota::iota::IotaVerificationMethod; +use identity_iota::iota::NetworkName; +use iota_client::block::address::Address; +use iota_client::block::output::AliasOutput; +use iota_client::crypto::keys::bip39; +use iota_client::secret::stronghold::StrongholdSecretManager; +use iota_client::secret::SecretManager; +use iota_client::Client; +use tokio::io::AsyncReadExt; + +// The endpoint of the IOTA node to use. +static API_ENDPOINT: &str = "https://api.testnet.shimmer.network/"; + +/// Demonstrates how to create a DID Document and publish it in a new Alias Output. #[tokio::main] -async fn main() -> Result<()> { - // Stronghold settings. - let stronghold_path: &str = "./example-strong.hodl"; - let password: String = "my-password".into(); - let stronghold: Stronghold = Stronghold::new(stronghold_path, password, None).await?; - - // Create a new identity with default settings and - // Stronghold as the storage. - let account: Account = Account::builder() - .storage(stronghold) - .create_identity(IdentitySetup::default()) - .await?; - - println!("[Example] Local Document = {:#?}", account.document()); - - // Fetch the DID Document from the Tangle - // - // This is an optional step to ensure DID Document consistency. - let resolved: ResolvedIotaDocument = account.resolve_identity().await?; - - println!("[Example] Tangle Document = {}", resolved.to_json_pretty()?); - - // Print the Identity Resolver Explorer URL. - let explorer: &ExplorerUrl = ExplorerUrl::mainnet(); - println!( - "[Example] Explore the DID Document = {}", - explorer.resolver_url(account.did())? - ); +async fn main() -> anyhow::Result<()> { + // Create a new client to interact with the IOTA ledger. + let client: Client = Client::builder().with_primary_node(API_ENDPOINT, None)?.finish()?; + + // Create a new Stronghold. + let mut stronghold = StrongholdSecretManager::builder() + .password("secure_password") + .build("./example-strong.hodl")?; + + // Generate a mnemonic and store it in the Stronghold. + let keypair = KeyPair::new(KeyType::Ed25519)?; + let mnemonic = bip39::wordlist::encode(keypair.private().as_ref(), &bip39::wordlist::ENGLISH) + .map_err(|err| anyhow::anyhow!("{err:?}"))?; + stronghold.store_mnemonic(mnemonic).await?; + + // Create a new secret manager backed by the Stronghold. + let secret_manager: SecretManager = SecretManager::Stronghold(stronghold); + + // Get an address from the secret manager. + let address: Address = client.get_addresses(&secret_manager).with_range(0..1).get_raw().await?[0]; + + // Get the Bech32 human-readable part (HRP) of the network. + let network_name: NetworkName = client.network_name().await?; + + println!("Your wallet address is: {}", address.to_bech32(network_name.as_ref())); + println!("Please request funds from https://faucet.testnet.shimmer.network/, then press Enter."); + tokio::io::stdin().read_u8().await?; + + // Create a new DID document with a placeholder DID. + // The DID will be derived from the Alias Id of the Alias Output after publishing. + let mut document: IotaDocument = IotaDocument::new(&network_name); + + // Insert a new Ed25519 verification method in the DID document. + let keypair: KeyPair = KeyPair::new(KeyType::Ed25519)?; + let method: IotaVerificationMethod = + IotaVerificationMethod::new(document.id().clone(), keypair.type_(), keypair.public(), "#key-1")?; + document.insert_method(method, MethodScope::VerificationMethod)?; + + // Construct an Alias Output containing the DID document, with the wallet address + // set as both the state controller and governor. + let alias_output: AliasOutput = client.new_did_output(address, document, None).await?; + println!("Alias Output: {}", alias_output.to_json()?); + + // Publish the Alias Output and get the published DID document. + let document: IotaDocument = client.publish_did_output(&secret_manager, alias_output).await?; + println!("Published DID document: {:#}", document); Ok(()) } ``` -*Example output* + +_Example output_ + ```json { "doc": { - "id": "did:iota:8nG4d85jnqTYGMWt5DL63FobHF5Ersuw4foQnEo66nbD", - "capabilityInvocation": [ + "id": "did:iota:rms:0x4113f08e360a3c1725bb1f93d94f6e807a1ef88f091d45f93513c3e88dac3248", + "verificationMethod": [ { - "id": "did:iota:8nG4d85jnqTYGMWt5DL63FobHF5Ersuw4foQnEo66nbD#sign-0", - "controller": "did:iota:8nG4d85jnqTYGMWt5DL63FobHF5Ersuw4foQnEo66nbD", + "id": "did:iota:rms:0x4113f08e360a3c1725bb1f93d94f6e807a1ef88f091d45f93513c3e88dac3248#key-1", + "controller": "did:iota:rms:0x4113f08e360a3c1725bb1f93d94f6e807a1ef88f091d45f93513c3e88dac3248", "type": "Ed25519VerificationKey2018", - "publicKeyMultibase": "zHCoXy5XR9BmxMfXK8GrKziPGJLFBnrfeuH3XR4GuQoR2" + "publicKeyMultibase": "z7BoQerJn9NxwcA4KHGK9CP5FJRRZJBmsxrGPvWiyuFGG" } ] }, "meta": { - "created": "2022-06-14T13:16:04Z", - "updated": "2022-06-14T13:16:04Z" - }, - "proof": { - "type": "JcsEd25519Signature2020", - "verificationMethod": "did:iota:8nG4d85jnqTYGMWt5DL63FobHF5Ersuw4foQnEo66nbD#sign-0", - "signatureValue": "2zx5UCTbcbzSRtPmNj12fzPe1fdGAPPEyT3WGjkP8ADb6xx5jj6E6tcGCYPgWi9YvohkwHSjAVPS5sD2Zac5deyW" - }, - "integrationMessageId": "446c1416eda4b40ec793f902fe4ba18e88d8f164637426d9239fc7c1b921c8c3" + "created": "2022-09-06T12:12:11Z", + "updated": "2022-09-06T12:12:11Z" + } } ``` -```text -[Example] Explore the DID Document = https://explorer.iota.org/mainnet/identity-resolver/did:iota:8nG4d85jnqTYGMWt5DL63FobHF5Ersuw4foQnEo66nbD -``` -The output link points to the [Identity Resolver on the IOTA Tangle Explorer](https://explorer.iota.org/mainnet/identity-resolver/did:iota:8jYcEGiNYUWcSdEtjCAcS97G58qq1VrWzW7M57BsHymz). ## Roadmap and Milestones @@ -163,20 +191,19 @@ IOTA Identity is in heavy development, and will naturally change as it matures a #### Basic Framework -| Feature | Not started | In Research | In Development | Done | Notes | -| :------------------------- | :---------: | :------: | :---------------: | :-: | :-------------------------------------------------------------------- | -| [IOTA DID Method](https://wiki.iota.org/identity.rs/specs/did/iota_did_method_spec) | | | | ✔️ | Finished implementation. | -| [Verifiable Credentials](https://www.w3.org/TR/vc-data-model/) | | | | ✔️ | Finished implementation. | -| Account | | | | ✔️ | Finished implementation. | -| Identity Actor | | | 🔶 | | | -| [DIDComm](https://wiki.iota.org/identity.rs/specs/didcomm/overview) | | | 🔶 | | In-progress with Actor | -| Selective Disclosure | | 🔶 | | | | -| Zero Knowledge Proofs | | 🔶 | | | | -| Support Embedded Rust | | 🔶 | | | | -| [WASM Bindings](https://github.com/iotaledger/identity.rs/blob/HEAD/bindings/wasm) | | | | ✔️ | Finished implementation. | -| [Code Examples](https://github.com/iotaledger/identity.rs/blob/HEAD/examples) | | | | ✔️ | | -| [Documentation Portal](https://wiki.iota.org/identity.rs/introduction) | | | 🔶 | | | - +| Feature | Not started | In Research | In Development | Done | Notes | +| :---------------------------------------------------------------------------------: | :---------: | :---------: | :------------: | :--: | :----------------------: | +| [IOTA DID Method](https://wiki.iota.org/identity.rs/specs/did/iota_did_method_spec) | | | | ✔️ | Finished implementation. | +| [Verifiable Credentials](https://www.w3.org/TR/vc-data-model/) | | | | ✔️ | Finished implementation. | +| Account | | | | ✔️ | Finished implementation. | +| [Identity Agent](https://github.com/iotaledger/identity.rs/tree/dev/identity_agent) | | | 🔶 | | | +| [DIDComm](https://wiki.iota.org/identity.rs/specs/didcomm/overview) | | | 🔶 | | In-progress with Agent | +| Selective Disclosure | | 🔶 | | | | +| Zero Knowledge Proofs | | 🔶 | | | | +| Support Embedded Rust | | 🔶 | | | | +| [WASM Bindings](https://github.com/iotaledger/identity.rs/blob/HEAD/bindings/wasm) | | | | ✔️ | Finished implementation. | +| [Code Examples](https://github.com/iotaledger/identity.rs/blob/HEAD/examples) | | | | ✔️ | | +| [Documentation Portal](https://wiki.iota.org/identity.rs/introduction) | | | 🔶 | | | #### Next Milestones diff --git a/bindings/stronghold-nodejs/Cargo.lock b/bindings/stronghold-nodejs/Cargo.lock index e1755bcd21..6edd12e8cb 100644 --- a/bindings/stronghold-nodejs/Cargo.lock +++ b/bindings/stronghold-nodejs/Cargo.lock @@ -730,7 +730,7 @@ dependencies = [ "identity_account_storage", "identity_core", "identity_did", - "identity_iota_core", + "identity_iota_core_legacy", "napi", "napi-build", "napi-derive", @@ -747,7 +747,7 @@ dependencies = [ "hashbrown", "identity_core", "identity_did", - "identity_iota_core", + "identity_iota_core_legacy", "iota-crypto 0.12.1", "iota_stronghold", "once_cell", @@ -792,7 +792,7 @@ dependencies = [ ] [[package]] -name = "identity_iota_core" +name = "identity_iota_core_legacy" version = "0.6.0" dependencies = [ "bee-message", diff --git a/bindings/stronghold-nodejs/Cargo.toml b/bindings/stronghold-nodejs/Cargo.toml index f46374b402..97f674e323 100644 --- a/bindings/stronghold-nodejs/Cargo.toml +++ b/bindings/stronghold-nodejs/Cargo.toml @@ -10,7 +10,7 @@ crate-type = ["cdylib"] identity_account_storage = { version = "=0.6.0", path = "../../identity_account_storage", default-features = false, features = ["stronghold", "send-sync-storage", "encryption"] } identity_core = { version = "=0.6.0", path = "../../identity_core", default-features = false } identity_did = { version = "=0.6.0", path = "../../identity_did", default-features = false } -identity_iota_core = { version = "=0.6.0", path = "../../identity_iota_core", default-features = false } +identity_iota_core_legacy = { version = "=0.6.0", path = "../../identity_iota_core_legacy", default-features = false } napi = { version = "2.4.3", default-features = false, features = ["napi4", "tokio_rt", "serde-json"] } napi-derive = { version = "2.4.1", default-features = false, features = ["compat-mode", "full"] } serde = { version = "1.0", default-features = false, features = ["derive"] } diff --git a/bindings/stronghold-nodejs/src/error.rs b/bindings/stronghold-nodejs/src/error.rs index 09ff585580..b1a792433f 100644 --- a/bindings/stronghold-nodejs/src/error.rs +++ b/bindings/stronghold-nodejs/src/error.rs @@ -3,7 +3,7 @@ use identity_account_storage::Result as AccountStorageResult; use identity_core::Result as CoreResult; -use identity_iota_core::Result as IotaCoreResult; +use identity_iota_core_legacy::Result as IotaCoreResult; use napi::bindgen_prelude::Error; use napi::Result; use serde_json::Result as SerdeResult; diff --git a/bindings/stronghold-nodejs/src/stronghold.rs b/bindings/stronghold-nodejs/src/stronghold.rs index 4b52446387..de9be2744e 100644 --- a/bindings/stronghold-nodejs/src/stronghold.rs +++ b/bindings/stronghold-nodejs/src/stronghold.rs @@ -8,7 +8,7 @@ use identity_account_storage::types::KeyLocation; use identity_core::crypto::PrivateKey; use identity_core::crypto::PublicKey; use identity_did::did::CoreDID; -use identity_iota_core::tangle::NetworkName; +use identity_iota_core_legacy::tangle::NetworkName; use napi::bindgen_prelude::Error; use napi::Result; use napi_derive::napi; diff --git a/bindings/stronghold-nodejs/src/types/derive.rs b/bindings/stronghold-nodejs/src/types/derive.rs index a216aff066..c6991f4cec 100644 --- a/bindings/stronghold-nodejs/src/types/derive.rs +++ b/bindings/stronghold-nodejs/src/types/derive.rs @@ -8,7 +8,7 @@ use identity_account_storage::types::EncryptionAlgorithm; use identity_account_storage::types::KeyLocation; use identity_account_storage::types::Signature; use identity_did::did::CoreDID; -use identity_iota_core::document::IotaDocument; +use identity_iota_core_legacy::document::IotaDocument; use napi::Result; use napi_derive::napi; diff --git a/bindings/wasm/Cargo.toml b/bindings/wasm/Cargo.toml index 3557b54054..75b74c4961 100644 --- a/bindings/wasm/Cargo.toml +++ b/bindings/wasm/Cargo.toml @@ -33,17 +33,6 @@ wasm-bindgen-futures = { version = "0.4", default-features = false } version = "=0.6.0" path = "../../identity_iota" default-features = false -features = [ - "account", - "storage-test-suite", - "unstable-encryption", - "revocation-bitmap", -] - -[dependencies.identity_stardust] -version = "=0.6.0" -path = "../../identity_stardust" -default-features = false features = ["client", "revocation-bitmap"] [dependencies.identity_resolver] @@ -53,6 +42,7 @@ default-features = false features = ["revocation-bitmap"] [dev-dependencies] +rand = "0.8.5" wasm-bindgen-test = { version = "0.3" } [target.'cfg(all(target_arch = "wasm32", not(target_os = "wasi")))'.dependencies] diff --git a/bindings/wasm/README.md b/bindings/wasm/README.md index 7052baf5c0..cf645de6c5 100644 --- a/bindings/wasm/README.md +++ b/bindings/wasm/README.md @@ -5,23 +5,26 @@ ## [API Reference](https://wiki.iota.org/identity.rs/libraries/wasm/api_reference) ## [Account Examples](https://github.com/iotaledger/identity.rs/blob/main/bindings/wasm/examples-account/README.md) + ## [Low-Level Examples](https://github.com/iotaledger/identity.rs/blob/main/bindings/wasm/examples/README.md) ## Install the library: Latest Release: this version matches the `main` branch of this repository, is stable and will have changelogs. + ```bash npm install @iota/identity-wasm ``` Development Release: this version usually matches the latest code changes from the `dev` branch and may see frequent breaking changes. + ```bash npm install @iota/identity-wasm@dev ``` ## Build -Alternatively, you can build the bindings yourself if you have Rust installed. If not, refer to [rustup.rs](https://rustup.rs) for the installation. +Alternatively, you can build the bindings yourself if you have Rust installed. If not, refer to [rustup.rs](https://rustup.rs) for the installation. Install [`wasm-bindgen-cli`](https://github.com/rustwasm/wasm-bindgen). A manual installation is required because we use the [Weak References](https://rustwasm.github.io/wasm-bindgen/reference/weak-references.html) feature, which [`wasm-pack` does not expose](https://github.com/rustwasm/wasm-pack/issues/930). @@ -30,6 +33,7 @@ cargo install --force wasm-bindgen-cli ``` Then, install the necessary dependencies using: + ```bash npm install ``` @@ -51,42 +55,111 @@ npm run build:web The minimum supported version for node is: `v16` ## NodeJS Usage - -```javascript -const identity = require('@iota/identity-wasm/node'); - +```typescript +const { + KeyPair, + KeyType, + MethodScope, + IotaDocument, + IotaVerificationMethod, + IotaService, + MethodRelationship, + IotaIdentityClient, +} = require('@iota/identity-wasm/node'); +const { Client } = require('@cycraig/iota-client-wasm/node'); + +const API_ENDPOINT = "https://api.testnet.shimmer.network/"; + +/** Demonstrate how to create a DID Document. */ async function main() { + // Create a new client with the given network endpoint. + const client = new Client({ + primaryNode: API_ENDPOINT, + localPow: true, + }); + + const didClient = new IotaIdentityClient(client); + + // Get the Bech32 human-readable part (HRP) of the network. + const networkHrp = await didClient.getNetworkHrp(); + + // Create a new DID document with a placeholder DID. + // The DID will be derived from the Alias Id of the Alias Output after publishing. + const document = new IotaDocument(networkHrp); + + // Insert a new Ed25519 verification method in the DID document. + let keypair = new KeyPair(KeyType.Ed25519); + let method = new IotaVerificationMethod( + document.id(), + keypair.type(), + keypair.public(), + "#key-1" + ); + document.insertMethod(method, MethodScope.VerificationMethod()); + + // Attach a new method relationship to the existing method. + document.attachMethodRelationship( + document.id().join("#key-1"), + MethodRelationship.Authentication + ); + + // Add a new Service. + const service = new IotaService({ + id: document.id().join("#linked-domain"), + type: "LinkedDomains", + serviceEndpoint: "https://iota.org/", + }); + document.insertService(service); + + console.log(`Created document `, JSON.stringify(document.toJSON(), null, 2)); +} - // The creation step generates a keypair, builds an identity - // and publishes it to the IOTA mainnet. - const builder = new identity.AccountBuilder(); - const account = await builder.createIdentity(); - - // Retrieve the DID of the newly created identity. - const did = account.did(); - - // Print the DID of the created Identity. - console.log(did.toString()) +main(); +``` - // Print the local state of the DID Document - console.log(account.document()); +which prints - // Print the Explorer URL for the DID. - console.log(`Explorer Url:`, identity.ExplorerUrl.mainnet().resolverUrl(did)); +``` +Created document { + "doc": { + "id": "did:iota:0x0000000000000000000000000000000000000000000000000000000000000000", + "verificationMethod": [ + { + "id": "did:iota:0x0000000000000000000000000000000000000000000000000000000000000000#key-1", + "controller": "did:iota:0x0000000000000000000000000000000000000000000000000000000000000000", + "type": "Ed25519VerificationKey2018", + "publicKeyMultibase": "z4SxypezRxr1YdMAJBePfHGxZ9hNZ53WVixZq3PbUcztW" + } + ], + "authentication": [ + "did:iota:0x0000000000000000000000000000000000000000000000000000000000000000#key-1" + ], + "service": [ + { + "id": "did:iota:0x0000000000000000000000000000000000000000000000000000000000000000#linked-domain", + "type": "LinkedDomains", + "serviceEndpoint": "https://iota.org/" + } + ] + }, + "meta": { + "created": "2022-09-09T11:29:32Z", + "updated": "2022-09-09T11:29:32Z" + } } - -main(); ``` +**NOTE: see the [examples](https://github.com/iotaledger/identity.rs/blob/main/bindings/wasm/examples/README.md) for how to publish an IOTA DID Document.** + ## Web Setup The library loads the WASM file with an HTTP GET request, so the .wasm file must be copied to the root of the dist folder. @@ -103,22 +176,23 @@ $ npm install rollup-plugin-copy --save-dev ```js // Include the copy plugin -import copy from 'rollup-plugin-copy' +import copy from "rollup-plugin-copy"; // Add the copy plugin to the `plugins` array of your rollup config: copy({ targets: [ - { - src: 'node_modules/@cycraig/iota-client-wasm/web/wasm/client_wasm_bg.wasm', - dest: 'public', - rename: 'client_wasm_bg.wasm' - }, - { - src: 'node_modules/@iota/identity-wasm/web/identity_wasm_bg.wasm', - dest: 'public', - rename: 'identity_wasm_bg.wasm' - }] -}) + { + src: "node_modules/@cycraig/iota-client-wasm/web/wasm/client_wasm_bg.wasm", + dest: "public", + rename: "client_wasm_bg.wasm", + }, + { + src: "node_modules/@iota/identity-wasm/web/identity_wasm_bg.wasm", + dest: "public", + rename: "identity_wasm_bg.wasm", + }, + ], +}); ``` ### Webpack @@ -151,49 +225,69 @@ new CopyWebPlugin({ ### Web Usage -```js +```typescript import * as client from "@cycraig/iota-client-wasm/web"; import * as identity from "@iota/identity-wasm/web"; -client.init().then(() => identity.init()).then(() => { - - // The creation step generates a keypair, builds an identity - // and publishes it to the IOTA mainnet. - let builder = new identity.AccountBuilder(); - let account = await builder.createIdentity(); - - // Retrieve the DID of the newly created identity. - const did = account.did(); - - // Print the DID of the created Identity. - console.log(did.toString()) - - // Print the local state of the DID Document - console.log(account.document()); +/** Demonstrate how to create a DID Document. */ +async function createDocument() { + // Create a new client with the given network endpoint. + const iotaClient = new client.Client({ + primaryNode: API_ENDPOINT, + localPow: true, + }); + + const didClient = new identity.IotaIdentityClient(iotaClient); + + // Get the Bech32 human-readable part (HRP) of the network. + const networkHrp = await didClient.getNetworkHrp(); + + // Create a new DID document with a placeholder DID. + // The DID will be derived from the Alias Id of the Alias Output after publishing. + const document = new identity.IotaDocument(networkHrp); + + // Insert a new Ed25519 verification method in the DID document. + let keypair = new identity.KeyPair(identity.KeyType.Ed25519); + let method = new identity.IotaVerificationMethod( + document.id(), + keypair.type(), + keypair.public(), + "#key-1" + ); + document.insertMethod(method, identity.MethodScope.VerificationMethod()); + + // Attach a new method relationship to the existing method. + document.attachMethodRelationship( + document.id().join("#key-1"), + identity.MethodRelationship.Authentication + ); + + // Add a new Service. + const service = new identity.IotaService({ + id: document.id().join("#linked-domain"), + type: "LinkedDomains", + serviceEndpoint: "https://iota.org/", + }); + document.insertService(service); + + console.log(`Created document `, JSON.stringify(document.toJSON(), null, 2)); +} -}); +client + .init() + .then(() => identity.init()) + .then(() => { + await createDocument(); + }); // or (async () => { await client.init(); await identity.init(); - - // The creation step generates a keypair, builds an identity - // and publishes it to the IOTA mainnet. - let builder = new identity.AccountBuilder(); - let account = await builder.createIdentity(); - - // Retrieve the DID of the newly created identity. - const did = account.did(); - // Print the DID of the created Identity. - console.log(did.toString()) - - // Print the local state of the DID Document - console.log(account.document()); - -})() + await createDocument(); +})(); // Default path is "identity_wasm_bg.wasm", but you can override it like this await identity.init("./static/identity_wasm_bg.wasm"); @@ -201,6 +295,8 @@ await identity.init("./static/identity_wasm_bg.wasm"); Calling `identity.init().then()` or `await identity.init()` is required to load the Wasm file from the server if not available, because of that it will only be slow for the first time. +**NOTE: see the [examples](https://github.com/iotaledger/identity.rs/blob/main/bindings/wasm/examples/README.md) for how to publish an IOTA DID Document.** + ## Examples in the Wild You may find it useful to see how the WASM bindings are being used in existing applications: diff --git a/bindings/wasm/docs/api-reference.md b/bindings/wasm/docs/api-reference.md index 9ef2da25c1..b05c6ec6f2 100644 --- a/bindings/wasm/docs/api-reference.md +++ b/bindings/wasm/docs/api-reference.md @@ -1,31 +1,6 @@ ## Classes
-
Account
-

An account manages one identity.

-

It handles private keys, writing to storage and -publishing to the Tangle.

-
-
AccountBuilder
-

An [Account] builder for easy account configuration.

-

To reduce memory usage, accounts created from the same builder share the same Storage -used to store identities, and the same Client used to publish identities to the Tangle.

-

The configuration on the other hand is cloned, and therefore unique for each built account. -This means a builder can be reconfigured in-between account creations, without affecting -the configuration of previously built accounts.

-
-
AgreementInfo
-

Agreement information used as the input for the concat KDF.

-
-
AutoSave
-
-
CekAlgorithm
-

Supported algorithms used to determine and potentially encrypt the content encryption key (CEK).

-
-
ChainState
-
-
Client
-
CoreDID

A method-agnostic Decentralized Identifier (DID).

@@ -48,52 +23,32 @@ the configuration of previously built accounts.

CredentialValidator
-
DIDUrl
-

A DID URL conforming to the IOTA DID method specification.

-
-
DiffChainHistory
-
-
DiffMessage
-

Defines the difference between two DID Documents' JSON representations.

-
-
Document
-
-
DocumentHistory
-

A DID Document's history and current state.

-
-
DocumentMetadata
-

Additional attributes related to an IOTA DID Document.

-
Duration

A span of time.

Ed25519
-
EncryptedData
-

The structure returned after encrypting data

+
IotaDID
+

A DID conforming to the IOTA DID method specification.

-
EncryptionAlgorithm
-

Supported content encryption algorithms.

+
IotaDIDUrl
+

A DID URL conforming to the IOTA DID method specification.

-
ExplorerUrl
+
IotaDocument
-
IntegrationChainHistory
-
-
IotaDID
-

A DID conforming to the IOTA DID method specification.

+
IotaDocumentMetadata
+

Additional attributes related to an IOTA DID Document.

+
+
IotaIdentityClientExt
+

An extension interface that provides helper functions for publication +and resolution of DID documents in Alias Outputs.

-
KeyLocation
-

The storage location of a verification method key.

-

A key is uniquely identified by the fragment and a hash of its public key. -Importantly, the fragment alone is insufficient to represent the storage location. -For example, when rotating a key, there will be two keys in storage for the -same identity with the same fragment. The key_hash disambiguates the keys in -situations like these.

-

The string representation of that location can be obtained via canonicalRepr.

+
IotaService
+

A Service adhering to the IOTA DID method specification.

-
KeyPair
+
IotaVerificationMethod
-
MethodContent
+
KeyPair
MethodData

Supported verification method data formats.

@@ -111,8 +66,6 @@ verifiable Credentials and Presentations.

Configuration

The resolver will only be able to resolve DID documents for methods it has been configured for in the constructor.

-
Network
-
Presentation
PresentationValidationOptions
@@ -132,60 +85,11 @@ See IProofOptions.

Associates a purpose with a Proof.

See https://w3c-ccg.github.io/security-vocab/#proofPurpose

-
Receipt
-
-
ResolvedDocument
-

An IOTA DID document resolved from the Tangle. Represents an integration chain message possibly -merged with one or more DiffMessages.

-
-
Resolver
-
-
ResolverBuilder
-

Builder for configuring [Clients][Client] when constructing a [Resolver].

-
RevocationBitmap

A compressed bitmap for managing credential revocation.

-
Service
-

A DID Document Service used to enable trusted interactions associated -with a DID subject.

-

See: https://www.w3.org/TR/did-core/#services

-
-
Signature
-

A digital signature.

-
-
StardustDID
-

A DID conforming to the IOTA UTXO DID method specification.

-
-
StardustDIDUrl
-

A DID URL conforming to the IOTA Stardust UTXO DID method specification.

-
-
StardustDocument
-
-
StardustDocumentMetadata
-

Additional attributes related to an IOTA DID Document.

-
-
StardustIdentityClientExt
-

An extension interface that provides helper functions for publication -and resolution of DID documents in Alias Outputs.

-
-
StardustService
-

A Service adhering to the IOTA UTXO DID method specification.

-
-
StardustVerificationMethod
-
-
StorageTestSuite
-

A test suite for the Storage interface.

-

This module contains a set of tests that a correct storage implementation -should pass. Note that not every edge case is tested.

-

Tests usually rely on multiple interface methods being implemented, so they should only -be run on a fully implemented version. That's why there is not a single test case for every -interface method.

-
Timestamp
-
VerificationMethod
-
VerifierOptions

Holds additional proof verification options. See IVerifierOptions.

@@ -198,13 +102,10 @@ See IVerifierOptions.

## Members
-
StateMetadataEncoding
-
MethodRelationship
-
DIDType
-

Supported types representing a DID that can be generated by the storage interface.

-
+
StateMetadataEncoding
+
StatusCheck

Controls validation behaviour when checking whether or not a credential has been revoked by its credentialStatus.

@@ -249,8 +150,6 @@ This variant is the default used if no other variant is specified when construct
KeyType
-
DIDMessageEncoding
-
## Functions @@ -261,6775 +160,3624 @@ This variant is the default used if no other variant is specified when construct
- - -## Account -An account manages one identity. + -It handles private keys, writing to storage and -publishing to the Tangle. +## CoreDID +A method-agnostic Decentralized Identifier (DID). **Kind**: global class -* [Account](#Account) - * [.createService(options)](#Account+createService) ⇒ Promise.<void> - * [.deleteMethod(options)](#Account+deleteMethod) ⇒ Promise.<void> - * [.deleteService(options)](#Account+deleteService) ⇒ Promise.<void> - * [.setController(options)](#Account+setController) ⇒ Promise.<void> - * [.attachMethodRelationships(options)](#Account+attachMethodRelationships) ⇒ Promise.<void> - * [.createMethod(options)](#Account+createMethod) ⇒ Promise.<void> - * [.detachMethodRelationships(options)](#Account+detachMethodRelationships) ⇒ Promise.<void> - * [.did()](#Account+did) ⇒ [IotaDID](#IotaDID) - * [.autopublish()](#Account+autopublish) ⇒ boolean - * [.autosave()](#Account+autosave) ⇒ [AutoSave](#AutoSave) - * [.document()](#Account+document) ⇒ [Document](#Document) - * [.resolveIdentity()](#Account+resolveIdentity) ⇒ [Promise.<ResolvedDocument>](#ResolvedDocument) - * [.deleteIdentity()](#Account+deleteIdentity) ⇒ Promise.<void> - * [.publish(publish_options)](#Account+publish) ⇒ Promise.<void> - * [.createSignedCredential(fragment, credential, options)](#Account+createSignedCredential) ⇒ [Promise.<Credential>](#Credential) - * [.createSignedDocument(fragment, document, options)](#Account+createSignedDocument) ⇒ [Promise.<Document>](#Document) - * [.createSignedPresentation(fragment, presentation, options)](#Account+createSignedPresentation) ⇒ [Promise.<Presentation>](#Presentation) - * [.createSignedData(fragment, data, options)](#Account+createSignedData) ⇒ Promise.<any> - * [.updateDocumentUnchecked(document)](#Account+updateDocumentUnchecked) ⇒ Promise.<void> - * [.fetchDocument()](#Account+fetchDocument) ⇒ Promise.<void> - * [.revokeCredentials(fragment, indices)](#Account+revokeCredentials) ⇒ Promise.<void> - * [.unrevokeCredentials(fragment, indices)](#Account+unrevokeCredentials) ⇒ Promise.<void> - * [.encryptData(plaintext, associated_data, encryption_algorithm, cek_algorithm, public_key)](#Account+encryptData) ⇒ [Promise.<EncryptedData>](#EncryptedData) - * [.decryptData(data, encryption_algorithm, cek_algorithm, fragment)](#Account+decryptData) ⇒ Promise.<Uint8Array> - * [.setAlsoKnownAs(options)](#Account+setAlsoKnownAs) ⇒ Promise.<void> +* [CoreDID](#CoreDID) + * _instance_ + * [.setMethodName(value)](#CoreDID+setMethodName) + * [.setMethodId(value)](#CoreDID+setMethodId) + * [.scheme()](#CoreDID+scheme) ⇒ string + * [.authority()](#CoreDID+authority) ⇒ string + * [.method()](#CoreDID+method) ⇒ string + * [.methodId()](#CoreDID+methodId) ⇒ string + * [.join(segment)](#CoreDID+join) ⇒ [CoreDIDUrl](#CoreDIDUrl) + * [.toUrl()](#CoreDID+toUrl) ⇒ [CoreDIDUrl](#CoreDIDUrl) + * [.intoUrl()](#CoreDID+intoUrl) ⇒ [CoreDIDUrl](#CoreDIDUrl) + * [.toString()](#CoreDID+toString) ⇒ string + * [.toJSON()](#CoreDID+toJSON) ⇒ any + * [.clone()](#CoreDID+clone) ⇒ [CoreDID](#CoreDID) + * _static_ + * [.parse(input)](#CoreDID.parse) ⇒ [CoreDID](#CoreDID) + * [.validMethodName(value)](#CoreDID.validMethodName) ⇒ boolean + * [.validMethodId(value)](#CoreDID.validMethodId) ⇒ boolean + * [.fromJSON(json)](#CoreDID.fromJSON) ⇒ [CoreDID](#CoreDID) - + -### account.createService(options) ⇒ Promise.<void> -Adds a new Service to the DID Document. +### coreDID.setMethodName(value) +Set the method name of the `CoreDID`. -**Kind**: instance method of [Account](#Account) +**Kind**: instance method of [CoreDID](#CoreDID) | Param | Type | | --- | --- | -| options | CreateServiceOptions | +| value | string | - + -### account.deleteMethod(options) ⇒ Promise.<void> -Deletes a verification method if the method exists. +### coreDID.setMethodId(value) +Set the method-specific-id of the `DID`. -**Kind**: instance method of [Account](#Account) +**Kind**: instance method of [CoreDID](#CoreDID) | Param | Type | | --- | --- | -| options | DeleteMethodOptions | - - +| value | string | -### account.deleteService(options) ⇒ Promise.<void> -Deletes a Service if it exists. + -**Kind**: instance method of [Account](#Account) +### coreDID.scheme() ⇒ string +Returns the `CoreDID` scheme. -| Param | Type | -| --- | --- | -| options | DeleteServiceOptions | +E.g. +- `"did:example:12345678" -> "did"` +- `"did:iota:smr:12345678" -> "did"` - +**Kind**: instance method of [CoreDID](#CoreDID) + -### account.setController(options) ⇒ Promise.<void> -Sets the controllers of the DID document. +### coreDID.authority() ⇒ string +Returns the `CoreDID` authority: the method name and method-id. -**Kind**: instance method of [Account](#Account) +E.g. +- `"did:example:12345678" -> "example:12345678"` +- `"did:iota:smr:12345678" -> "iota:smr:12345678"` -| Param | Type | -| --- | --- | -| options | SetControllerOptions | +**Kind**: instance method of [CoreDID](#CoreDID) + - +### coreDID.method() ⇒ string +Returns the `CoreDID` method name. -### account.attachMethodRelationships(options) ⇒ Promise.<void> -Attach one or more verification relationships to a method. +E.g. +- `"did:example:12345678" -> "example"` +- `"did:iota:smr:12345678" -> "iota"` -Note: the method must exist and be in the set of verification methods; -it cannot be an embedded method. +**Kind**: instance method of [CoreDID](#CoreDID) + -**Kind**: instance method of [Account](#Account) +### coreDID.methodId() ⇒ string +Returns the `CoreDID` method-specific ID. -| Param | Type | -| --- | --- | -| options | AttachMethodRelationshipOptions | +E.g. +- `"did:example:12345678" -> "12345678"` +- `"did:iota:smr:12345678" -> "smr:12345678"` - +**Kind**: instance method of [CoreDID](#CoreDID) + -### account.createMethod(options) ⇒ Promise.<void> -Adds a new verification method to the DID document. +### coreDID.join(segment) ⇒ [CoreDIDUrl](#CoreDIDUrl) +Construct a new `CoreDIDUrl` by joining with a relative DID Url string. -**Kind**: instance method of [Account](#Account) +**Kind**: instance method of [CoreDID](#CoreDID) | Param | Type | | --- | --- | -| options | CreateMethodOptions | - - - -### account.detachMethodRelationships(options) ⇒ Promise.<void> -Detaches the given relationship from the given method, if the method exists. +| segment | string | -**Kind**: instance method of [Account](#Account) + -| Param | Type | -| --- | --- | -| options | DetachMethodRelationshipOptions | +### coreDID.toUrl() ⇒ [CoreDIDUrl](#CoreDIDUrl) +Clones the `CoreDID` into a `CoreDIDUrl`. - +**Kind**: instance method of [CoreDID](#CoreDID) + -### account.did() ⇒ [IotaDID](#IotaDID) -Returns the [IotaDID](#IotaDID) of the managed identity. +### coreDID.intoUrl() ⇒ [CoreDIDUrl](#CoreDIDUrl) +Converts the `CoreDID` into a `CoreDIDUrl`, consuming it. -**Kind**: instance method of [Account](#Account) - +**Kind**: instance method of [CoreDID](#CoreDID) + -### account.autopublish() ⇒ boolean -Returns whether auto-publish is enabled. +### coreDID.toString() ⇒ string +Returns the `CoreDID` as a string. -**Kind**: instance method of [Account](#Account) - +**Kind**: instance method of [CoreDID](#CoreDID) + -### account.autosave() ⇒ [AutoSave](#AutoSave) -Returns the auto-save configuration value. +### coreDID.toJSON() ⇒ any +Serializes this to a JSON object. -**Kind**: instance method of [Account](#Account) - +**Kind**: instance method of [CoreDID](#CoreDID) + -### account.document() ⇒ [Document](#Document) -Returns a copy of the document managed by the `Account`. +### coreDID.clone() ⇒ [CoreDID](#CoreDID) +Deep clones the object. -Note: the returned document only has a valid signature after publishing an integration chain update. -In general, for use cases where the signature is required, it is advisable to resolve the -document from the Tangle. +**Kind**: instance method of [CoreDID](#CoreDID) + -**Kind**: instance method of [Account](#Account) - +### CoreDID.parse(input) ⇒ [CoreDID](#CoreDID) +Parses a `CoreDID` from the given `input`. -### account.resolveIdentity() ⇒ [Promise.<ResolvedDocument>](#ResolvedDocument) -Resolves the DID Document associated with this `Account` from the Tangle. +### Errors -**Kind**: instance method of [Account](#Account) - +Throws an error if the input is not a valid `CoreDID`. -### account.deleteIdentity() ⇒ Promise.<void> -Removes the identity from the local storage entirely. +**Kind**: static method of [CoreDID](#CoreDID) -Note: This will remove all associated document updates and key material - recovery is NOT POSSIBLE! +| Param | Type | +| --- | --- | +| input | string | -**Kind**: instance method of [Account](#Account) - + -### account.publish(publish_options) ⇒ Promise.<void> -Push all unpublished changes to the tangle in a single message. +### CoreDID.validMethodName(value) ⇒ boolean +Validates whether a string is a valid DID method name. -**Kind**: instance method of [Account](#Account) +**Kind**: static method of [CoreDID](#CoreDID) | Param | Type | | --- | --- | -| publish_options | PublishOptions \| undefined | +| value | string | - + -### account.createSignedCredential(fragment, credential, options) ⇒ [Promise.<Credential>](#Credential) -Signs a [Credential](#Credential) with the key specified by `fragment`. +### CoreDID.validMethodId(value) ⇒ boolean +Validates whether a string is a valid `DID` method-id. -**Kind**: instance method of [Account](#Account) +**Kind**: static method of [CoreDID](#CoreDID) | Param | Type | | --- | --- | -| fragment | string | -| credential | [Credential](#Credential) | -| options | [ProofOptions](#ProofOptions) | +| value | string | - + -### account.createSignedDocument(fragment, document, options) ⇒ [Promise.<Document>](#Document) -Signs a [Document](#Document) with the key specified by `fragment`. +### CoreDID.fromJSON(json) ⇒ [CoreDID](#CoreDID) +Deserializes an instance from a JSON object. -**Kind**: instance method of [Account](#Account) +**Kind**: static method of [CoreDID](#CoreDID) | Param | Type | | --- | --- | -| fragment | string | -| document | [Document](#Document) | -| options | [ProofOptions](#ProofOptions) | +| json | any | - + -### account.createSignedPresentation(fragment, presentation, options) ⇒ [Promise.<Presentation>](#Presentation) -Signs a [Presentation](#Presentation) the key specified by `fragment`. +## CoreDIDUrl +A method agnostic DID Url. -**Kind**: instance method of [Account](#Account) +**Kind**: global class -| Param | Type | -| --- | --- | -| fragment | string | -| presentation | [Presentation](#Presentation) | -| options | [ProofOptions](#ProofOptions) | +* [CoreDIDUrl](#CoreDIDUrl) + * _instance_ + * [.did()](#CoreDIDUrl+did) ⇒ [CoreDID](#CoreDID) + * [.urlStr()](#CoreDIDUrl+urlStr) ⇒ string + * [.fragment()](#CoreDIDUrl+fragment) ⇒ string \| undefined + * [.setFragment(value)](#CoreDIDUrl+setFragment) + * [.path()](#CoreDIDUrl+path) ⇒ string \| undefined + * [.setPath(value)](#CoreDIDUrl+setPath) + * [.query()](#CoreDIDUrl+query) ⇒ string \| undefined + * [.setQuery(value)](#CoreDIDUrl+setQuery) + * [.join(segment)](#CoreDIDUrl+join) ⇒ [CoreDIDUrl](#CoreDIDUrl) + * [.toString()](#CoreDIDUrl+toString) ⇒ string + * [.toJSON()](#CoreDIDUrl+toJSON) ⇒ any + * [.clone()](#CoreDIDUrl+clone) ⇒ [CoreDIDUrl](#CoreDIDUrl) + * _static_ + * [.parse(input)](#CoreDIDUrl.parse) ⇒ [CoreDIDUrl](#CoreDIDUrl) + * [.fromJSON(json)](#CoreDIDUrl.fromJSON) ⇒ [CoreDIDUrl](#CoreDIDUrl) - + -### account.createSignedData(fragment, data, options) ⇒ Promise.<any> -Signs arbitrary `data` with the key specified by `fragment`. +### coreDIDUrl.did() ⇒ [CoreDID](#CoreDID) +Return a copy of the `CoreDID` section of the `CoreDIDUrl`. -**Kind**: instance method of [Account](#Account) +**Kind**: instance method of [CoreDIDUrl](#CoreDIDUrl) + -| Param | Type | -| --- | --- | -| fragment | string | -| data | any | -| options | [ProofOptions](#ProofOptions) | +### coreDIDUrl.urlStr() ⇒ string +Return a copy of the relative DID Url as a string, including only the path, query, and fragment. - +**Kind**: instance method of [CoreDIDUrl](#CoreDIDUrl) + -### account.updateDocumentUnchecked(document) ⇒ Promise.<void> -Overwrites the [Document](#Document) this account manages, **without doing any validation**. +### coreDIDUrl.fragment() ⇒ string \| undefined +Returns a copy of the `CoreDIDUrl` method fragment, if any. Excludes the leading '#'. -### WARNING +**Kind**: instance method of [CoreDIDUrl](#CoreDIDUrl) + -This method is dangerous and can easily corrupt the internal state, -potentially making the identity unusable. Only call this if you fully -understand the implications! +### coreDIDUrl.setFragment(value) +Sets the `fragment` component of the `CoreDIDUrl`. -**Kind**: instance method of [Account](#Account) +**Kind**: instance method of [CoreDIDUrl](#CoreDIDUrl) | Param | Type | | --- | --- | -| document | [Document](#Document) | - - +| value | string \| undefined | -### account.fetchDocument() ⇒ Promise.<void> -Fetches the latest changes from the tangle and **overwrites** the local document. + -If a DID is managed from distributed accounts, this should be called before making changes -to the identity, to avoid publishing updates that would be ignored. +### coreDIDUrl.path() ⇒ string \| undefined +Returns a copy of the `CoreDIDUrl` path. -**Kind**: instance method of [Account](#Account) - +**Kind**: instance method of [CoreDIDUrl](#CoreDIDUrl) + -### account.revokeCredentials(fragment, indices) ⇒ Promise.<void> -If the document has a `RevocationBitmap` service identified by `fragment`, -revoke all specified `indices`. +### coreDIDUrl.setPath(value) +Sets the `path` component of the `CoreDIDUrl`. -**Kind**: instance method of [Account](#Account) +**Kind**: instance method of [CoreDIDUrl](#CoreDIDUrl) | Param | Type | | --- | --- | -| fragment | string | -| indices | number \| Array.<number> | +| value | string \| undefined | - + -### account.unrevokeCredentials(fragment, indices) ⇒ Promise.<void> -If the document has a `RevocationBitmap` service identified by `fragment`, -unrevoke all specified `indices`. +### coreDIDUrl.query() ⇒ string \| undefined +Returns a copy of the `CoreDIDUrl` method query, if any. Excludes the leading '?'. + +**Kind**: instance method of [CoreDIDUrl](#CoreDIDUrl) + + +### coreDIDUrl.setQuery(value) +Sets the `query` component of the `CoreDIDUrl`. -**Kind**: instance method of [Account](#Account) +**Kind**: instance method of [CoreDIDUrl](#CoreDIDUrl) | Param | Type | | --- | --- | -| fragment | string | -| indices | number \| Array.<number> | +| value | string \| undefined | - - -### account.encryptData(plaintext, associated_data, encryption_algorithm, cek_algorithm, public_key) ⇒ [Promise.<EncryptedData>](#EncryptedData) -Encrypts the given `plaintext` with the specified `encryption_algorithm` and `cek_algorithm`. - -Returns an [`EncryptedData`] instance. - -**Kind**: instance method of [Account](#Account) - -| Param | Type | -| --- | --- | -| plaintext | Uint8Array | -| associated_data | Uint8Array | -| encryption_algorithm | [EncryptionAlgorithm](#EncryptionAlgorithm) | -| cek_algorithm | [CekAlgorithm](#CekAlgorithm) | -| public_key | Uint8Array | - - - -### account.decryptData(data, encryption_algorithm, cek_algorithm, fragment) ⇒ Promise.<Uint8Array> -Decrypts the given `data` with the key identified by `fragment` using the given `encryption_algorithm` and -`cek_algorithm`. - -Returns the decrypted text. - -**Kind**: instance method of [Account](#Account) + -| Param | Type | -| --- | --- | -| data | [EncryptedData](#EncryptedData) | -| encryption_algorithm | [EncryptionAlgorithm](#EncryptionAlgorithm) | -| cek_algorithm | [CekAlgorithm](#CekAlgorithm) | -| fragment | string | +### coreDIDUrl.join(segment) ⇒ [CoreDIDUrl](#CoreDIDUrl) +Append a string representing a path, query, and/or fragment, returning a new `CoreDIDUrl`. - +Must begin with a valid delimiter character: '/', '?', '#'. Overwrites the existing URL +segment and any following segments in order of path, query, then fragment. -### account.setAlsoKnownAs(options) ⇒ Promise.<void> -Sets the `alsoKnownAs` property in the DID document. +I.e. +- joining a path will clear the query and fragment. +- joining a query will clear the fragment. +- joining a fragment will only overwrite the fragment. -**Kind**: instance method of [Account](#Account) +**Kind**: instance method of [CoreDIDUrl](#CoreDIDUrl) | Param | Type | | --- | --- | -| options | SetAlsoKnownAsOptions | - - - -## AccountBuilder -An [`Account`] builder for easy account configuration. - -To reduce memory usage, accounts created from the same builder share the same `Storage` -used to store identities, and the same [Client](#Client) used to publish identities to the Tangle. - -The configuration on the other hand is cloned, and therefore unique for each built account. -This means a builder can be reconfigured in-between account creations, without affecting -the configuration of previously built accounts. +| segment | string | -**Kind**: global class + -* [AccountBuilder](#AccountBuilder) - * [new AccountBuilder(options)](#new_AccountBuilder_new) - * [.loadIdentity(did)](#AccountBuilder+loadIdentity) ⇒ [Promise.<Account>](#Account) - * [.createIdentity(identity_setup)](#AccountBuilder+createIdentity) ⇒ [Promise.<Account>](#Account) +### coreDIDUrl.toString() ⇒ string +Returns the `CoreDIDUrl` as a string. - +**Kind**: instance method of [CoreDIDUrl](#CoreDIDUrl) + -### new AccountBuilder(options) -Creates a new `AccountBuilder`. +### coreDIDUrl.toJSON() ⇒ any +Serializes this to a JSON object. +**Kind**: instance method of [CoreDIDUrl](#CoreDIDUrl) + -| Param | Type | -| --- | --- | -| options | AccountBuilderOptions \| undefined | +### coreDIDUrl.clone() ⇒ [CoreDIDUrl](#CoreDIDUrl) +Deep clones the object. - +**Kind**: instance method of [CoreDIDUrl](#CoreDIDUrl) + -### accountBuilder.loadIdentity(did) ⇒ [Promise.<Account>](#Account) -Loads an existing identity with the specified `did` using the current builder configuration. -The identity must exist in the configured `Storage`. +### CoreDIDUrl.parse(input) ⇒ [CoreDIDUrl](#CoreDIDUrl) +Parses a `CoreDIDUrl` from the input string. -**Kind**: instance method of [AccountBuilder](#AccountBuilder) +**Kind**: static method of [CoreDIDUrl](#CoreDIDUrl) | Param | Type | | --- | --- | -| did | [IotaDID](#IotaDID) | - - +| input | string | -### accountBuilder.createIdentity(identity_setup) ⇒ [Promise.<Account>](#Account) -Creates a new identity based on the builder configuration and returns -an [Account](#Account) object to manage it. + -The identity is stored locally in the `Storage`. The DID network is automatically determined -by the [Client](#Client) used to publish it. +### CoreDIDUrl.fromJSON(json) ⇒ [CoreDIDUrl](#CoreDIDUrl) +Deserializes an instance from a JSON object. -**Kind**: instance method of [AccountBuilder](#AccountBuilder) +**Kind**: static method of [CoreDIDUrl](#CoreDIDUrl) | Param | Type | | --- | --- | -| identity_setup | IdentitySetup \| undefined | +| json | any | - + -## AgreementInfo -Agreement information used as the input for the concat KDF. +## CoreDocument +A method-agnostic DID Document. **Kind**: global class -* [AgreementInfo](#AgreementInfo) - * [new AgreementInfo(apu, apv, pub_info, priv_info)](#new_AgreementInfo_new) +* [CoreDocument](#CoreDocument) + * [new CoreDocument(values)](#new_CoreDocument_new) * _instance_ - * [.apu()](#AgreementInfo+apu) ⇒ Uint8Array - * [.apv()](#AgreementInfo+apv) ⇒ Uint8Array - * [.pubInfo()](#AgreementInfo+pubInfo) ⇒ Uint8Array - * [.privInfo()](#AgreementInfo+privInfo) ⇒ Uint8Array - * [.toJSON()](#AgreementInfo+toJSON) ⇒ any + * [.id()](#CoreDocument+id) ⇒ [CoreDID](#CoreDID) + * [.setId(id)](#CoreDocument+setId) + * [.controller()](#CoreDocument+controller) ⇒ [Array.<CoreDID>](#CoreDID) + * [.setController(controllers)](#CoreDocument+setController) + * [.alsoKnownAs()](#CoreDocument+alsoKnownAs) ⇒ Array.<string> + * [.setAlsoKnownAs(urls)](#CoreDocument+setAlsoKnownAs) + * [.verificatonMethod()](#CoreDocument+verificatonMethod) ⇒ [Array.<CoreVerificationMethod>](#CoreVerificationMethod) + * [.authentication()](#CoreDocument+authentication) ⇒ Array.<(CoreDIDUrl\|CoreVerificationMethod)> + * [.assertionMethod()](#CoreDocument+assertionMethod) ⇒ Array.<(CoreDIDUrl\|CoreVerificationMethod)> + * [.keyAgreement()](#CoreDocument+keyAgreement) ⇒ Array.<(CoreDIDUrl\|CoreVerificationMethod)> + * [.capabilityDelegation()](#CoreDocument+capabilityDelegation) ⇒ Array.<(CoreDIDUrl\|CoreVerificationMethod)> + * [.capabilityInvocation()](#CoreDocument+capabilityInvocation) ⇒ Array.<(CoreDIDUrl\|CoreVerificationMethod)> + * [.properties()](#CoreDocument+properties) ⇒ Map.<string, any> + * [.setPropertyUnchecked(key, value)](#CoreDocument+setPropertyUnchecked) + * [.service()](#CoreDocument+service) ⇒ [Array.<CoreService>](#CoreService) + * [.insertService(service)](#CoreDocument+insertService) ⇒ boolean + * [.removeService(didUrl)](#CoreDocument+removeService) ⇒ boolean + * [.resolveService(query)](#CoreDocument+resolveService) ⇒ [CoreService](#CoreService) \| undefined + * [.methods()](#CoreDocument+methods) ⇒ [Array.<CoreVerificationMethod>](#CoreVerificationMethod) + * [.verificationRelationships()](#CoreDocument+verificationRelationships) ⇒ Array.<(CoreDIDUrl\|CoreVerificationMethod)> + * [.insertMethod(method, scope)](#CoreDocument+insertMethod) + * [.removeMethod(did)](#CoreDocument+removeMethod) + * [.resolveMethod(query, scope)](#CoreDocument+resolveMethod) ⇒ [CoreVerificationMethod](#CoreVerificationMethod) \| undefined + * [.attachMethodRelationship(didUrl, relationship)](#CoreDocument+attachMethodRelationship) ⇒ boolean + * [.detachMethodRelationship(didUrl, relationship)](#CoreDocument+detachMethodRelationship) ⇒ boolean + * [.verifyData(data, options)](#CoreDocument+verifyData) ⇒ boolean + * [.revokeCredentials(serviceQuery, indices)](#CoreDocument+revokeCredentials) + * [.unrevokeCredentials(serviceQuery, indices)](#CoreDocument+unrevokeCredentials) + * [.signData(data, privateKey, methodQuery, options)](#CoreDocument+signData) ⇒ any + * [.toJSON()](#CoreDocument+toJSON) ⇒ any + * [.clone()](#CoreDocument+clone) ⇒ [CoreDocument](#CoreDocument) * _static_ - * [.fromJSON(json)](#AgreementInfo.fromJSON) ⇒ [AgreementInfo](#AgreementInfo) + * [.fromJSON(json)](#CoreDocument.fromJSON) ⇒ [CoreDocument](#CoreDocument) - + -### new AgreementInfo(apu, apv, pub_info, priv_info) -Creates an `AgreementInfo` Object. +### new CoreDocument(values) +Creates a new `CoreDocument` with the given properties. | Param | Type | | --- | --- | -| apu | Uint8Array | -| apv | Uint8Array | -| pub_info | Uint8Array | -| priv_info | Uint8Array | - - +| values | ICoreDocument | -### agreementInfo.apu() ⇒ Uint8Array -Returns a copy of `apu' + -**Kind**: instance method of [AgreementInfo](#AgreementInfo) - +### coreDocument.id() ⇒ [CoreDID](#CoreDID) +Returns a copy of the DID Document `id`. -### agreementInfo.apv() ⇒ Uint8Array -Returns a copy of `apv' +**Kind**: instance method of [CoreDocument](#CoreDocument) + -**Kind**: instance method of [AgreementInfo](#AgreementInfo) - +### coreDocument.setId(id) +Sets the DID of the document. -### agreementInfo.pubInfo() ⇒ Uint8Array -Returns a copy of `pubInfo' +**Kind**: instance method of [CoreDocument](#CoreDocument) -**Kind**: instance method of [AgreementInfo](#AgreementInfo) - +| Param | Type | +| --- | --- | +| id | [CoreDID](#CoreDID) | -### agreementInfo.privInfo() ⇒ Uint8Array -Returns a copy of `privInfo' + -**Kind**: instance method of [AgreementInfo](#AgreementInfo) - +### coreDocument.controller() ⇒ [Array.<CoreDID>](#CoreDID) +Returns a copy of the document controllers. -### agreementInfo.toJSON() ⇒ any -Serializes this to a JSON object. +**Kind**: instance method of [CoreDocument](#CoreDocument) + -**Kind**: instance method of [AgreementInfo](#AgreementInfo) - +### coreDocument.setController(controllers) +Sets the controllers of the DID Document. -### AgreementInfo.fromJSON(json) ⇒ [AgreementInfo](#AgreementInfo) -Deserializes an instance from a JSON object. +Note: Duplicates will be ignored. +Use `null` to remove all controllers. -**Kind**: static method of [AgreementInfo](#AgreementInfo) +**Kind**: instance method of [CoreDocument](#CoreDocument) | Param | Type | | --- | --- | -| json | any | - - - -## AutoSave -**Kind**: global class +| controllers | [CoreDID](#CoreDID) \| [Array.<CoreDID>](#CoreDID) \| null | -* [AutoSave](#AutoSave) - * _instance_ - * [.toJSON()](#AutoSave+toJSON) ⇒ any - * _static_ - * [.never()](#AutoSave.never) ⇒ [AutoSave](#AutoSave) - * [.every()](#AutoSave.every) ⇒ [AutoSave](#AutoSave) - * [.batch(number_of_actions)](#AutoSave.batch) ⇒ [AutoSave](#AutoSave) - * [.fromJSON(json)](#AutoSave.fromJSON) ⇒ [AutoSave](#AutoSave) + - +### coreDocument.alsoKnownAs() ⇒ Array.<string> +Returns a copy of the document's `alsoKnownAs` set. -### autoSave.toJSON() ⇒ any -Serializes this to a JSON object. +**Kind**: instance method of [CoreDocument](#CoreDocument) + -**Kind**: instance method of [AutoSave](#AutoSave) - +### coreDocument.setAlsoKnownAs(urls) +Sets the `alsoKnownAs` property in the DID document. -### AutoSave.never() ⇒ [AutoSave](#AutoSave) -Never save. +**Kind**: instance method of [CoreDocument](#CoreDocument) -**Kind**: static method of [AutoSave](#AutoSave) - +| Param | Type | +| --- | --- | +| urls | string \| Array.<string> \| null | -### AutoSave.every() ⇒ [AutoSave](#AutoSave) -Save after every action. + -**Kind**: static method of [AutoSave](#AutoSave) - +### coreDocument.verificatonMethod() ⇒ [Array.<CoreVerificationMethod>](#CoreVerificationMethod) +Returns a copy of the document's `verificationMethod` set. -### AutoSave.batch(number_of_actions) ⇒ [AutoSave](#AutoSave) -Save after every N actions. +**Kind**: instance method of [CoreDocument](#CoreDocument) + -**Kind**: static method of [AutoSave](#AutoSave) +### coreDocument.authentication() ⇒ Array.<(CoreDIDUrl\|CoreVerificationMethod)> +Returns a copy of the document's `authentication` set. -| Param | Type | -| --- | --- | -| number_of_actions | number | +**Kind**: instance method of [CoreDocument](#CoreDocument) + - +### coreDocument.assertionMethod() ⇒ Array.<(CoreDIDUrl\|CoreVerificationMethod)> +Returns a copy of the document's `assertionMethod` set. -### AutoSave.fromJSON(json) ⇒ [AutoSave](#AutoSave) -Deserializes an instance from a JSON object. +**Kind**: instance method of [CoreDocument](#CoreDocument) + -**Kind**: static method of [AutoSave](#AutoSave) +### coreDocument.keyAgreement() ⇒ Array.<(CoreDIDUrl\|CoreVerificationMethod)> +Returns a copy of the document's `keyAgreement` set. -| Param | Type | -| --- | --- | -| json | any | +**Kind**: instance method of [CoreDocument](#CoreDocument) + - +### coreDocument.capabilityDelegation() ⇒ Array.<(CoreDIDUrl\|CoreVerificationMethod)> +Returns a copy of the document's `capabilityDelegation` set. -## CekAlgorithm -Supported algorithms used to determine and potentially encrypt the content encryption key (CEK). +**Kind**: instance method of [CoreDocument](#CoreDocument) + -**Kind**: global class +### coreDocument.capabilityInvocation() ⇒ Array.<(CoreDIDUrl\|CoreVerificationMethod)> +Returns a copy of the document's `capabilityInvocation` set. -* [CekAlgorithm](#CekAlgorithm) - * _instance_ - * [.toJSON()](#CekAlgorithm+toJSON) ⇒ any - * _static_ - * [.EcdhEs(agreement)](#CekAlgorithm.EcdhEs) ⇒ [CekAlgorithm](#CekAlgorithm) - * [.EcdhEsA256Kw(agreement)](#CekAlgorithm.EcdhEsA256Kw) ⇒ [CekAlgorithm](#CekAlgorithm) - * [.fromJSON(json)](#CekAlgorithm.fromJSON) ⇒ [CekAlgorithm](#CekAlgorithm) +**Kind**: instance method of [CoreDocument](#CoreDocument) + - +### coreDocument.properties() ⇒ Map.<string, any> +Returns a copy of the custom DID Document properties. -### cekAlgorithm.toJSON() ⇒ any -Serializes this to a JSON object. +**Kind**: instance method of [CoreDocument](#CoreDocument) + -**Kind**: instance method of [CekAlgorithm](#CekAlgorithm) - +### coreDocument.setPropertyUnchecked(key, value) +Sets a custom property in the DID Document. +If the value is set to `null`, the custom property will be removed. -### CekAlgorithm.EcdhEs(agreement) ⇒ [CekAlgorithm](#CekAlgorithm) -Elliptic Curve Diffie-Hellman Ephemeral Static key agreement using Concat KDF. +### WARNING +This method can overwrite existing properties like `id` and result in an invalid document. -**Kind**: static method of [CekAlgorithm](#CekAlgorithm) +**Kind**: instance method of [CoreDocument](#CoreDocument) | Param | Type | | --- | --- | -| agreement | [AgreementInfo](#AgreementInfo) | - - +| key | string | +| value | any | -### CekAlgorithm.EcdhEsA256Kw(agreement) ⇒ [CekAlgorithm](#CekAlgorithm) -Elliptic Curve Diffie-Hellman Ephemeral Static key agreement using Concat KDF. + -**Kind**: static method of [CekAlgorithm](#CekAlgorithm) +### coreDocument.service() ⇒ [Array.<CoreService>](#CoreService) +Returns a set of all [CoreService](#CoreService) in the document. -| Param | Type | -| --- | --- | -| agreement | [AgreementInfo](#AgreementInfo) | +**Kind**: instance method of [CoreDocument](#CoreDocument) + - +### coreDocument.insertService(service) ⇒ boolean +Add a new [CoreService](#CoreService) to the document. -### CekAlgorithm.fromJSON(json) ⇒ [CekAlgorithm](#CekAlgorithm) -Deserializes an instance from a JSON object. +Returns `true` if the service was added. -**Kind**: static method of [CekAlgorithm](#CekAlgorithm) +**Kind**: instance method of [CoreDocument](#CoreDocument) | Param | Type | | --- | --- | -| json | any | - - - -## ChainState -**Kind**: global class +| service | [CoreService](#CoreService) | -* [ChainState](#ChainState) - * _instance_ - * [.toJSON()](#ChainState+toJSON) ⇒ any - * [.clone()](#ChainState+clone) ⇒ [ChainState](#ChainState) - * _static_ - * [.fromJSON(json)](#ChainState.fromJSON) ⇒ [ChainState](#ChainState) + - +### coreDocument.removeService(didUrl) ⇒ boolean +Remoce a [CoreService](#CoreService) identified by the given [CoreDIDUrl](#CoreDIDUrl) from the document. -### chainState.toJSON() ⇒ any -Serializes this to a JSON object. +Returns `true` if the service was removed. -**Kind**: instance method of [ChainState](#ChainState) - +**Kind**: instance method of [CoreDocument](#CoreDocument) -### chainState.clone() ⇒ [ChainState](#ChainState) -Deep clones the object. +| Param | Type | +| --- | --- | +| didUrl | [CoreDIDUrl](#CoreDIDUrl) | -**Kind**: instance method of [ChainState](#ChainState) - + -### ChainState.fromJSON(json) ⇒ [ChainState](#ChainState) -Deserializes an instance from a JSON object. +### coreDocument.resolveService(query) ⇒ [CoreService](#CoreService) \| undefined +Returns the first [CoreService](#CoreService) with an `id` property matching the provided `query`, +if present. -**Kind**: static method of [ChainState](#ChainState) +**Kind**: instance method of [CoreDocument](#CoreDocument) | Param | Type | | --- | --- | -| json | any | - - - -## Client -**Kind**: global class - -* [Client](#Client) - * [new Client()](#new_Client_new) - * _instance_ - * [.network()](#Client+network) ⇒ [Network](#Network) - * [.publishDocument(document)](#Client+publishDocument) ⇒ [Promise.<Receipt>](#Receipt) - * ~~[.publishDiff(message_id, diff)](#Client+publishDiff) ⇒ [Promise.<Receipt>](#Receipt)~~ - * [.publishJSON(index, data)](#Client+publishJSON) ⇒ [Promise.<Receipt>](#Receipt) - * [.publishJsonWithRetry(index, data, interval, max_attempts)](#Client+publishJsonWithRetry) ⇒ Promise.<any> - * [.isMessageIncluded(messageId)](#Client+isMessageIncluded) ⇒ Promise.<boolean> - * [.resolve(did)](#Client+resolve) ⇒ [Promise.<ResolvedDocument>](#ResolvedDocument) - * [.resolveHistory(did)](#Client+resolveHistory) ⇒ [Promise.<DocumentHistory>](#DocumentHistory) - * ~~[.resolveDiffHistory(document)](#Client+resolveDiffHistory) ⇒ [Promise.<DiffChainHistory>](#DiffChainHistory)~~ - * _static_ - * [.fromConfig(config)](#Client.fromConfig) ⇒ [Promise.<Client>](#Client) +| query | [CoreDIDUrl](#CoreDIDUrl) \| string | - + -### new Client() -Creates a new `Client` with default settings. +### coreDocument.methods() ⇒ [Array.<CoreVerificationMethod>](#CoreVerificationMethod) +Returns a list of all [CoreVerificationMethod](#CoreVerificationMethod) in the DID Document. - +**Kind**: instance method of [CoreDocument](#CoreDocument) + -### client.network() ⇒ [Network](#Network) -Returns the `Client` Tangle network. +### coreDocument.verificationRelationships() ⇒ Array.<(CoreDIDUrl\|CoreVerificationMethod)> +Returns an array of all verification relationships. -**Kind**: instance method of [Client](#Client) - +**Kind**: instance method of [CoreDocument](#CoreDocument) + -### client.publishDocument(document) ⇒ [Promise.<Receipt>](#Receipt) -Publishes a [Document](#Document) to the Tangle. +### coreDocument.insertMethod(method, scope) +Adds a new `method` to the document in the given `scope`. -**Kind**: instance method of [Client](#Client) +**Kind**: instance method of [CoreDocument](#CoreDocument) | Param | Type | | --- | --- | -| document | [Document](#Document) | - - +| method | [CoreVerificationMethod](#CoreVerificationMethod) | +| scope | [MethodScope](#MethodScope) | -### ~~client.publishDiff(message_id, diff) ⇒ [Promise.<Receipt>](#Receipt)~~ -***Deprecated*** + -Publishes a `DiffMessage` to the Tangle. +### coreDocument.removeMethod(did) +Removes all references to the specified Verification Method. -**Kind**: instance method of [Client](#Client) +**Kind**: instance method of [CoreDocument](#CoreDocument) | Param | Type | | --- | --- | -| message_id | string | -| diff | [DiffMessage](#DiffMessage) | - - - -### client.publishJSON(index, data) ⇒ [Promise.<Receipt>](#Receipt) -Publishes arbitrary JSON data to the specified index on the Tangle. +| did | [CoreDIDUrl](#CoreDIDUrl) | -**Kind**: instance method of [Client](#Client) + -| Param | Type | +### coreDocument.resolveMethod(query, scope) ⇒ [CoreVerificationMethod](#CoreVerificationMethod) \| undefined +Returns a copy of the first verification method with an `id` property +matching the provided `query` and the verification relationship +specified by `scope`, if present. + +**Kind**: instance method of [CoreDocument](#CoreDocument) + +| Param | Type | | --- | --- | -| index | string | -| data | any | +| query | [CoreDIDUrl](#CoreDIDUrl) \| string | +| scope | [MethodScope](#MethodScope) \| undefined | + + - +### coreDocument.attachMethodRelationship(didUrl, relationship) ⇒ boolean +Attaches the relationship to the given method, if the method exists. -### client.publishJsonWithRetry(index, data, interval, max_attempts) ⇒ Promise.<any> -Publishes arbitrary JSON data to the specified index on the Tangle. -Retries (promotes or reattaches) the message until it’s included (referenced by a milestone). -Default interval is 5 seconds and max attempts is 40. +Note: The method needs to be in the set of verification methods, +so it cannot be an embedded one. -**Kind**: instance method of [Client](#Client) +**Kind**: instance method of [CoreDocument](#CoreDocument) | Param | Type | | --- | --- | -| index | string | -| data | any | -| interval | number \| undefined | -| max_attempts | number \| undefined | +| didUrl | [CoreDIDUrl](#CoreDIDUrl) | +| relationship | number | - + -### client.isMessageIncluded(messageId) ⇒ Promise.<boolean> -Checks if a message is confirmed by a milestone. +### coreDocument.detachMethodRelationship(didUrl, relationship) ⇒ boolean +Detaches the given relationship from the given method, if the method exists. -**Kind**: instance method of [Client](#Client) +**Kind**: instance method of [CoreDocument](#CoreDocument) | Param | Type | | --- | --- | -| messageId | string | +| didUrl | [CoreDIDUrl](#CoreDIDUrl) | +| relationship | number | - + -### client.resolve(did) ⇒ [Promise.<ResolvedDocument>](#ResolvedDocument) -Fetch the DID document specified by the given `DID`. +### coreDocument.verifyData(data, options) ⇒ boolean +Verifies the authenticity of `data` using the target verification method. -**Kind**: instance method of [Client](#Client) +**Kind**: instance method of [CoreDocument](#CoreDocument) | Param | Type | | --- | --- | -| did | [IotaDID](#IotaDID) \| string | +| data | any | +| options | [VerifierOptions](#VerifierOptions) | - + -### client.resolveHistory(did) ⇒ [Promise.<DocumentHistory>](#DocumentHistory) -Returns the message history of the given DID. +### coreDocument.revokeCredentials(serviceQuery, indices) +If the document has a `RevocationBitmap` service identified by `serviceQuery`, +revoke all specified `indices`. -**Kind**: instance method of [Client](#Client) +**Kind**: instance method of [CoreDocument](#CoreDocument) | Param | Type | | --- | --- | -| did | [IotaDID](#IotaDID) \| string | - - - -### ~~client.resolveDiffHistory(document) ⇒ [Promise.<DiffChainHistory>](#DiffChainHistory)~~ -***Deprecated*** +| serviceQuery | [CoreDIDUrl](#CoreDIDUrl) \| string | +| indices | number \| Array.<number> | -Returns the `DiffChainHistory` of a diff chain starting from a document on the -integration chain. + -NOTE: the document must have been published to the tangle and have a valid message id and -capability invocation method. +### coreDocument.unrevokeCredentials(serviceQuery, indices) +If the document has a `RevocationBitmap` service identified by `serviceQuery`, +unrevoke all specified `indices`. -**Kind**: instance method of [Client](#Client) +**Kind**: instance method of [CoreDocument](#CoreDocument) | Param | Type | | --- | --- | -| document | [ResolvedDocument](#ResolvedDocument) | +| serviceQuery | [CoreDIDUrl](#CoreDIDUrl) \| string | +| indices | number \| Array.<number> | + + - +### coreDocument.signData(data, privateKey, methodQuery, options) ⇒ any +Creates a signature for the given `data` with the specified DID Document +Verification Method. -### Client.fromConfig(config) ⇒ [Promise.<Client>](#Client) -Creates a new `Client` with the given settings. +NOTE: use `signSelf` or `signDocument` for DID Documents. -**Kind**: static method of [Client](#Client) +**Kind**: instance method of [CoreDocument](#CoreDocument) | Param | Type | | --- | --- | -| config | IClientConfig | +| data | any | +| privateKey | Uint8Array | +| methodQuery | [CoreDIDUrl](#CoreDIDUrl) \| string | +| options | [ProofOptions](#ProofOptions) | - + -## CoreDID -A method-agnostic Decentralized Identifier (DID). +### coreDocument.toJSON() ⇒ any +Serializes this to a JSON object. -**Kind**: global class +**Kind**: instance method of [CoreDocument](#CoreDocument) + -* [CoreDID](#CoreDID) - * _instance_ - * [.setMethodName(value)](#CoreDID+setMethodName) - * [.setMethodId(value)](#CoreDID+setMethodId) - * [.scheme()](#CoreDID+scheme) ⇒ string - * [.authority()](#CoreDID+authority) ⇒ string - * [.method()](#CoreDID+method) ⇒ string - * [.methodId()](#CoreDID+methodId) ⇒ string - * [.join(segment)](#CoreDID+join) ⇒ [CoreDIDUrl](#CoreDIDUrl) - * [.toUrl()](#CoreDID+toUrl) ⇒ [CoreDIDUrl](#CoreDIDUrl) - * [.intoUrl()](#CoreDID+intoUrl) ⇒ [CoreDIDUrl](#CoreDIDUrl) - * [.toString()](#CoreDID+toString) ⇒ string - * [.toJSON()](#CoreDID+toJSON) ⇒ any - * [.clone()](#CoreDID+clone) ⇒ [CoreDID](#CoreDID) - * _static_ - * [.parse(input)](#CoreDID.parse) ⇒ [CoreDID](#CoreDID) - * [.validMethodName(value)](#CoreDID.validMethodName) ⇒ boolean - * [.validMethodId(value)](#CoreDID.validMethodId) ⇒ boolean - * [.fromJSON(json)](#CoreDID.fromJSON) ⇒ [CoreDID](#CoreDID) +### coreDocument.clone() ⇒ [CoreDocument](#CoreDocument) +Deep clones the object. - +**Kind**: instance method of [CoreDocument](#CoreDocument) + -### coreDID.setMethodName(value) -Set the method name of the `CoreDID`. +### CoreDocument.fromJSON(json) ⇒ [CoreDocument](#CoreDocument) +Deserializes an instance from a JSON object. -**Kind**: instance method of [CoreDID](#CoreDID) +**Kind**: static method of [CoreDocument](#CoreDocument) | Param | Type | | --- | --- | -| value | string | +| json | any | - + -### coreDID.setMethodId(value) -Set the method-specific-id of the `DID`. +## CoreService +A DID Document Service used to enable trusted interactions associated with a DID subject. -**Kind**: instance method of [CoreDID](#CoreDID) +**Kind**: global class + +* [CoreService](#CoreService) + * [new CoreService(service)](#new_CoreService_new) + * _instance_ + * [.id()](#CoreService+id) ⇒ [CoreDIDUrl](#CoreDIDUrl) + * [.type()](#CoreService+type) ⇒ Array.<string> + * [.serviceEndpoint()](#CoreService+serviceEndpoint) ⇒ string \| Array.<string> \| Map.<string, Array.<string>> + * [.properties()](#CoreService+properties) ⇒ Map.<string, any> + * [.toJSON()](#CoreService+toJSON) ⇒ any + * [.clone()](#CoreService+clone) ⇒ [CoreService](#CoreService) + * _static_ + * [.fromJSON(json)](#CoreService.fromJSON) ⇒ [CoreService](#CoreService) + + + +### new CoreService(service) | Param | Type | | --- | --- | -| value | string | +| service | ICoreService | - + -### coreDID.scheme() ⇒ string -Returns the `CoreDID` scheme. +### coreService.id() ⇒ [CoreDIDUrl](#CoreDIDUrl) +Returns a copy of the `CoreService` id. -E.g. -- `"did:example:12345678" -> "did"` -- `"did:iota:smr:12345678" -> "did"` +**Kind**: instance method of [CoreService](#CoreService) + -**Kind**: instance method of [CoreDID](#CoreDID) - +### coreService.type() ⇒ Array.<string> +Returns a copy of the `CoreService` type. -### coreDID.authority() ⇒ string -Returns the `CoreDID` authority: the method name and method-id. +**Kind**: instance method of [CoreService](#CoreService) + -E.g. -- `"did:example:12345678" -> "example:12345678"` -- `"did:iota:smr:12345678" -> "iota:smr:12345678"` +### coreService.serviceEndpoint() ⇒ string \| Array.<string> \| Map.<string, Array.<string>> +Returns a copy of the `CoreService` endpoint. -**Kind**: instance method of [CoreDID](#CoreDID) - +**Kind**: instance method of [CoreService](#CoreService) + -### coreDID.method() ⇒ string -Returns the `CoreDID` method name. +### coreService.properties() ⇒ Map.<string, any> +Returns a copy of the custom properties on the `CoreService`. -E.g. -- `"did:example:12345678" -> "example"` -- `"did:iota:smr:12345678" -> "iota"` +**Kind**: instance method of [CoreService](#CoreService) + -**Kind**: instance method of [CoreDID](#CoreDID) - +### coreService.toJSON() ⇒ any +Serializes this to a JSON object. -### coreDID.methodId() ⇒ string -Returns the `CoreDID` method-specific ID. +**Kind**: instance method of [CoreService](#CoreService) + -E.g. -- `"did:example:12345678" -> "12345678"` -- `"did:iota:smr:12345678" -> "smr:12345678"` +### coreService.clone() ⇒ [CoreService](#CoreService) +Deep clones the object. -**Kind**: instance method of [CoreDID](#CoreDID) - +**Kind**: instance method of [CoreService](#CoreService) + -### coreDID.join(segment) ⇒ [CoreDIDUrl](#CoreDIDUrl) -Construct a new `CoreDIDUrl` by joining with a relative DID Url string. +### CoreService.fromJSON(json) ⇒ [CoreService](#CoreService) +Deserializes an instance from a JSON object. -**Kind**: instance method of [CoreDID](#CoreDID) +**Kind**: static method of [CoreService](#CoreService) | Param | Type | | --- | --- | -| segment | string | - - - -### coreDID.toUrl() ⇒ [CoreDIDUrl](#CoreDIDUrl) -Clones the `CoreDID` into a `CoreDIDUrl`. +| json | any | -**Kind**: instance method of [CoreDID](#CoreDID) - + -### coreDID.intoUrl() ⇒ [CoreDIDUrl](#CoreDIDUrl) -Converts the `CoreDID` into a `CoreDIDUrl`, consuming it. +## CoreVerificationMethod +A DID Document Verification Method. -**Kind**: instance method of [CoreDID](#CoreDID) - +**Kind**: global class -### coreDID.toString() ⇒ string -Returns the `CoreDID` as a string. +* [CoreVerificationMethod](#CoreVerificationMethod) + * [new CoreVerificationMethod(did, keyType, publicKey, fragment)](#new_CoreVerificationMethod_new) + * _instance_ + * [.id()](#CoreVerificationMethod+id) ⇒ [CoreDIDUrl](#CoreDIDUrl) + * [.setId(id)](#CoreVerificationMethod+setId) + * [.controller()](#CoreVerificationMethod+controller) ⇒ [CoreDID](#CoreDID) + * [.setController(did)](#CoreVerificationMethod+setController) + * [.type()](#CoreVerificationMethod+type) ⇒ [MethodType](#MethodType) + * [.setType(type_)](#CoreVerificationMethod+setType) + * [.data()](#CoreVerificationMethod+data) ⇒ [MethodData](#MethodData) + * [.setData(data)](#CoreVerificationMethod+setData) + * [.properties()](#CoreVerificationMethod+properties) ⇒ Map.<string, any> + * [.setPropertyUnchecked(key, value)](#CoreVerificationMethod+setPropertyUnchecked) + * [.toJSON()](#CoreVerificationMethod+toJSON) ⇒ any + * [.clone()](#CoreVerificationMethod+clone) ⇒ [CoreVerificationMethod](#CoreVerificationMethod) + * _static_ + * [.fromJSON(json)](#CoreVerificationMethod.fromJSON) ⇒ [CoreVerificationMethod](#CoreVerificationMethod) -**Kind**: instance method of [CoreDID](#CoreDID) - + -### coreDID.toJSON() ⇒ any -Serializes this to a JSON object. +### new CoreVerificationMethod(did, keyType, publicKey, fragment) +Creates a new `CoreVerificationMethod` from the given `did` and public key. -**Kind**: instance method of [CoreDID](#CoreDID) - -### coreDID.clone() ⇒ [CoreDID](#CoreDID) -Deep clones the object. +| Param | Type | +| --- | --- | +| did | [CoreDID](#CoreDID) | +| keyType | number | +| publicKey | Uint8Array | +| fragment | string | -**Kind**: instance method of [CoreDID](#CoreDID) - + -### CoreDID.parse(input) ⇒ [CoreDID](#CoreDID) -Parses a `CoreDID` from the given `input`. +### coreVerificationMethod.id() ⇒ [CoreDIDUrl](#CoreDIDUrl) +Returns a copy of the `CoreDIDUrl` of the `CoreVerificationMethod`'s `id`. -### Errors +**Kind**: instance method of [CoreVerificationMethod](#CoreVerificationMethod) + -Throws an error if the input is not a valid `CoreDID`. +### coreVerificationMethod.setId(id) +Sets the id of the `CoreVerificationMethod`. -**Kind**: static method of [CoreDID](#CoreDID) +**Kind**: instance method of [CoreVerificationMethod](#CoreVerificationMethod) | Param | Type | | --- | --- | -| input | string | - - - -### CoreDID.validMethodName(value) ⇒ boolean -Validates whether a string is a valid DID method name. +| id | [CoreDIDUrl](#CoreDIDUrl) | -**Kind**: static method of [CoreDID](#CoreDID) + -| Param | Type | -| --- | --- | -| value | string | +### coreVerificationMethod.controller() ⇒ [CoreDID](#CoreDID) +Returns a copy of the `controller` `DID` of the `CoreVerificationMethod`. - +**Kind**: instance method of [CoreVerificationMethod](#CoreVerificationMethod) + -### CoreDID.validMethodId(value) ⇒ boolean -Validates whether a string is a valid `DID` method-id. +### coreVerificationMethod.setController(did) +Sets the `controller` `DID` of the `CoreVerificationMethod` object. -**Kind**: static method of [CoreDID](#CoreDID) +**Kind**: instance method of [CoreVerificationMethod](#CoreVerificationMethod) | Param | Type | | --- | --- | -| value | string | +| did | [CoreDID](#CoreDID) | - + -### CoreDID.fromJSON(json) ⇒ [CoreDID](#CoreDID) -Deserializes an instance from a JSON object. +### coreVerificationMethod.type() ⇒ [MethodType](#MethodType) +Returns a copy of the `CoreVerificationMethod` type. -**Kind**: static method of [CoreDID](#CoreDID) +**Kind**: instance method of [CoreVerificationMethod](#CoreVerificationMethod) + + +### coreVerificationMethod.setType(type_) +Sets the `CoreVerificationMethod` type. + +**Kind**: instance method of [CoreVerificationMethod](#CoreVerificationMethod) | Param | Type | | --- | --- | -| json | any | - - +| type_ | [MethodType](#MethodType) | -## CoreDIDUrl -A method agnostic DID Url. + -**Kind**: global class +### coreVerificationMethod.data() ⇒ [MethodData](#MethodData) +Returns a copy of the `CoreVerificationMethod` public key data. -* [CoreDIDUrl](#CoreDIDUrl) - * _instance_ - * [.did()](#CoreDIDUrl+did) ⇒ [CoreDID](#CoreDID) - * [.urlStr()](#CoreDIDUrl+urlStr) ⇒ string - * [.fragment()](#CoreDIDUrl+fragment) ⇒ string \| undefined - * [.setFragment(value)](#CoreDIDUrl+setFragment) - * [.path()](#CoreDIDUrl+path) ⇒ string \| undefined - * [.setPath(value)](#CoreDIDUrl+setPath) - * [.query()](#CoreDIDUrl+query) ⇒ string \| undefined - * [.setQuery(value)](#CoreDIDUrl+setQuery) - * [.join(segment)](#CoreDIDUrl+join) ⇒ [CoreDIDUrl](#CoreDIDUrl) - * [.toString()](#CoreDIDUrl+toString) ⇒ string - * [.toJSON()](#CoreDIDUrl+toJSON) ⇒ any - * [.clone()](#CoreDIDUrl+clone) ⇒ [CoreDIDUrl](#CoreDIDUrl) - * _static_ - * [.parse(input)](#CoreDIDUrl.parse) ⇒ [CoreDIDUrl](#CoreDIDUrl) - * [.fromJSON(json)](#CoreDIDUrl.fromJSON) ⇒ [CoreDIDUrl](#CoreDIDUrl) +**Kind**: instance method of [CoreVerificationMethod](#CoreVerificationMethod) + - +### coreVerificationMethod.setData(data) +Sets `CoreVerificationMethod` public key data. -### coreDIDUrl.did() ⇒ [CoreDID](#CoreDID) -Return a copy of the `CoreDID` section of the `CoreDIDUrl`. +**Kind**: instance method of [CoreVerificationMethod](#CoreVerificationMethod) -**Kind**: instance method of [CoreDIDUrl](#CoreDIDUrl) - +| Param | Type | +| --- | --- | +| data | [MethodData](#MethodData) | -### coreDIDUrl.urlStr() ⇒ string -Return a copy of the relative DID Url as a string, including only the path, query, and fragment. + -**Kind**: instance method of [CoreDIDUrl](#CoreDIDUrl) - +### coreVerificationMethod.properties() ⇒ Map.<string, any> +Get custom properties of the Verification Method. -### coreDIDUrl.fragment() ⇒ string \| undefined -Returns a copy of the `CoreDIDUrl` method fragment, if any. Excludes the leading '#'. +**Kind**: instance method of [CoreVerificationMethod](#CoreVerificationMethod) + -**Kind**: instance method of [CoreDIDUrl](#CoreDIDUrl) - +### coreVerificationMethod.setPropertyUnchecked(key, value) +Adds a custom property to the Verification Method. +If the value is set to `null`, the custom property will be removed. -### coreDIDUrl.setFragment(value) -Sets the `fragment` component of the `CoreDIDUrl`. +### WARNING +This method can overwrite existing properties like `id` and result +in an invalid Verification Method. -**Kind**: instance method of [CoreDIDUrl](#CoreDIDUrl) +**Kind**: instance method of [CoreVerificationMethod](#CoreVerificationMethod) | Param | Type | | --- | --- | -| value | string \| undefined | +| key | string | +| value | any | - + -### coreDIDUrl.path() ⇒ string \| undefined -Returns a copy of the `CoreDIDUrl` path. +### coreVerificationMethod.toJSON() ⇒ any +Serializes this to a JSON object. -**Kind**: instance method of [CoreDIDUrl](#CoreDIDUrl) - +**Kind**: instance method of [CoreVerificationMethod](#CoreVerificationMethod) + -### coreDIDUrl.setPath(value) -Sets the `path` component of the `CoreDIDUrl`. +### coreVerificationMethod.clone() ⇒ [CoreVerificationMethod](#CoreVerificationMethod) +Deep clones the object. -**Kind**: instance method of [CoreDIDUrl](#CoreDIDUrl) +**Kind**: instance method of [CoreVerificationMethod](#CoreVerificationMethod) + + +### CoreVerificationMethod.fromJSON(json) ⇒ [CoreVerificationMethod](#CoreVerificationMethod) +Deserializes an instance from a JSON object. + +**Kind**: static method of [CoreVerificationMethod](#CoreVerificationMethod) | Param | Type | | --- | --- | -| value | string \| undefined | +| json | any | - + -### coreDIDUrl.query() ⇒ string \| undefined -Returns a copy of the `CoreDIDUrl` method query, if any. Excludes the leading '?'. +## Credential +**Kind**: global class -**Kind**: instance method of [CoreDIDUrl](#CoreDIDUrl) - +* [Credential](#Credential) + * [new Credential(values)](#new_Credential_new) + * _instance_ + * [.context()](#Credential+context) ⇒ Array.<(string\|Record.<string, any>)> + * [.id()](#Credential+id) ⇒ string \| undefined + * [.type()](#Credential+type) ⇒ Array.<string> + * [.credentialSubject()](#Credential+credentialSubject) ⇒ Array.<Subject> + * [.issuer()](#Credential+issuer) ⇒ string \| Issuer + * [.issuanceDate()](#Credential+issuanceDate) ⇒ [Timestamp](#Timestamp) + * [.expirationDate()](#Credential+expirationDate) ⇒ [Timestamp](#Timestamp) \| undefined + * [.credentialStatus()](#Credential+credentialStatus) ⇒ Array.<Status> + * [.credentialSchema()](#Credential+credentialSchema) ⇒ Array.<Schema> + * [.refreshService()](#Credential+refreshService) ⇒ Array.<RefreshService> + * [.termsOfUse()](#Credential+termsOfUse) ⇒ Array.<Policy> + * [.evidence()](#Credential+evidence) ⇒ Array.<Evidence> + * [.nonTransferable()](#Credential+nonTransferable) ⇒ boolean \| undefined + * [.proof()](#Credential+proof) ⇒ [Proof](#Proof) \| undefined + * [.properties()](#Credential+properties) ⇒ Map.<string, any> + * [.toJSON()](#Credential+toJSON) ⇒ any + * [.clone()](#Credential+clone) ⇒ [Credential](#Credential) + * _static_ + * [.BaseContext()](#Credential.BaseContext) ⇒ string + * [.BaseType()](#Credential.BaseType) ⇒ string + * [.fromJSON(json)](#Credential.fromJSON) ⇒ [Credential](#Credential) -### coreDIDUrl.setQuery(value) -Sets the `query` component of the `CoreDIDUrl`. + + +### new Credential(values) +Constructs a new `Credential`. -**Kind**: instance method of [CoreDIDUrl](#CoreDIDUrl) | Param | Type | | --- | --- | -| value | string \| undefined | - - +| values | ICredential | -### coreDIDUrl.join(segment) ⇒ [CoreDIDUrl](#CoreDIDUrl) -Append a string representing a path, query, and/or fragment, returning a new `CoreDIDUrl`. + -Must begin with a valid delimiter character: '/', '?', '#'. Overwrites the existing URL -segment and any following segments in order of path, query, then fragment. +### credential.context() ⇒ Array.<(string\|Record.<string, any>)> +Returns a copy of the JSON-LD context(s) applicable to the `Credential`. -I.e. -- joining a path will clear the query and fragment. -- joining a query will clear the fragment. -- joining a fragment will only overwrite the fragment. +**Kind**: instance method of [Credential](#Credential) + -**Kind**: instance method of [CoreDIDUrl](#CoreDIDUrl) +### credential.id() ⇒ string \| undefined +Returns a copy of the unique `URI` identifying the `Credential` . -| Param | Type | -| --- | --- | -| segment | string | +**Kind**: instance method of [Credential](#Credential) + - +### credential.type() ⇒ Array.<string> +Returns a copy of the URIs defining the type of the `Credential`. -### coreDIDUrl.toString() ⇒ string -Returns the `CoreDIDUrl` as a string. +**Kind**: instance method of [Credential](#Credential) + -**Kind**: instance method of [CoreDIDUrl](#CoreDIDUrl) - +### credential.credentialSubject() ⇒ Array.<Subject> +Returns a copy of the `Credential` subject(s). -### coreDIDUrl.toJSON() ⇒ any -Serializes this to a JSON object. +**Kind**: instance method of [Credential](#Credential) + -**Kind**: instance method of [CoreDIDUrl](#CoreDIDUrl) - +### credential.issuer() ⇒ string \| Issuer +Returns a copy of the issuer of the `Credential`. -### coreDIDUrl.clone() ⇒ [CoreDIDUrl](#CoreDIDUrl) -Deep clones the object. +**Kind**: instance method of [Credential](#Credential) + -**Kind**: instance method of [CoreDIDUrl](#CoreDIDUrl) - +### credential.issuanceDate() ⇒ [Timestamp](#Timestamp) +Returns a copy of the timestamp of when the `Credential` becomes valid. -### CoreDIDUrl.parse(input) ⇒ [CoreDIDUrl](#CoreDIDUrl) -Parses a `CoreDIDUrl` from the input string. +**Kind**: instance method of [Credential](#Credential) + -**Kind**: static method of [CoreDIDUrl](#CoreDIDUrl) +### credential.expirationDate() ⇒ [Timestamp](#Timestamp) \| undefined +Returns a copy of the timestamp of when the `Credential` should no longer be considered valid. -| Param | Type | -| --- | --- | -| input | string | +**Kind**: instance method of [Credential](#Credential) + - +### credential.credentialStatus() ⇒ Array.<Status> +Returns a copy of the information used to determine the current status of the `Credential`. -### CoreDIDUrl.fromJSON(json) ⇒ [CoreDIDUrl](#CoreDIDUrl) -Deserializes an instance from a JSON object. +**Kind**: instance method of [Credential](#Credential) + -**Kind**: static method of [CoreDIDUrl](#CoreDIDUrl) +### credential.credentialSchema() ⇒ Array.<Schema> +Returns a copy of the information used to assist in the enforcement of a specific `Credential` structure. -| Param | Type | -| --- | --- | -| json | any | +**Kind**: instance method of [Credential](#Credential) + - +### credential.refreshService() ⇒ Array.<RefreshService> +Returns a copy of the service(s) used to refresh an expired `Credential`. -## CoreDocument -A method-agnostic DID Document. +**Kind**: instance method of [Credential](#Credential) + -**Kind**: global class +### credential.termsOfUse() ⇒ Array.<Policy> +Returns a copy of the terms-of-use specified by the `Credential` issuer. -* [CoreDocument](#CoreDocument) - * [new CoreDocument(values)](#new_CoreDocument_new) - * _instance_ - * [.id()](#CoreDocument+id) ⇒ [CoreDID](#CoreDID) - * [.setId(id)](#CoreDocument+setId) - * [.controller()](#CoreDocument+controller) ⇒ [Array.<CoreDID>](#CoreDID) - * [.setController(controllers)](#CoreDocument+setController) - * [.alsoKnownAs()](#CoreDocument+alsoKnownAs) ⇒ Array.<string> - * [.setAlsoKnownAs(urls)](#CoreDocument+setAlsoKnownAs) - * [.verificatonMethod()](#CoreDocument+verificatonMethod) ⇒ [Array.<CoreVerificationMethod>](#CoreVerificationMethod) - * [.authentication()](#CoreDocument+authentication) ⇒ Array.<(CoreDIDUrl\|CoreVerificationMethod)> - * [.assertionMethod()](#CoreDocument+assertionMethod) ⇒ Array.<(CoreDIDUrl\|CoreVerificationMethod)> - * [.keyAgreement()](#CoreDocument+keyAgreement) ⇒ Array.<(CoreDIDUrl\|CoreVerificationMethod)> - * [.capabilityDelegation()](#CoreDocument+capabilityDelegation) ⇒ Array.<(CoreDIDUrl\|CoreVerificationMethod)> - * [.capabilityInvocation()](#CoreDocument+capabilityInvocation) ⇒ Array.<(CoreDIDUrl\|CoreVerificationMethod)> - * [.properties()](#CoreDocument+properties) ⇒ Map.<string, any> - * [.setPropertyUnchecked(key, value)](#CoreDocument+setPropertyUnchecked) - * [.service()](#CoreDocument+service) ⇒ [Array.<CoreService>](#CoreService) - * [.insertService(service)](#CoreDocument+insertService) ⇒ boolean - * [.removeService(didUrl)](#CoreDocument+removeService) ⇒ boolean - * [.resolveService(query)](#CoreDocument+resolveService) ⇒ [CoreService](#CoreService) \| undefined - * [.methods()](#CoreDocument+methods) ⇒ [Array.<CoreVerificationMethod>](#CoreVerificationMethod) - * [.verificationRelationships()](#CoreDocument+verificationRelationships) ⇒ Array.<(CoreDIDUrl\|CoreVerificationMethod)> - * [.insertMethod(method, scope)](#CoreDocument+insertMethod) - * [.removeMethod(did)](#CoreDocument+removeMethod) - * [.resolveMethod(query, scope)](#CoreDocument+resolveMethod) ⇒ [CoreVerificationMethod](#CoreVerificationMethod) \| undefined - * [.attachMethodRelationship(didUrl, relationship)](#CoreDocument+attachMethodRelationship) ⇒ boolean - * [.detachMethodRelationship(didUrl, relationship)](#CoreDocument+detachMethodRelationship) ⇒ boolean - * [.verifyData(data, options)](#CoreDocument+verifyData) ⇒ boolean - * [.revokeCredentials(serviceQuery, indices)](#CoreDocument+revokeCredentials) - * [.unrevokeCredentials(serviceQuery, indices)](#CoreDocument+unrevokeCredentials) - * [.signData(data, privateKey, methodQuery, options)](#CoreDocument+signData) ⇒ any - * [.toJSON()](#CoreDocument+toJSON) ⇒ any - * [.clone()](#CoreDocument+clone) ⇒ [CoreDocument](#CoreDocument) - * _static_ - * [.fromJSON(json)](#CoreDocument.fromJSON) ⇒ [CoreDocument](#CoreDocument) - - - -### new CoreDocument(values) -Creates a new `CoreDocument` with the given properties. - - -| Param | Type | -| --- | --- | -| values | ICoreDocument | - - - -### coreDocument.id() ⇒ [CoreDID](#CoreDID) -Returns a copy of the DID Document `id`. - -**Kind**: instance method of [CoreDocument](#CoreDocument) - - -### coreDocument.setId(id) -Sets the DID of the document. - -**Kind**: instance method of [CoreDocument](#CoreDocument) - -| Param | Type | -| --- | --- | -| id | [CoreDID](#CoreDID) | - - - -### coreDocument.controller() ⇒ [Array.<CoreDID>](#CoreDID) -Returns a copy of the document controllers. - -**Kind**: instance method of [CoreDocument](#CoreDocument) - - -### coreDocument.setController(controllers) -Sets the controllers of the DID Document. - -Note: Duplicates will be ignored. -Use `null` to remove all controllers. - -**Kind**: instance method of [CoreDocument](#CoreDocument) - -| Param | Type | -| --- | --- | -| controllers | [CoreDID](#CoreDID) \| [Array.<CoreDID>](#CoreDID) \| null | - - - -### coreDocument.alsoKnownAs() ⇒ Array.<string> -Returns a copy of the document's `alsoKnownAs` set. - -**Kind**: instance method of [CoreDocument](#CoreDocument) - - -### coreDocument.setAlsoKnownAs(urls) -Sets the `alsoKnownAs` property in the DID document. - -**Kind**: instance method of [CoreDocument](#CoreDocument) - -| Param | Type | -| --- | --- | -| urls | string \| Array.<string> \| null | - - - -### coreDocument.verificatonMethod() ⇒ [Array.<CoreVerificationMethod>](#CoreVerificationMethod) -Returns a copy of the document's `verificationMethod` set. - -**Kind**: instance method of [CoreDocument](#CoreDocument) - - -### coreDocument.authentication() ⇒ Array.<(CoreDIDUrl\|CoreVerificationMethod)> -Returns a copy of the document's `authentication` set. - -**Kind**: instance method of [CoreDocument](#CoreDocument) - - -### coreDocument.assertionMethod() ⇒ Array.<(CoreDIDUrl\|CoreVerificationMethod)> -Returns a copy of the document's `assertionMethod` set. - -**Kind**: instance method of [CoreDocument](#CoreDocument) - - -### coreDocument.keyAgreement() ⇒ Array.<(CoreDIDUrl\|CoreVerificationMethod)> -Returns a copy of the document's `keyAgreement` set. - -**Kind**: instance method of [CoreDocument](#CoreDocument) - - -### coreDocument.capabilityDelegation() ⇒ Array.<(CoreDIDUrl\|CoreVerificationMethod)> -Returns a copy of the document's `capabilityDelegation` set. - -**Kind**: instance method of [CoreDocument](#CoreDocument) - - -### coreDocument.capabilityInvocation() ⇒ Array.<(CoreDIDUrl\|CoreVerificationMethod)> -Returns a copy of the document's `capabilityInvocation` set. - -**Kind**: instance method of [CoreDocument](#CoreDocument) - - -### coreDocument.properties() ⇒ Map.<string, any> -Returns a copy of the custom DID Document properties. - -**Kind**: instance method of [CoreDocument](#CoreDocument) - - -### coreDocument.setPropertyUnchecked(key, value) -Sets a custom property in the DID Document. -If the value is set to `null`, the custom property will be removed. - -### WARNING -This method can overwrite existing properties like `id` and result in an invalid document. - -**Kind**: instance method of [CoreDocument](#CoreDocument) - -| Param | Type | -| --- | --- | -| key | string | -| value | any | - - - -### coreDocument.service() ⇒ [Array.<CoreService>](#CoreService) -Returns a set of all [CoreService](#CoreService) in the document. - -**Kind**: instance method of [CoreDocument](#CoreDocument) - - -### coreDocument.insertService(service) ⇒ boolean -Add a new [CoreService](#CoreService) to the document. - -Returns `true` if the service was added. - -**Kind**: instance method of [CoreDocument](#CoreDocument) - -| Param | Type | -| --- | --- | -| service | [CoreService](#CoreService) | - - - -### coreDocument.removeService(didUrl) ⇒ boolean -Remoce a [CoreService](#CoreService) identified by the given [CoreDIDUrl](#CoreDIDUrl) from the document. - -Returns `true` if the service was removed. - -**Kind**: instance method of [CoreDocument](#CoreDocument) - -| Param | Type | -| --- | --- | -| didUrl | [CoreDIDUrl](#CoreDIDUrl) | - - - -### coreDocument.resolveService(query) ⇒ [CoreService](#CoreService) \| undefined -Returns the first [CoreService](#CoreService) with an `id` property matching the provided `query`, -if present. - -**Kind**: instance method of [CoreDocument](#CoreDocument) - -| Param | Type | -| --- | --- | -| query | [CoreDIDUrl](#CoreDIDUrl) \| string | - - - -### coreDocument.methods() ⇒ [Array.<CoreVerificationMethod>](#CoreVerificationMethod) -Returns a list of all [CoreVerificationMethod](#CoreVerificationMethod) in the DID Document. - -**Kind**: instance method of [CoreDocument](#CoreDocument) - - -### coreDocument.verificationRelationships() ⇒ Array.<(CoreDIDUrl\|CoreVerificationMethod)> -Returns an array of all verification relationships. - -**Kind**: instance method of [CoreDocument](#CoreDocument) - - -### coreDocument.insertMethod(method, scope) -Adds a new `method` to the document in the given `scope`. - -**Kind**: instance method of [CoreDocument](#CoreDocument) - -| Param | Type | -| --- | --- | -| method | [CoreVerificationMethod](#CoreVerificationMethod) | -| scope | [MethodScope](#MethodScope) | - - - -### coreDocument.removeMethod(did) -Removes all references to the specified Verification Method. - -**Kind**: instance method of [CoreDocument](#CoreDocument) - -| Param | Type | -| --- | --- | -| did | [CoreDIDUrl](#CoreDIDUrl) | - - - -### coreDocument.resolveMethod(query, scope) ⇒ [CoreVerificationMethod](#CoreVerificationMethod) \| undefined -Returns a copy of the first verification method with an `id` property -matching the provided `query` and the verification relationship -specified by `scope`, if present. - -**Kind**: instance method of [CoreDocument](#CoreDocument) - -| Param | Type | -| --- | --- | -| query | [CoreDIDUrl](#CoreDIDUrl) \| string | -| scope | [MethodScope](#MethodScope) \| undefined | - - - -### coreDocument.attachMethodRelationship(didUrl, relationship) ⇒ boolean -Attaches the relationship to the given method, if the method exists. - -Note: The method needs to be in the set of verification methods, -so it cannot be an embedded one. - -**Kind**: instance method of [CoreDocument](#CoreDocument) - -| Param | Type | -| --- | --- | -| didUrl | [CoreDIDUrl](#CoreDIDUrl) | -| relationship | number | - - - -### coreDocument.detachMethodRelationship(didUrl, relationship) ⇒ boolean -Detaches the given relationship from the given method, if the method exists. - -**Kind**: instance method of [CoreDocument](#CoreDocument) - -| Param | Type | -| --- | --- | -| didUrl | [CoreDIDUrl](#CoreDIDUrl) | -| relationship | number | - - - -### coreDocument.verifyData(data, options) ⇒ boolean -Verifies the authenticity of `data` using the target verification method. - -**Kind**: instance method of [CoreDocument](#CoreDocument) - -| Param | Type | -| --- | --- | -| data | any | -| options | [VerifierOptions](#VerifierOptions) | - - - -### coreDocument.revokeCredentials(serviceQuery, indices) -If the document has a `RevocationBitmap` service identified by `serviceQuery`, -revoke all specified `indices`. - -**Kind**: instance method of [CoreDocument](#CoreDocument) - -| Param | Type | -| --- | --- | -| serviceQuery | [CoreDIDUrl](#CoreDIDUrl) \| string | -| indices | number \| Array.<number> | - - - -### coreDocument.unrevokeCredentials(serviceQuery, indices) -If the document has a `RevocationBitmap` service identified by `serviceQuery`, -unrevoke all specified `indices`. - -**Kind**: instance method of [CoreDocument](#CoreDocument) - -| Param | Type | -| --- | --- | -| serviceQuery | [CoreDIDUrl](#CoreDIDUrl) \| string | -| indices | number \| Array.<number> | - - - -### coreDocument.signData(data, privateKey, methodQuery, options) ⇒ any -Creates a signature for the given `data` with the specified DID Document -Verification Method. - -NOTE: use `signSelf` or `signDocument` for DID Documents. - -**Kind**: instance method of [CoreDocument](#CoreDocument) - -| Param | Type | -| --- | --- | -| data | any | -| privateKey | Uint8Array | -| methodQuery | [CoreDIDUrl](#CoreDIDUrl) \| string | -| options | [ProofOptions](#ProofOptions) | - - - -### coreDocument.toJSON() ⇒ any -Serializes this to a JSON object. - -**Kind**: instance method of [CoreDocument](#CoreDocument) - - -### coreDocument.clone() ⇒ [CoreDocument](#CoreDocument) -Deep clones the object. - -**Kind**: instance method of [CoreDocument](#CoreDocument) - - -### CoreDocument.fromJSON(json) ⇒ [CoreDocument](#CoreDocument) -Deserializes an instance from a JSON object. - -**Kind**: static method of [CoreDocument](#CoreDocument) - -| Param | Type | -| --- | --- | -| json | any | - - - -## CoreService -A DID Document Service used to enable trusted interactions associated with a DID subject. - -**Kind**: global class - -* [CoreService](#CoreService) - * [new CoreService(service)](#new_CoreService_new) - * _instance_ - * [.id()](#CoreService+id) ⇒ [CoreDIDUrl](#CoreDIDUrl) - * [.type()](#CoreService+type) ⇒ Array.<string> - * [.serviceEndpoint()](#CoreService+serviceEndpoint) ⇒ string \| Array.<string> \| Map.<string, Array.<string>> - * [.properties()](#CoreService+properties) ⇒ Map.<string, any> - * [.toJSON()](#CoreService+toJSON) ⇒ any - * [.clone()](#CoreService+clone) ⇒ [CoreService](#CoreService) - * _static_ - * [.fromJSON(json)](#CoreService.fromJSON) ⇒ [CoreService](#CoreService) - - - -### new CoreService(service) - -| Param | Type | -| --- | --- | -| service | ICoreService | - - - -### coreService.id() ⇒ [CoreDIDUrl](#CoreDIDUrl) -Returns a copy of the `CoreService` id. - -**Kind**: instance method of [CoreService](#CoreService) - - -### coreService.type() ⇒ Array.<string> -Returns a copy of the `CoreService` type. - -**Kind**: instance method of [CoreService](#CoreService) - - -### coreService.serviceEndpoint() ⇒ string \| Array.<string> \| Map.<string, Array.<string>> -Returns a copy of the `CoreService` endpoint. - -**Kind**: instance method of [CoreService](#CoreService) - - -### coreService.properties() ⇒ Map.<string, any> -Returns a copy of the custom properties on the `CoreService`. - -**Kind**: instance method of [CoreService](#CoreService) - - -### coreService.toJSON() ⇒ any -Serializes this to a JSON object. - -**Kind**: instance method of [CoreService](#CoreService) - - -### coreService.clone() ⇒ [CoreService](#CoreService) -Deep clones the object. - -**Kind**: instance method of [CoreService](#CoreService) - - -### CoreService.fromJSON(json) ⇒ [CoreService](#CoreService) -Deserializes an instance from a JSON object. - -**Kind**: static method of [CoreService](#CoreService) - -| Param | Type | -| --- | --- | -| json | any | - - - -## CoreVerificationMethod -A DID Document Verification Method. - -**Kind**: global class - -* [CoreVerificationMethod](#CoreVerificationMethod) - * [new CoreVerificationMethod(did, keyType, publicKey, fragment)](#new_CoreVerificationMethod_new) - * _instance_ - * [.id()](#CoreVerificationMethod+id) ⇒ [CoreDIDUrl](#CoreDIDUrl) - * [.setId(id)](#CoreVerificationMethod+setId) - * [.controller()](#CoreVerificationMethod+controller) ⇒ [CoreDID](#CoreDID) - * [.setController(did)](#CoreVerificationMethod+setController) - * [.type()](#CoreVerificationMethod+type) ⇒ [MethodType](#MethodType) - * [.setType(type_)](#CoreVerificationMethod+setType) - * [.data()](#CoreVerificationMethod+data) ⇒ [MethodData](#MethodData) - * [.setData(data)](#CoreVerificationMethod+setData) - * [.properties()](#CoreVerificationMethod+properties) ⇒ Map.<string, any> - * [.setPropertyUnchecked(key, value)](#CoreVerificationMethod+setPropertyUnchecked) - * [.toJSON()](#CoreVerificationMethod+toJSON) ⇒ any - * [.clone()](#CoreVerificationMethod+clone) ⇒ [CoreVerificationMethod](#CoreVerificationMethod) - * _static_ - * [.fromJSON(json)](#CoreVerificationMethod.fromJSON) ⇒ [CoreVerificationMethod](#CoreVerificationMethod) - - - -### new CoreVerificationMethod(did, keyType, publicKey, fragment) -Creates a new `CoreVerificationMethod` from the given `did` and public key. - - -| Param | Type | -| --- | --- | -| did | [CoreDID](#CoreDID) | -| keyType | number | -| publicKey | Uint8Array | -| fragment | string | - - - -### coreVerificationMethod.id() ⇒ [CoreDIDUrl](#CoreDIDUrl) -Returns a copy of the `CoreDIDUrl` of the `CoreVerificationMethod`'s `id`. - -**Kind**: instance method of [CoreVerificationMethod](#CoreVerificationMethod) - - -### coreVerificationMethod.setId(id) -Sets the id of the `CoreVerificationMethod`. - -**Kind**: instance method of [CoreVerificationMethod](#CoreVerificationMethod) - -| Param | Type | -| --- | --- | -| id | [CoreDIDUrl](#CoreDIDUrl) | - - - -### coreVerificationMethod.controller() ⇒ [CoreDID](#CoreDID) -Returns a copy of the `controller` `DID` of the `CoreVerificationMethod`. - -**Kind**: instance method of [CoreVerificationMethod](#CoreVerificationMethod) - - -### coreVerificationMethod.setController(did) -Sets the `controller` `DID` of the `CoreVerificationMethod` object. - -**Kind**: instance method of [CoreVerificationMethod](#CoreVerificationMethod) - -| Param | Type | -| --- | --- | -| did | [CoreDID](#CoreDID) | - - - -### coreVerificationMethod.type() ⇒ [MethodType](#MethodType) -Returns a copy of the `CoreVerificationMethod` type. - -**Kind**: instance method of [CoreVerificationMethod](#CoreVerificationMethod) - - -### coreVerificationMethod.setType(type_) -Sets the `CoreVerificationMethod` type. - -**Kind**: instance method of [CoreVerificationMethod](#CoreVerificationMethod) - -| Param | Type | -| --- | --- | -| type_ | [MethodType](#MethodType) | - - - -### coreVerificationMethod.data() ⇒ [MethodData](#MethodData) -Returns a copy of the `CoreVerificationMethod` public key data. - -**Kind**: instance method of [CoreVerificationMethod](#CoreVerificationMethod) - - -### coreVerificationMethod.setData(data) -Sets `CoreVerificationMethod` public key data. - -**Kind**: instance method of [CoreVerificationMethod](#CoreVerificationMethod) - -| Param | Type | -| --- | --- | -| data | [MethodData](#MethodData) | - - - -### coreVerificationMethod.properties() ⇒ Map.<string, any> -Get custom properties of the Verification Method. - -**Kind**: instance method of [CoreVerificationMethod](#CoreVerificationMethod) - - -### coreVerificationMethod.setPropertyUnchecked(key, value) -Adds a custom property to the Verification Method. -If the value is set to `null`, the custom property will be removed. - -### WARNING -This method can overwrite existing properties like `id` and result -in an invalid Verification Method. - -**Kind**: instance method of [CoreVerificationMethod](#CoreVerificationMethod) - -| Param | Type | -| --- | --- | -| key | string | -| value | any | - - - -### coreVerificationMethod.toJSON() ⇒ any -Serializes this to a JSON object. - -**Kind**: instance method of [CoreVerificationMethod](#CoreVerificationMethod) - - -### coreVerificationMethod.clone() ⇒ [CoreVerificationMethod](#CoreVerificationMethod) -Deep clones the object. - -**Kind**: instance method of [CoreVerificationMethod](#CoreVerificationMethod) - - -### CoreVerificationMethod.fromJSON(json) ⇒ [CoreVerificationMethod](#CoreVerificationMethod) -Deserializes an instance from a JSON object. - -**Kind**: static method of [CoreVerificationMethod](#CoreVerificationMethod) - -| Param | Type | -| --- | --- | -| json | any | - - - -## Credential -**Kind**: global class - -* [Credential](#Credential) - * [new Credential(values)](#new_Credential_new) - * _instance_ - * [.context()](#Credential+context) ⇒ Array.<(string\|Record.<string, any>)> - * [.id()](#Credential+id) ⇒ string \| undefined - * [.type()](#Credential+type) ⇒ Array.<string> - * [.credentialSubject()](#Credential+credentialSubject) ⇒ Array.<Subject> - * [.issuer()](#Credential+issuer) ⇒ string \| Issuer - * [.issuanceDate()](#Credential+issuanceDate) ⇒ [Timestamp](#Timestamp) - * [.expirationDate()](#Credential+expirationDate) ⇒ [Timestamp](#Timestamp) \| undefined - * [.credentialStatus()](#Credential+credentialStatus) ⇒ Array.<Status> - * [.credentialSchema()](#Credential+credentialSchema) ⇒ Array.<Schema> - * [.refreshService()](#Credential+refreshService) ⇒ Array.<RefreshService> - * [.termsOfUse()](#Credential+termsOfUse) ⇒ Array.<Policy> - * [.evidence()](#Credential+evidence) ⇒ Array.<Evidence> - * [.nonTransferable()](#Credential+nonTransferable) ⇒ boolean \| undefined - * [.proof()](#Credential+proof) ⇒ [Proof](#Proof) \| undefined - * [.properties()](#Credential+properties) ⇒ Map.<string, any> - * [.toJSON()](#Credential+toJSON) ⇒ any - * [.clone()](#Credential+clone) ⇒ [Credential](#Credential) - * _static_ - * [.BaseContext()](#Credential.BaseContext) ⇒ string - * [.BaseType()](#Credential.BaseType) ⇒ string - * [.fromJSON(json)](#Credential.fromJSON) ⇒ [Credential](#Credential) - - - -### new Credential(values) -Constructs a new `Credential`. - - -| Param | Type | -| --- | --- | -| values | ICredential | - - - -### credential.context() ⇒ Array.<(string\|Record.<string, any>)> -Returns a copy of the JSON-LD context(s) applicable to the `Credential`. - -**Kind**: instance method of [Credential](#Credential) - - -### credential.id() ⇒ string \| undefined -Returns a copy of the unique `URI` identifying the `Credential` . - -**Kind**: instance method of [Credential](#Credential) - - -### credential.type() ⇒ Array.<string> -Returns a copy of the URIs defining the type of the `Credential`. - -**Kind**: instance method of [Credential](#Credential) - - -### credential.credentialSubject() ⇒ Array.<Subject> -Returns a copy of the `Credential` subject(s). - -**Kind**: instance method of [Credential](#Credential) - - -### credential.issuer() ⇒ string \| Issuer -Returns a copy of the issuer of the `Credential`. - -**Kind**: instance method of [Credential](#Credential) - - -### credential.issuanceDate() ⇒ [Timestamp](#Timestamp) -Returns a copy of the timestamp of when the `Credential` becomes valid. - -**Kind**: instance method of [Credential](#Credential) - - -### credential.expirationDate() ⇒ [Timestamp](#Timestamp) \| undefined -Returns a copy of the timestamp of when the `Credential` should no longer be considered valid. - -**Kind**: instance method of [Credential](#Credential) - - -### credential.credentialStatus() ⇒ Array.<Status> -Returns a copy of the information used to determine the current status of the `Credential`. - -**Kind**: instance method of [Credential](#Credential) - - -### credential.credentialSchema() ⇒ Array.<Schema> -Returns a copy of the information used to assist in the enforcement of a specific `Credential` structure. - -**Kind**: instance method of [Credential](#Credential) - - -### credential.refreshService() ⇒ Array.<RefreshService> -Returns a copy of the service(s) used to refresh an expired `Credential`. - -**Kind**: instance method of [Credential](#Credential) - - -### credential.termsOfUse() ⇒ Array.<Policy> -Returns a copy of the terms-of-use specified by the `Credential` issuer. - -**Kind**: instance method of [Credential](#Credential) - - -### credential.evidence() ⇒ Array.<Evidence> -Returns a copy of the human-readable evidence used to support the claims within the `Credential`. - -**Kind**: instance method of [Credential](#Credential) - - -### credential.nonTransferable() ⇒ boolean \| undefined -Returns whether or not the `Credential` must only be contained within a [Presentation](#Presentation) -with a proof issued from the `Credential` subject. - -**Kind**: instance method of [Credential](#Credential) - - -### credential.proof() ⇒ [Proof](#Proof) \| undefined -Returns a copy of the proof used to verify the `Credential`. - -**Kind**: instance method of [Credential](#Credential) - - -### credential.properties() ⇒ Map.<string, any> -Returns a copy of the miscellaneous properties on the `Credential`. - -**Kind**: instance method of [Credential](#Credential) - - -### credential.toJSON() ⇒ any -Serializes this to a JSON object. - -**Kind**: instance method of [Credential](#Credential) - - -### credential.clone() ⇒ [Credential](#Credential) -Deep clones the object. - -**Kind**: instance method of [Credential](#Credential) - - -### Credential.BaseContext() ⇒ string -Returns the base JSON-LD context. - -**Kind**: static method of [Credential](#Credential) - - -### Credential.BaseType() ⇒ string -Returns the base type. - -**Kind**: static method of [Credential](#Credential) - - -### Credential.fromJSON(json) ⇒ [Credential](#Credential) -Deserializes an instance from a JSON object. - -**Kind**: static method of [Credential](#Credential) - -| Param | Type | -| --- | --- | -| json | any | - - - -## CredentialValidationOptions -Options to declare validation criteria when validating credentials. - -**Kind**: global class - -* [CredentialValidationOptions](#CredentialValidationOptions) - * [new CredentialValidationOptions(options)](#new_CredentialValidationOptions_new) - * _instance_ - * [.toJSON()](#CredentialValidationOptions+toJSON) ⇒ any - * [.clone()](#CredentialValidationOptions+clone) ⇒ [CredentialValidationOptions](#CredentialValidationOptions) - * _static_ - * [.default()](#CredentialValidationOptions.default) ⇒ [CredentialValidationOptions](#CredentialValidationOptions) - * [.fromJSON(json)](#CredentialValidationOptions.fromJSON) ⇒ [CredentialValidationOptions](#CredentialValidationOptions) - - - -### new CredentialValidationOptions(options) -Creates a new `CredentialValidationOptions` from the given fields. - -Throws an error if any of the options are invalid. - - -| Param | Type | -| --- | --- | -| options | ICredentialValidationOptions | - - - -### credentialValidationOptions.toJSON() ⇒ any -Serializes this to a JSON object. - -**Kind**: instance method of [CredentialValidationOptions](#CredentialValidationOptions) - - -### credentialValidationOptions.clone() ⇒ [CredentialValidationOptions](#CredentialValidationOptions) -Deep clones the object. - -**Kind**: instance method of [CredentialValidationOptions](#CredentialValidationOptions) - - -### CredentialValidationOptions.default() ⇒ [CredentialValidationOptions](#CredentialValidationOptions) -Creates a new `CredentialValidationOptions` with defaults. - -**Kind**: static method of [CredentialValidationOptions](#CredentialValidationOptions) - - -### CredentialValidationOptions.fromJSON(json) ⇒ [CredentialValidationOptions](#CredentialValidationOptions) -Deserializes an instance from a JSON object. - -**Kind**: static method of [CredentialValidationOptions](#CredentialValidationOptions) - -| Param | Type | -| --- | --- | -| json | any | - - - -## CredentialValidator -**Kind**: global class - -* [CredentialValidator](#CredentialValidator) - * [.validate(credential, issuer, options, fail_fast)](#CredentialValidator.validate) - * [.checkStructure(credential)](#CredentialValidator.checkStructure) - * [.checkExpiresOnOrAfter(credential, timestamp)](#CredentialValidator.checkExpiresOnOrAfter) - * [.checkIssuedOnOrBefore(credential, timestamp)](#CredentialValidator.checkIssuedOnOrBefore) - * [.verifySignature(credential, trustedIssuers, options)](#CredentialValidator.verifySignature) - * [.checkSubjectHolderRelationship(credential, holder, relationship)](#CredentialValidator.checkSubjectHolderRelationship) - * [.checkStatus(credential, trustedIssuers, statusCheck)](#CredentialValidator.checkStatus) - * [.extractIssuer(credential)](#CredentialValidator.extractIssuer) ⇒ [CoreDID](#CoreDID) \| [StardustDID](#StardustDID) - - - -### CredentialValidator.validate(credential, issuer, options, fail_fast) -Validates a `Credential`. - -The following properties are validated according to `options`: -- the issuer's signature, -- the expiration date, -- the issuance date, -- the semantic structure. - -### Warning -The lack of an error returned from this method is in of itself not enough to conclude that the credential can be -trusted. This section contains more information on additional checks that should be carried out before and after -calling this method. - -#### The state of the issuer's DID Document -The caller must ensure that `issuer` represents an up-to-date DID Document. The convenience method -`Resolver::resolveCredentialIssuer` can help extract the latest available state of the issuer's DID Document. - -#### Properties that are not validated - There are many properties defined in [The Verifiable Credentials Data Model](https://www.w3.org/TR/vc-data-model/) that are **not** validated, such as: -`credentialStatus`, `type`, `credentialSchema`, `refreshService`, **and more**. -These should be manually checked after validation, according to your requirements. - -### Errors -An error is returned whenever a validated condition is not satisfied. - -**Kind**: static method of [CredentialValidator](#CredentialValidator) - -| Param | Type | -| --- | --- | -| credential | [Credential](#Credential) | -| issuer | [StardustDocument](#StardustDocument) \| [CoreDocument](#CoreDocument) | -| options | [CredentialValidationOptions](#CredentialValidationOptions) | -| fail_fast | number | - - - -### CredentialValidator.checkStructure(credential) -Validates the semantic structure of the `Credential`. - -### Warning -This does not validate against the credential's schema nor the structure of the subject claims. - -**Kind**: static method of [CredentialValidator](#CredentialValidator) - -| Param | Type | -| --- | --- | -| credential | [Credential](#Credential) | - - - -### CredentialValidator.checkExpiresOnOrAfter(credential, timestamp) -Validate that the credential expires on or after the specified timestamp. - -**Kind**: static method of [CredentialValidator](#CredentialValidator) - -| Param | Type | -| --- | --- | -| credential | [Credential](#Credential) | -| timestamp | [Timestamp](#Timestamp) | - - - -### CredentialValidator.checkIssuedOnOrBefore(credential, timestamp) -Validate that the credential is issued on or before the specified timestamp. - -**Kind**: static method of [CredentialValidator](#CredentialValidator) - -| Param | Type | -| --- | --- | -| credential | [Credential](#Credential) | -| timestamp | [Timestamp](#Timestamp) | - - - -### CredentialValidator.verifySignature(credential, trustedIssuers, options) -Verify the signature using the DID Document of a trusted issuer. - -# Warning -The caller must ensure that the DID Documents of the trusted issuers are up-to-date. -### Errors -This method immediately returns an error if -the credential issuer' url cannot be parsed to a DID belonging to one of the trusted issuers. Otherwise an attempt -to verify the credential's signature will be made and an error is returned upon failure. - -**Kind**: static method of [CredentialValidator](#CredentialValidator) - -| Param | Type | -| --- | --- | -| credential | [Credential](#Credential) | -| trustedIssuers | Array.<(StardustDocument\|CoreDocument)> | -| options | [VerifierOptions](#VerifierOptions) | - - - -### CredentialValidator.checkSubjectHolderRelationship(credential, holder, relationship) -Validate that the relationship between the `holder` and the credential subjects is in accordance with -`relationship`. The `holder` parameter is expected to be the URL of the holder. - -**Kind**: static method of [CredentialValidator](#CredentialValidator) - -| Param | Type | -| --- | --- | -| credential | [Credential](#Credential) | -| holder | string | -| relationship | number | - - - -### CredentialValidator.checkStatus(credential, trustedIssuers, statusCheck) -Checks whether the credential status has been revoked. - -Only supports `BitmapRevocation2022`. - -**Kind**: static method of [CredentialValidator](#CredentialValidator) - -| Param | Type | -| --- | --- | -| credential | [Credential](#Credential) | -| trustedIssuers | Array.<(StardustDocument\|CoreDocument)> | -| statusCheck | number | - - - -### CredentialValidator.extractIssuer(credential) ⇒ [CoreDID](#CoreDID) \| [StardustDID](#StardustDID) -Utility for extracting the issuer field of a `Credential` as a DID. - -### Errors - -Fails if the issuer field is not a valid DID. - -**Kind**: static method of [CredentialValidator](#CredentialValidator) - -| Param | Type | -| --- | --- | -| credential | [Credential](#Credential) | - - - -## DIDUrl -A DID URL conforming to the IOTA DID method specification. - -**Kind**: global class - -* [DIDUrl](#DIDUrl) - * _instance_ - * [.did()](#DIDUrl+did) ⇒ [IotaDID](#IotaDID) - * [.urlStr()](#DIDUrl+urlStr) ⇒ string - * [.fragment()](#DIDUrl+fragment) ⇒ string \| undefined - * [.setFragment(value)](#DIDUrl+setFragment) - * [.path()](#DIDUrl+path) ⇒ string \| undefined - * [.setPath(value)](#DIDUrl+setPath) - * [.query()](#DIDUrl+query) ⇒ string \| undefined - * [.setQuery(value)](#DIDUrl+setQuery) - * [.join(segment)](#DIDUrl+join) ⇒ [DIDUrl](#DIDUrl) - * [.toString()](#DIDUrl+toString) ⇒ string - * [.toJSON()](#DIDUrl+toJSON) ⇒ any - * [.clone()](#DIDUrl+clone) ⇒ [DIDUrl](#DIDUrl) - * _static_ - * [.parse(input)](#DIDUrl.parse) ⇒ [DIDUrl](#DIDUrl) - * [.fromJSON(json)](#DIDUrl.fromJSON) ⇒ [DIDUrl](#DIDUrl) - - - -### didUrl.did() ⇒ [IotaDID](#IotaDID) -Return a copy of the `DID` section of the `DIDUrl`. - -**Kind**: instance method of [DIDUrl](#DIDUrl) - - -### didUrl.urlStr() ⇒ string -Return a copy of the relative DID Url as a string, including only the path, query, and fragment. - -**Kind**: instance method of [DIDUrl](#DIDUrl) - - -### didUrl.fragment() ⇒ string \| undefined -Returns a copy of the `DIDUrl` method fragment, if any. Excludes the leading '#'. - -**Kind**: instance method of [DIDUrl](#DIDUrl) - - -### didUrl.setFragment(value) -Sets the `fragment` component of the `DIDUrl`. - -**Kind**: instance method of [DIDUrl](#DIDUrl) - -| Param | Type | -| --- | --- | -| value | string \| undefined | - - - -### didUrl.path() ⇒ string \| undefined -Returns a copy of the `DIDUrl` path. - -**Kind**: instance method of [DIDUrl](#DIDUrl) - - -### didUrl.setPath(value) -Sets the `path` component of the `DIDUrl`. - -**Kind**: instance method of [DIDUrl](#DIDUrl) - -| Param | Type | -| --- | --- | -| value | string \| undefined | - - - -### didUrl.query() ⇒ string \| undefined -Returns a copy of the `DIDUrl` method query, if any. Excludes the leading '?'. - -**Kind**: instance method of [DIDUrl](#DIDUrl) - - -### didUrl.setQuery(value) -Sets the `query` component of the `DIDUrl`. - -**Kind**: instance method of [DIDUrl](#DIDUrl) - -| Param | Type | -| --- | --- | -| value | string \| undefined | - - - -### didUrl.join(segment) ⇒ [DIDUrl](#DIDUrl) -Append a string representing a path, query, and/or fragment, returning a new `DIDUrl`. - -Must begin with a valid delimiter character: '/', '?', '#'. Overwrites the existing URL -segment and any following segments in order of path, query, then fragment. - -I.e. -- joining a path will clear the query and fragment. -- joining a query will clear the fragment. -- joining a fragment will only overwrite the fragment. - -**Kind**: instance method of [DIDUrl](#DIDUrl) - -| Param | Type | -| --- | --- | -| segment | string | - - - -### didUrl.toString() ⇒ string -Returns the `DIDUrl` as a string. - -**Kind**: instance method of [DIDUrl](#DIDUrl) - - -### didUrl.toJSON() ⇒ any -Serializes this to a JSON object. - -**Kind**: instance method of [DIDUrl](#DIDUrl) - - -### didUrl.clone() ⇒ [DIDUrl](#DIDUrl) -Deep clones the object. - -**Kind**: instance method of [DIDUrl](#DIDUrl) - - -### DIDUrl.parse(input) ⇒ [DIDUrl](#DIDUrl) -Parses a `DIDUrl` from the input string. - -**Kind**: static method of [DIDUrl](#DIDUrl) - -| Param | Type | -| --- | --- | -| input | string | - - - -### DIDUrl.fromJSON(json) ⇒ [DIDUrl](#DIDUrl) -Deserializes an instance from a JSON object. - -**Kind**: static method of [DIDUrl](#DIDUrl) - -| Param | Type | -| --- | --- | -| json | any | - - - -## ~~DiffChainHistory~~ -***Deprecated*** - -**Kind**: global class - -* ~~[DiffChainHistory](#DiffChainHistory)~~ - * _instance_ - * [.chainData()](#DiffChainHistory+chainData) ⇒ [Array.<DiffMessage>](#DiffMessage) - * [.spam()](#DiffChainHistory+spam) ⇒ Array.<string> - * [.toJSON()](#DiffChainHistory+toJSON) ⇒ any - * _static_ - * [.fromJSON(json)](#DiffChainHistory.fromJSON) ⇒ [DiffChainHistory](#DiffChainHistory) - - - -### diffChainHistory.chainData() ⇒ [Array.<DiffMessage>](#DiffMessage) -Returns an `Array` of the diff chain `DiffMessages`. - -NOTE: this clones the field. - -**Kind**: instance method of [DiffChainHistory](#DiffChainHistory) - - -### diffChainHistory.spam() ⇒ Array.<string> -Returns an `Array` of `MessageIds` as strings. - -NOTE: this clones the field. - -**Kind**: instance method of [DiffChainHistory](#DiffChainHistory) - - -### diffChainHistory.toJSON() ⇒ any -Serializes as a JSON object. - -**Kind**: instance method of [DiffChainHistory](#DiffChainHistory) - - -### DiffChainHistory.fromJSON(json) ⇒ [DiffChainHistory](#DiffChainHistory) -Deserializes from a JSON object. - -**Kind**: static method of [DiffChainHistory](#DiffChainHistory) - -| Param | Type | -| --- | --- | -| json | any | - - - -## ~~DiffMessage~~ -***Deprecated*** - -Defines the difference between two DID `Document`s' JSON representations. - -**Kind**: global class - -* ~~[DiffMessage](#DiffMessage)~~ - * _instance_ - * ~~[.id()](#DiffMessage+id) ⇒ [IotaDID](#IotaDID)~~ - * ~~[.did()](#DiffMessage+did) ⇒ [IotaDID](#IotaDID)~~ - * ~~[.diff()](#DiffMessage+diff) ⇒ string~~ - * ~~[.messageId()](#DiffMessage+messageId) ⇒ string~~ - * ~~[.setMessageId(message_id)](#DiffMessage+setMessageId)~~ - * ~~[.previousMessageId()](#DiffMessage+previousMessageId) ⇒ string~~ - * ~~[.setPreviousMessageId(message_id)](#DiffMessage+setPreviousMessageId)~~ - * ~~[.proof()](#DiffMessage+proof) ⇒ [Proof](#Proof) \| undefined~~ - * ~~[.merge(document)](#DiffMessage+merge) ⇒ [Document](#Document)~~ - * [.toJSON()](#DiffMessage+toJSON) ⇒ any - * [.clone()](#DiffMessage+clone) ⇒ [DiffMessage](#DiffMessage) - * _static_ - * [.fromJSON(json)](#DiffMessage.fromJSON) ⇒ [DiffMessage](#DiffMessage) - - - -### ~~diffMessage.id() ⇒ [IotaDID](#IotaDID)~~ -***Deprecated*** - -Returns the DID of the associated DID Document. - -NOTE: clones the data. - -**Kind**: instance method of [DiffMessage](#DiffMessage) - - -### ~~diffMessage.did() ⇒ [IotaDID](#IotaDID)~~ -***Deprecated*** - -Returns a copy of the DID of the associated DID Document. - -**Kind**: instance method of [DiffMessage](#DiffMessage) - - -### ~~diffMessage.diff() ⇒ string~~ -***Deprecated*** - -Returns a copy of the raw contents of the DID Document diff as a JSON string. - -**Kind**: instance method of [DiffMessage](#DiffMessage) - - -### ~~diffMessage.messageId() ⇒ string~~ -***Deprecated*** - -Returns a copy of the message_id of the DID Document diff. - -**Kind**: instance method of [DiffMessage](#DiffMessage) - - -### ~~diffMessage.setMessageId(message_id)~~ -***Deprecated*** - -Sets the message_id of the DID Document diff. - -**Kind**: instance method of [DiffMessage](#DiffMessage) - -| Param | Type | -| --- | --- | -| message_id | string | - - - -### ~~diffMessage.previousMessageId() ⇒ string~~ -***Deprecated*** - -Returns a copy of the Tangle message id of the previous DID Document diff. - -**Kind**: instance method of [DiffMessage](#DiffMessage) - - -### ~~diffMessage.setPreviousMessageId(message_id)~~ -***Deprecated*** - -Sets the Tangle message id of the previous DID Document diff. - -**Kind**: instance method of [DiffMessage](#DiffMessage) - -| Param | Type | -| --- | --- | -| message_id | string | - - - -### ~~diffMessage.proof() ⇒ [Proof](#Proof) \| undefined~~ -***Deprecated*** - -Returns a copy of the proof. - -**Kind**: instance method of [DiffMessage](#DiffMessage) - - -### ~~diffMessage.merge(document) ⇒ [Document](#Document)~~ -***Deprecated*** - -Returns a new DID Document which is the result of merging `self` -with the given Document. - -**Kind**: instance method of [DiffMessage](#DiffMessage) - -| Param | Type | -| --- | --- | -| document | [Document](#Document) | - - - -### diffMessage.toJSON() ⇒ any -Serializes this to a JSON object. - -**Kind**: instance method of [DiffMessage](#DiffMessage) - - -### diffMessage.clone() ⇒ [DiffMessage](#DiffMessage) -Deep clones the object. - -**Kind**: instance method of [DiffMessage](#DiffMessage) - - -### DiffMessage.fromJSON(json) ⇒ [DiffMessage](#DiffMessage) -Deserializes an instance from a JSON object. - -**Kind**: static method of [DiffMessage](#DiffMessage) - -| Param | Type | -| --- | --- | -| json | any | - - - -## Document -**Kind**: global class - -* [Document](#Document) - * [new Document(keypair, network, fragment)](#new_Document_new) - * _instance_ - * [.id()](#Document+id) ⇒ [IotaDID](#IotaDID) - * [.setController(controllers)](#Document+setController) - * [.controller()](#Document+controller) ⇒ [Array.<IotaDID>](#IotaDID) - * [.setAlsoKnownAs(urls)](#Document+setAlsoKnownAs) - * [.alsoKnownAs()](#Document+alsoKnownAs) ⇒ Array.<string> - * [.setPropertyUnchecked(key, value)](#Document+setPropertyUnchecked) - * [.properties()](#Document+properties) ⇒ Map.<string, any> - * [.service()](#Document+service) ⇒ [Array.<Service>](#Service) - * [.insertService(service)](#Document+insertService) ⇒ boolean - * [.removeService(did)](#Document+removeService) ⇒ boolean - * [.resolveService(query)](#Document+resolveService) ⇒ [Service](#Service) \| undefined - * [.methods()](#Document+methods) ⇒ [Array.<VerificationMethod>](#VerificationMethod) - * [.insertMethod(method, scope)](#Document+insertMethod) - * [.removeMethod(did)](#Document+removeMethod) - * [.defaultSigningMethod()](#Document+defaultSigningMethod) ⇒ [VerificationMethod](#VerificationMethod) - * [.resolveMethod(query, scope)](#Document+resolveMethod) ⇒ [VerificationMethod](#VerificationMethod) \| undefined - * [.resolveSigningMethod(query)](#Document+resolveSigningMethod) ⇒ [VerificationMethod](#VerificationMethod) - * [.attachMethodRelationship(didUrl, relationship)](#Document+attachMethodRelationship) ⇒ boolean - * [.detachMethodRelationship(didUrl, relationship)](#Document+detachMethodRelationship) ⇒ boolean - * [.signSelf(key_pair, method_query)](#Document+signSelf) - * [.signDocument(document, key_pair, method_query)](#Document+signDocument) - * [.signCredential(credential, privateKey, methodQuery, options)](#Document+signCredential) ⇒ [Credential](#Credential) - * [.signPresentation(presentation, privateKey, methodQuery, options)](#Document+signPresentation) ⇒ [Presentation](#Presentation) - * [.signData(data, privateKey, methodQuery, options)](#Document+signData) ⇒ any - * [.verifyData(data, options)](#Document+verifyData) ⇒ boolean - * [.verifyDocument(signed)](#Document+verifyDocument) - * ~~[.diff(other, message_id, key, method_query)](#Document+diff) ⇒ [DiffMessage](#DiffMessage)~~ - * ~~[.verifyDiff(diff)](#Document+verifyDiff)~~ - * ~~[.mergeDiff(diff)](#Document+mergeDiff)~~ - * [.integrationIndex()](#Document+integrationIndex) ⇒ string - * [.metadata()](#Document+metadata) ⇒ [DocumentMetadata](#DocumentMetadata) - * [.metadataCreated()](#Document+metadataCreated) ⇒ [Timestamp](#Timestamp) \| undefined - * [.setMetadataCreated(timestamp)](#Document+setMetadataCreated) - * [.metadataUpdated()](#Document+metadataUpdated) ⇒ [Timestamp](#Timestamp) \| undefined - * [.setMetadataUpdated(timestamp)](#Document+setMetadataUpdated) - * [.metadataPreviousMessageId()](#Document+metadataPreviousMessageId) ⇒ string - * [.setMetadataPreviousMessageId(value)](#Document+setMetadataPreviousMessageId) - * [.setMetadataPropertyUnchecked(key, value)](#Document+setMetadataPropertyUnchecked) - * [.proof()](#Document+proof) ⇒ [Proof](#Proof) \| undefined - * [.revokeCredentials(serviceQuery, indices)](#Document+revokeCredentials) - * [.unrevokeCredentials(serviceQuery, indices)](#Document+unrevokeCredentials) - * [.toJSON()](#Document+toJSON) ⇒ any - * [.clone()](#Document+clone) ⇒ [Document](#Document) - * _static_ - * [.fromVerificationMethod(method)](#Document.fromVerificationMethod) ⇒ [Document](#Document) - * [.isSigningMethodType(method_type)](#Document.isSigningMethodType) ⇒ boolean - * [.verifyRootDocument(document)](#Document.verifyRootDocument) - * ~~[.diffIndex(message_id)](#Document.diffIndex) ⇒ string~~ - * [.fromJSON(json)](#Document.fromJSON) ⇒ [Document](#Document) - - - -### new Document(keypair, network, fragment) -Creates a new DID Document from the given `KeyPair`, network, and verification method -fragment name. - -The DID Document will be pre-populated with a single verification method -derived from the provided `KeyPair` embedded as a capability invocation -verification relationship. This method will have the DID URL fragment -`#sign-0` by default and can be easily retrieved with `Document::defaultSigningMethod`. - -NOTE: the generated document is unsigned, see `Document::signSelf`. - -Arguments: - -* keypair: the initial verification method is derived from the public key with this keypair. -* network: Tangle network to use for the DID, default `Network::mainnet`. -* fragment: name of the initial verification method, default "sign-0". - - -| Param | Type | -| --- | --- | -| keypair | [KeyPair](#KeyPair) | -| network | string \| undefined | -| fragment | string \| undefined | - - - -### document.id() ⇒ [IotaDID](#IotaDID) -Returns a copy of the DID Document `id`. - -**Kind**: instance method of [Document](#Document) - - -### document.setController(controllers) -Sets the controllers of the DID Document. - -Note: Duplicates will be ignored. -Use `null` to remove all controllers. - -**Kind**: instance method of [Document](#Document) - -| Param | Type | -| --- | --- | -| controllers | [IotaDID](#IotaDID) \| [Array.<IotaDID>](#IotaDID) \| null | - - - -### document.controller() ⇒ [Array.<IotaDID>](#IotaDID) -Returns a copy of the list of document controllers. - -**Kind**: instance method of [Document](#Document) - - -### document.setAlsoKnownAs(urls) -Sets the `alsoKnownAs` property in the DID document. - -**Kind**: instance method of [Document](#Document) - -| Param | Type | -| --- | --- | -| urls | string \| Array.<string> \| null | - - - -### document.alsoKnownAs() ⇒ Array.<string> -Returns a copy of the document's `alsoKnownAs` set. - -**Kind**: instance method of [Document](#Document) - - -### document.setPropertyUnchecked(key, value) -Adds a custom property to the DID Document. -If the value is set to `null`, the custom property will be removed. - -### WARNING -This method can overwrite existing properties like `id` and result in an invalid document. - -**Kind**: instance method of [Document](#Document) - -| Param | Type | -| --- | --- | -| key | string | -| value | any | - - - -### document.properties() ⇒ Map.<string, any> -Returns a copy of the custom DID Document properties. - -**Kind**: instance method of [Document](#Document) - - -### document.service() ⇒ [Array.<Service>](#Service) -Return a set of all [Services](#Service) in the document. - -**Kind**: instance method of [Document](#Document) - - -### document.insertService(service) ⇒ boolean -Add a new [Service](#Service) to the document. - -Returns `true` if the service was added. - -**Kind**: instance method of [Document](#Document) - -| Param | Type | -| --- | --- | -| service | [Service](#Service) | - - - -### document.removeService(did) ⇒ boolean -Remove a [Service](#Service) identified by the given [DIDUrl](#DIDUrl) from the document. - -Returns `true` if a service was removed. - -**Kind**: instance method of [Document](#Document) - -| Param | Type | -| --- | --- | -| did | [DIDUrl](#DIDUrl) | - - - -### document.resolveService(query) ⇒ [Service](#Service) \| undefined -Returns the first [Service](#Service) with an `id` property matching the provided `query`, -if present. - -**Kind**: instance method of [Document](#Document) - -| Param | Type | -| --- | --- | -| query | [DIDUrl](#DIDUrl) \| string | - - - -### document.methods() ⇒ [Array.<VerificationMethod>](#VerificationMethod) -Returns a list of all [VerificationMethod](#VerificationMethod) in the DID Document. - -**Kind**: instance method of [Document](#Document) - - -### document.insertMethod(method, scope) -Adds a new `method` to the document in the given `scope`. - -**Kind**: instance method of [Document](#Document) - -| Param | Type | -| --- | --- | -| method | [VerificationMethod](#VerificationMethod) | -| scope | [MethodScope](#MethodScope) | - - - -### document.removeMethod(did) -Removes all references to the specified Verification Method. - -**Kind**: instance method of [Document](#Document) - -| Param | Type | -| --- | --- | -| did | [DIDUrl](#DIDUrl) | - - - -### document.defaultSigningMethod() ⇒ [VerificationMethod](#VerificationMethod) -Returns a copy of the first `VerificationMethod` with a capability invocation relationship -capable of signing this DID document. - -Throws an error if no signing method is present. - -**Kind**: instance method of [Document](#Document) - - -### document.resolveMethod(query, scope) ⇒ [VerificationMethod](#VerificationMethod) \| undefined -Returns a copy of the first verification method with an `id` property -matching the provided `query` and the verification relationship -specified by `scope`, if present. - -**Kind**: instance method of [Document](#Document) - -| Param | Type | -| --- | --- | -| query | [DIDUrl](#DIDUrl) \| string | -| scope | [MethodScope](#MethodScope) \| undefined | - - - -### document.resolveSigningMethod(query) ⇒ [VerificationMethod](#VerificationMethod) -Attempts to resolve the given method query into a method capable of signing a document update. - -**Kind**: instance method of [Document](#Document) - -| Param | Type | -| --- | --- | -| query | [DIDUrl](#DIDUrl) \| string | - - - -### document.attachMethodRelationship(didUrl, relationship) ⇒ boolean -Attaches the relationship to the given method, if the method exists. - -Note: The method needs to be in the set of verification methods, -so it cannot be an embedded one. - -**Kind**: instance method of [Document](#Document) - -| Param | Type | -| --- | --- | -| didUrl | [DIDUrl](#DIDUrl) | -| relationship | number | - - - -### document.detachMethodRelationship(didUrl, relationship) ⇒ boolean -Detaches the given relationship from the given method, if the method exists. - -**Kind**: instance method of [Document](#Document) - -| Param | Type | -| --- | --- | -| didUrl | [DIDUrl](#DIDUrl) | -| relationship | number | - - - -### document.signSelf(key_pair, method_query) -Signs the DID document with the verification method specified by `method_query`. -The `method_query` may be the full `DIDUrl` of the method or just its fragment, -e.g. "#sign-0". - -NOTE: does not validate whether the private key of the given `key_pair` corresponds to the -verification method. See `Document::verifySelfSigned`. - -**Kind**: instance method of [Document](#Document) - -| Param | Type | -| --- | --- | -| key_pair | [KeyPair](#KeyPair) | -| method_query | [DIDUrl](#DIDUrl) \| string | - - - -### document.signDocument(document, key_pair, method_query) -Signs another DID document using the verification method specified by `method_query`. -The `method_query` may be the full `DIDUrl` of the method or just its fragment, -e.g. "#sign-0". - -`Document.signSelf` should be used in general, this throws an error if trying to operate -on the same document. This is intended for signing updates to a document where a sole -capability invocation method is rotated or replaced entirely. - -NOTE: does not validate whether the private key of the given `key_pair` corresponds to the -verification method. See [Document.verifyDocument](#Document+verifyDocument). - -**Kind**: instance method of [Document](#Document) - -| Param | Type | -| --- | --- | -| document | [Document](#Document) | -| key_pair | [KeyPair](#KeyPair) | -| method_query | [DIDUrl](#DIDUrl) \| string | - - - -### document.signCredential(credential, privateKey, methodQuery, options) ⇒ [Credential](#Credential) -Creates a signature for the given `Credential` with the specified DID Document -Verification Method. - -**Kind**: instance method of [Document](#Document) - -| Param | Type | -| --- | --- | -| credential | [Credential](#Credential) | -| privateKey | Uint8Array | -| methodQuery | [DIDUrl](#DIDUrl) \| string | -| options | [ProofOptions](#ProofOptions) | - - - -### document.signPresentation(presentation, privateKey, methodQuery, options) ⇒ [Presentation](#Presentation) -Creates a signature for the given `Presentation` with the specified DID Document -Verification Method. - -**Kind**: instance method of [Document](#Document) - -| Param | Type | -| --- | --- | -| presentation | [Presentation](#Presentation) | -| privateKey | Uint8Array | -| methodQuery | [DIDUrl](#DIDUrl) \| string | -| options | [ProofOptions](#ProofOptions) | - - - -### document.signData(data, privateKey, methodQuery, options) ⇒ any -Creates a signature for the given `data` with the specified DID Document -Verification Method. - -NOTE: use `signSelf` or `signDocument` for DID Documents. - -**Kind**: instance method of [Document](#Document) - -| Param | Type | -| --- | --- | -| data | any | -| privateKey | Uint8Array | -| methodQuery | [DIDUrl](#DIDUrl) \| string | -| options | [ProofOptions](#ProofOptions) | - - - -### document.verifyData(data, options) ⇒ boolean -Verifies the authenticity of `data` using the target verification method. - -**Kind**: instance method of [Document](#Document) - -| Param | Type | -| --- | --- | -| data | any | -| options | [VerifierOptions](#VerifierOptions) | - - - -### document.verifyDocument(signed) -Verifies that the signature on the DID document `signed` was generated by a valid method from -this DID document. - -# Errors - -Fails if: -- The signature proof section is missing in the `signed` document. -- The method is not found in this document. -- An unsupported verification method is used. -- The signature verification operation fails. - -**Kind**: instance method of [Document](#Document) - -| Param | Type | -| --- | --- | -| signed | [Document](#Document) | - - - -### ~~document.diff(other, message_id, key, method_query) ⇒ [DiffMessage](#DiffMessage)~~ -***Deprecated*** - -Generate a `DiffMessage` between two DID Documents and sign it using the specified -`key` and `method`. - -**Kind**: instance method of [Document](#Document) - -| Param | Type | -| --- | --- | -| other | [Document](#Document) | -| message_id | string | -| key | [KeyPair](#KeyPair) | -| method_query | [DIDUrl](#DIDUrl) \| string | - - - -### ~~document.verifyDiff(diff)~~ -***Deprecated*** - -Verifies the signature of the `diff` was created using a capability invocation method -in this DID Document. - -# Errors - -Fails if an unsupported verification method is used or the verification operation fails. - -**Kind**: instance method of [Document](#Document) - -| Param | Type | -| --- | --- | -| diff | [DiffMessage](#DiffMessage) | - - - -### ~~document.mergeDiff(diff)~~ -***Deprecated*** - -Verifies a `DiffMessage` signature and attempts to merge the changes into `self`. - -**Kind**: instance method of [Document](#Document) - -| Param | Type | -| --- | --- | -| diff | [DiffMessage](#DiffMessage) | - - - -### document.integrationIndex() ⇒ string -Returns the Tangle index of the integration chain for this DID. - -This is simply the tag segment of the `DID`. -E.g. -For a document with DID: did:iota:1234567890abcdefghijklmnopqrstuvxyzABCDEFGHI, -`doc.integration_index()` == "1234567890abcdefghijklmnopqrstuvxyzABCDEFGHI" - -**Kind**: instance method of [Document](#Document) - - -### document.metadata() ⇒ [DocumentMetadata](#DocumentMetadata) -Returns a copy of the metadata associated with this document. - -NOTE: Copies all the metadata. See also `metadataCreated`, `metadataUpdated`, -`metadataPreviousMessageId`, `metadataProof` if only a subset of the metadata required. - -**Kind**: instance method of [Document](#Document) - - -### document.metadataCreated() ⇒ [Timestamp](#Timestamp) \| undefined -Returns a copy of the timestamp of when the DID document was created. - -**Kind**: instance method of [Document](#Document) - - -### document.setMetadataCreated(timestamp) -Sets the timestamp of when the DID document was created. - -**Kind**: instance method of [Document](#Document) - -| Param | Type | -| --- | --- | -| timestamp | [Timestamp](#Timestamp) \| undefined | - - - -### document.metadataUpdated() ⇒ [Timestamp](#Timestamp) \| undefined -Returns a copy of the timestamp of the last DID document update. - -**Kind**: instance method of [Document](#Document) - - -### document.setMetadataUpdated(timestamp) -Sets the timestamp of the last DID document update. - -**Kind**: instance method of [Document](#Document) - -| Param | Type | -| --- | --- | -| timestamp | [Timestamp](#Timestamp) \| undefined | - - - -### document.metadataPreviousMessageId() ⇒ string -Returns a copy of the previous integration chain message id. - -**Kind**: instance method of [Document](#Document) - - -### document.setMetadataPreviousMessageId(value) -Sets the previous integration chain message id. - -**Kind**: instance method of [Document](#Document) - -| Param | Type | -| --- | --- | -| value | string | - - - -### document.setMetadataPropertyUnchecked(key, value) -Sets a custom property in the document metadata. -If the value is set to `null`, the custom property will be removed. - -**Kind**: instance method of [Document](#Document) - -| Param | Type | -| --- | --- | -| key | string | -| value | any | - - - -### document.proof() ⇒ [Proof](#Proof) \| undefined -Returns a copy of the proof. - -**Kind**: instance method of [Document](#Document) - - -### document.revokeCredentials(serviceQuery, indices) -If the document has a `RevocationBitmap` service identified by `serviceQuery`, -revoke all specified `indices`. - -**Kind**: instance method of [Document](#Document) - -| Param | Type | -| --- | --- | -| serviceQuery | [DIDUrl](#DIDUrl) \| string | -| indices | number \| Array.<number> | - - - -### document.unrevokeCredentials(serviceQuery, indices) -If the document has a `RevocationBitmap` service identified by `serviceQuery`, -unrevoke all specified `indices`. - -**Kind**: instance method of [Document](#Document) - -| Param | Type | -| --- | --- | -| serviceQuery | [DIDUrl](#DIDUrl) \| string | -| indices | number \| Array.<number> | - - - -### document.toJSON() ⇒ any -Serializes this to a JSON object. - -**Kind**: instance method of [Document](#Document) - - -### document.clone() ⇒ [Document](#Document) -Deep clones the object. - -**Kind**: instance method of [Document](#Document) - - -### Document.fromVerificationMethod(method) ⇒ [Document](#Document) -Creates a new DID Document from the given `VerificationMethod`. - -NOTE: the generated document is unsigned, see `Document::signSelf`. - -**Kind**: static method of [Document](#Document) - -| Param | Type | -| --- | --- | -| method | [VerificationMethod](#VerificationMethod) | - - - -### Document.isSigningMethodType(method_type) ⇒ boolean -Returns whether the given [MethodType](#MethodType) can be used to sign document updates. - -**Kind**: static method of [Document](#Document) - -| Param | Type | -| --- | --- | -| method_type | [MethodType](#MethodType) | - - - -### Document.verifyRootDocument(document) -Verifies whether `document` is a valid root DID document according to the IOTA DID method -specification. - -It must be signed using a verification method with a public key whose BLAKE2b-256 hash matches -the DID tag. - -**Kind**: static method of [Document](#Document) - -| Param | Type | -| --- | --- | -| document | [Document](#Document) | - - - -### ~~Document.diffIndex(message_id) ⇒ string~~ -***Deprecated*** - -Returns the Tangle index of the DID diff chain. This should only be called on documents -published on the integration chain. - -This is the Base58-btc encoded SHA-256 digest of the hex-encoded message id. - -**Kind**: static method of [Document](#Document) - -| Param | Type | -| --- | --- | -| message_id | string | - - - -### Document.fromJSON(json) ⇒ [Document](#Document) -Deserializes an instance from a JSON object. - -**Kind**: static method of [Document](#Document) - -| Param | Type | -| --- | --- | -| json | any | - - - -## DocumentHistory -A DID Document's history and current state. - -**Kind**: global class - -* [DocumentHistory](#DocumentHistory) - * _instance_ - * [.integrationChainData()](#DocumentHistory+integrationChainData) ⇒ [Array.<ResolvedDocument>](#ResolvedDocument) - * [.integrationChainSpam()](#DocumentHistory+integrationChainSpam) ⇒ Array.<string> - * ~~[.diffChainData()](#DocumentHistory+diffChainData) ⇒ [Array.<DiffMessage>](#DiffMessage)~~ - * ~~[.diffChainSpam()](#DocumentHistory+diffChainSpam) ⇒ Array.<string>~~ - * [.toJSON()](#DocumentHistory+toJSON) ⇒ any - * [.clone()](#DocumentHistory+clone) ⇒ [DocumentHistory](#DocumentHistory) - * _static_ - * [.fromJSON(json)](#DocumentHistory.fromJSON) ⇒ [DocumentHistory](#DocumentHistory) - - - -### documentHistory.integrationChainData() ⇒ [Array.<ResolvedDocument>](#ResolvedDocument) -Returns an `Array` of integration chain `Documents`. - -NOTE: clones the data. - -**Kind**: instance method of [DocumentHistory](#DocumentHistory) - - -### documentHistory.integrationChainSpam() ⇒ Array.<string> -Returns an `Array` of message id strings for "spam" messages on the same index -as the integration chain. - -NOTE: clones the data. - -**Kind**: instance method of [DocumentHistory](#DocumentHistory) - - -### ~~documentHistory.diffChainData() ⇒ [Array.<DiffMessage>](#DiffMessage)~~ -***Deprecated*** - -Returns an `Array` of diff chain `DiffMessages`. - -NOTE: clones the data. - -**Kind**: instance method of [DocumentHistory](#DocumentHistory) - - -### ~~documentHistory.diffChainSpam() ⇒ Array.<string>~~ -***Deprecated*** - -Returns an `Array` of message id strings for "spam" messages on the same index -as the diff chain. - -NOTE: clones the data. - -**Kind**: instance method of [DocumentHistory](#DocumentHistory) - - -### documentHistory.toJSON() ⇒ any -Serializes this to a JSON object. - -**Kind**: instance method of [DocumentHistory](#DocumentHistory) - - -### documentHistory.clone() ⇒ [DocumentHistory](#DocumentHistory) -Deep clones the object. - -**Kind**: instance method of [DocumentHistory](#DocumentHistory) - - -### DocumentHistory.fromJSON(json) ⇒ [DocumentHistory](#DocumentHistory) -Deserializes an instance from a JSON object. - -**Kind**: static method of [DocumentHistory](#DocumentHistory) - -| Param | Type | -| --- | --- | -| json | any | - - - -## DocumentMetadata -Additional attributes related to an IOTA DID Document. - -**Kind**: global class - -* [DocumentMetadata](#DocumentMetadata) - * _instance_ - * [.created()](#DocumentMetadata+created) ⇒ [Timestamp](#Timestamp) \| undefined - * [.updated()](#DocumentMetadata+updated) ⇒ [Timestamp](#Timestamp) \| undefined - * [.previousMessageId()](#DocumentMetadata+previousMessageId) ⇒ string - * [.properties()](#DocumentMetadata+properties) ⇒ Map.<string, any> - * [.toJSON()](#DocumentMetadata+toJSON) ⇒ any - * [.clone()](#DocumentMetadata+clone) ⇒ [DocumentMetadata](#DocumentMetadata) - * _static_ - * [.fromJSON(json)](#DocumentMetadata.fromJSON) ⇒ [DocumentMetadata](#DocumentMetadata) - - - -### documentMetadata.created() ⇒ [Timestamp](#Timestamp) \| undefined -Returns a copy of the timestamp of when the DID document was created. - -**Kind**: instance method of [DocumentMetadata](#DocumentMetadata) - - -### documentMetadata.updated() ⇒ [Timestamp](#Timestamp) \| undefined -Returns a copy of the timestamp of the last DID document update. - -**Kind**: instance method of [DocumentMetadata](#DocumentMetadata) - - -### documentMetadata.previousMessageId() ⇒ string -Returns a copy of the previous message identifier. - -**Kind**: instance method of [DocumentMetadata](#DocumentMetadata) - - -### documentMetadata.properties() ⇒ Map.<string, any> -Returns a copy of the custom metadata properties. - -**Kind**: instance method of [DocumentMetadata](#DocumentMetadata) - - -### documentMetadata.toJSON() ⇒ any -Serializes this to a JSON object. - -**Kind**: instance method of [DocumentMetadata](#DocumentMetadata) - - -### documentMetadata.clone() ⇒ [DocumentMetadata](#DocumentMetadata) -Deep clones the object. - -**Kind**: instance method of [DocumentMetadata](#DocumentMetadata) - - -### DocumentMetadata.fromJSON(json) ⇒ [DocumentMetadata](#DocumentMetadata) -Deserializes an instance from a JSON object. - -**Kind**: static method of [DocumentMetadata](#DocumentMetadata) - -| Param | Type | -| --- | --- | -| json | any | - - - -## Duration -A span of time. - -**Kind**: global class - -* [Duration](#Duration) - * _instance_ - * [.toJSON()](#Duration+toJSON) ⇒ any - * _static_ - * [.seconds(seconds)](#Duration.seconds) ⇒ [Duration](#Duration) - * [.minutes(minutes)](#Duration.minutes) ⇒ [Duration](#Duration) - * [.hours(hours)](#Duration.hours) ⇒ [Duration](#Duration) - * [.days(days)](#Duration.days) ⇒ [Duration](#Duration) - * [.weeks(weeks)](#Duration.weeks) ⇒ [Duration](#Duration) - * [.fromJSON(json)](#Duration.fromJSON) ⇒ [Duration](#Duration) - - - -### duration.toJSON() ⇒ any -Serializes this to a JSON object. - -**Kind**: instance method of [Duration](#Duration) - - -### Duration.seconds(seconds) ⇒ [Duration](#Duration) -Create a new `Duration` with the given number of seconds. - -**Kind**: static method of [Duration](#Duration) - -| Param | Type | -| --- | --- | -| seconds | number | - - - -### Duration.minutes(minutes) ⇒ [Duration](#Duration) -Create a new `Duration` with the given number of minutes. - -**Kind**: static method of [Duration](#Duration) - -| Param | Type | -| --- | --- | -| minutes | number | - - - -### Duration.hours(hours) ⇒ [Duration](#Duration) -Create a new `Duration` with the given number of hours. - -**Kind**: static method of [Duration](#Duration) - -| Param | Type | -| --- | --- | -| hours | number | - - - -### Duration.days(days) ⇒ [Duration](#Duration) -Create a new `Duration` with the given number of days. - -**Kind**: static method of [Duration](#Duration) - -| Param | Type | -| --- | --- | -| days | number | - - - -### Duration.weeks(weeks) ⇒ [Duration](#Duration) -Create a new `Duration` with the given number of weeks. - -**Kind**: static method of [Duration](#Duration) - -| Param | Type | -| --- | --- | -| weeks | number | - - - -### Duration.fromJSON(json) ⇒ [Duration](#Duration) -Deserializes an instance from a JSON object. - -**Kind**: static method of [Duration](#Duration) - -| Param | Type | -| --- | --- | -| json | any | - - - -## Ed25519 -**Kind**: global class - -* [Ed25519](#Ed25519) - * [.PRIVATE_KEY_LENGTH()](#Ed25519.PRIVATE_KEY_LENGTH) ⇒ number - * [.PUBLIC_KEY_LENGTH()](#Ed25519.PUBLIC_KEY_LENGTH) ⇒ number - * [.SIGNATURE_LENGTH()](#Ed25519.SIGNATURE_LENGTH) ⇒ number - * [.sign(message, privateKey)](#Ed25519.sign) ⇒ Uint8Array - * [.verify(message, signature, publicKey)](#Ed25519.verify) - - - -### Ed25519.PRIVATE\_KEY\_LENGTH() ⇒ number -Length in bytes of an Ed25519 private key. - -**Kind**: static method of [Ed25519](#Ed25519) - - -### Ed25519.PUBLIC\_KEY\_LENGTH() ⇒ number -Length in bytes of an Ed25519 public key. - -**Kind**: static method of [Ed25519](#Ed25519) - - -### Ed25519.SIGNATURE\_LENGTH() ⇒ number -Length in bytes of an Ed25519 signature. - -**Kind**: static method of [Ed25519](#Ed25519) - - -### Ed25519.sign(message, privateKey) ⇒ Uint8Array -Computes an EdDSA signature using an Ed25519 private key. - -NOTE: this differs from [Document.signData](#Document+signData) which uses JCS -to canonicalize JSON messages. - -The private key must be a 32-byte seed in compliance with [RFC 8032](https://datatracker.ietf.org/doc/html/rfc8032#section-3.2). -Other implementations often use another format. See [this blog post](https://blog.mozilla.org/warner/2011/11/29/ed25519-keys/) for further explanation. - -**Kind**: static method of [Ed25519](#Ed25519) - -| Param | Type | -| --- | --- | -| message | Uint8Array | -| privateKey | Uint8Array | - - - -### Ed25519.verify(message, signature, publicKey) -Verifies an EdDSA signature against an Ed25519 public key. - -NOTE: this differs from [Document.verifyData](#Document+verifyData) which uses JCS -to canonicalize JSON messages. - -**Kind**: static method of [Ed25519](#Ed25519) - -| Param | Type | -| --- | --- | -| message | Uint8Array | -| signature | Uint8Array | -| publicKey | Uint8Array | - - - -## EncryptedData -The structure returned after encrypting data - -**Kind**: global class - -* [EncryptedData](#EncryptedData) - * _instance_ - * [.nonce()](#EncryptedData+nonce) ⇒ Uint8Array - * [.associatedData()](#EncryptedData+associatedData) ⇒ Uint8Array - * [.ciphertext()](#EncryptedData+ciphertext) ⇒ Uint8Array - * [.tag()](#EncryptedData+tag) ⇒ Uint8Array - * [.toJSON()](#EncryptedData+toJSON) ⇒ any - * _static_ - * [.fromJSON(json)](#EncryptedData.fromJSON) ⇒ [EncryptedData](#EncryptedData) - - - -### encryptedData.nonce() ⇒ Uint8Array -Returns a copy of the nonce - -**Kind**: instance method of [EncryptedData](#EncryptedData) - - -### encryptedData.associatedData() ⇒ Uint8Array -Returns a copy of the associated data - -**Kind**: instance method of [EncryptedData](#EncryptedData) - - -### encryptedData.ciphertext() ⇒ Uint8Array -Returns a copy of the ciphertext - -**Kind**: instance method of [EncryptedData](#EncryptedData) - - -### encryptedData.tag() ⇒ Uint8Array -Returns a copy of the tag - -**Kind**: instance method of [EncryptedData](#EncryptedData) - - -### encryptedData.toJSON() ⇒ any -Serializes this to a JSON object. - -**Kind**: instance method of [EncryptedData](#EncryptedData) - - -### EncryptedData.fromJSON(json) ⇒ [EncryptedData](#EncryptedData) -Deserializes an instance from a JSON object. - -**Kind**: static method of [EncryptedData](#EncryptedData) - -| Param | Type | -| --- | --- | -| json | any | - - - -## EncryptionAlgorithm -Supported content encryption algorithms. - -**Kind**: global class - -* [EncryptionAlgorithm](#EncryptionAlgorithm) - * _instance_ - * [.keyLength()](#EncryptionAlgorithm+keyLength) ⇒ number - * [.toJSON()](#EncryptionAlgorithm+toJSON) ⇒ any - * _static_ - * [.A256GCM()](#EncryptionAlgorithm.A256GCM) ⇒ [EncryptionAlgorithm](#EncryptionAlgorithm) - * [.fromJSON(json)](#EncryptionAlgorithm.fromJSON) ⇒ [EncryptionAlgorithm](#EncryptionAlgorithm) - - - -### encryptionAlgorithm.keyLength() ⇒ number -Returns the length of the cipher's key. - -**Kind**: instance method of [EncryptionAlgorithm](#EncryptionAlgorithm) - - -### encryptionAlgorithm.toJSON() ⇒ any -Serializes this to a JSON object. - -**Kind**: instance method of [EncryptionAlgorithm](#EncryptionAlgorithm) - - -### EncryptionAlgorithm.A256GCM() ⇒ [EncryptionAlgorithm](#EncryptionAlgorithm) -AES GCM using 256-bit key. - -**Kind**: static method of [EncryptionAlgorithm](#EncryptionAlgorithm) - - -### EncryptionAlgorithm.fromJSON(json) ⇒ [EncryptionAlgorithm](#EncryptionAlgorithm) -Deserializes an instance from a JSON object. - -**Kind**: static method of [EncryptionAlgorithm](#EncryptionAlgorithm) - -| Param | Type | -| --- | --- | -| json | any | - - - -## ExplorerUrl -**Kind**: global class - -* [ExplorerUrl](#ExplorerUrl) - * _instance_ - * [.messageUrl(message_id)](#ExplorerUrl+messageUrl) ⇒ string - * [.resolverUrl(did)](#ExplorerUrl+resolverUrl) ⇒ string - * [.toString()](#ExplorerUrl+toString) ⇒ string - * [.toJSON()](#ExplorerUrl+toJSON) ⇒ any - * _static_ - * [.parse(url)](#ExplorerUrl.parse) ⇒ [ExplorerUrl](#ExplorerUrl) - * [.mainnet()](#ExplorerUrl.mainnet) ⇒ [ExplorerUrl](#ExplorerUrl) - * [.devnet()](#ExplorerUrl.devnet) ⇒ [ExplorerUrl](#ExplorerUrl) - * [.fromJSON(json)](#ExplorerUrl.fromJSON) ⇒ [ExplorerUrl](#ExplorerUrl) - - - -### explorerUrl.messageUrl(message_id) ⇒ string -Returns the web explorer URL of the given `message_id`. - -E.g. https://explorer.iota.org/mainnet/message/{message_id} - -**Kind**: instance method of [ExplorerUrl](#ExplorerUrl) - -| Param | Type | -| --- | --- | -| message_id | string | - - - -### explorerUrl.resolverUrl(did) ⇒ string -Returns the web identity resolver URL for the given DID. - -E.g. https://explorer.iota.org/mainnet/identity-resolver/{did} - -**Kind**: instance method of [ExplorerUrl](#ExplorerUrl) - -| Param | Type | -| --- | --- | -| did | [IotaDID](#IotaDID) \| string | - - - -### explorerUrl.toString() ⇒ string -**Kind**: instance method of [ExplorerUrl](#ExplorerUrl) - - -### explorerUrl.toJSON() ⇒ any -Serializes this to a JSON object. - -**Kind**: instance method of [ExplorerUrl](#ExplorerUrl) - - -### ExplorerUrl.parse(url) ⇒ [ExplorerUrl](#ExplorerUrl) -Constructs a new Tangle explorer URL from a string. - -Use `ExplorerUrl::mainnet` or `ExplorerUrl::devnet` unless using a private Tangle -or local explorer. - -**Kind**: static method of [ExplorerUrl](#ExplorerUrl) - -| Param | Type | -| --- | --- | -| url | string | - - - -### ExplorerUrl.mainnet() ⇒ [ExplorerUrl](#ExplorerUrl) -Returns the Tangle explorer URL for the mainnet. - -**Kind**: static method of [ExplorerUrl](#ExplorerUrl) - - -### ExplorerUrl.devnet() ⇒ [ExplorerUrl](#ExplorerUrl) -Returns the Tangle explorer URL for the devnet. - -**Kind**: static method of [ExplorerUrl](#ExplorerUrl) - - -### ExplorerUrl.fromJSON(json) ⇒ [ExplorerUrl](#ExplorerUrl) -Deserializes an instance from a JSON object. - -**Kind**: static method of [ExplorerUrl](#ExplorerUrl) - -| Param | Type | -| --- | --- | -| json | any | - - - -## IntegrationChainHistory -**Kind**: global class - -* [IntegrationChainHistory](#IntegrationChainHistory) - * _instance_ - * [.chainData()](#IntegrationChainHistory+chainData) ⇒ [Array.<ResolvedDocument>](#ResolvedDocument) - * [.spam()](#IntegrationChainHistory+spam) ⇒ Array.<string> - * [.toJSON()](#IntegrationChainHistory+toJSON) ⇒ any - * _static_ - * [.fromJSON(json)](#IntegrationChainHistory.fromJSON) ⇒ [IntegrationChainHistory](#IntegrationChainHistory) - - - -### integrationChainHistory.chainData() ⇒ [Array.<ResolvedDocument>](#ResolvedDocument) -Returns an `Array` of the integration chain `Documents`. - -NOTE: this clones the field. - -**Kind**: instance method of [IntegrationChainHistory](#IntegrationChainHistory) - - -### integrationChainHistory.spam() ⇒ Array.<string> -Returns an `Array` of `MessageIds` as strings. - -NOTE: this clones the field. - -**Kind**: instance method of [IntegrationChainHistory](#IntegrationChainHistory) - - -### integrationChainHistory.toJSON() ⇒ any -Serializes as a JSON object. - -**Kind**: instance method of [IntegrationChainHistory](#IntegrationChainHistory) - - -### IntegrationChainHistory.fromJSON(json) ⇒ [IntegrationChainHistory](#IntegrationChainHistory) -Deserializes from a JSON object. - -**Kind**: static method of [IntegrationChainHistory](#IntegrationChainHistory) - -| Param | Type | -| --- | --- | -| json | any | - - - -## IotaDID -A DID conforming to the IOTA DID method specification. - -**Kind**: global class - -* [IotaDID](#IotaDID) - * [new IotaDID(public_key, network)](#new_IotaDID_new) - * _instance_ - * [.network()](#IotaDID+network) ⇒ [Network](#Network) - * [.networkStr()](#IotaDID+networkStr) ⇒ string - * [.tag()](#IotaDID+tag) ⇒ string - * [.scheme()](#IotaDID+scheme) ⇒ string - * [.authority()](#IotaDID+authority) ⇒ string - * [.method()](#IotaDID+method) ⇒ string - * [.methodId()](#IotaDID+methodId) ⇒ string - * [.join(segment)](#IotaDID+join) ⇒ [DIDUrl](#DIDUrl) - * [.toUrl()](#IotaDID+toUrl) ⇒ [DIDUrl](#DIDUrl) - * [.intoUrl()](#IotaDID+intoUrl) ⇒ [DIDUrl](#DIDUrl) - * [.toString()](#IotaDID+toString) ⇒ string - * [.toJSON()](#IotaDID+toJSON) ⇒ any - * [.clone()](#IotaDID+clone) ⇒ [IotaDID](#IotaDID) - * _static_ - * [.METHOD](#IotaDID.METHOD) ⇒ string - * [.DEFAULT_NETWORK](#IotaDID.DEFAULT_NETWORK) ⇒ string - * [.parse(input)](#IotaDID.parse) ⇒ [IotaDID](#IotaDID) - * [.fromJSON(json)](#IotaDID.fromJSON) ⇒ [IotaDID](#IotaDID) - - - -### new IotaDID(public_key, network) -Creates a new `DID` from a public key. - - -| Param | Type | -| --- | --- | -| public_key | Uint8Array | -| network | string \| undefined | - - - -### iotaDID.network() ⇒ [Network](#Network) -Returns the Tangle network of the `IotaDID`. - -**Kind**: instance method of [IotaDID](#IotaDID) - - -### iotaDID.networkStr() ⇒ string -Returns the Tangle network name of the `IotaDID`. - -**Kind**: instance method of [IotaDID](#IotaDID) - - -### iotaDID.tag() ⇒ string -Returns a copy of the unique tag of the `IotaDID`. - -**Kind**: instance method of [IotaDID](#IotaDID) - - -### iotaDID.scheme() ⇒ string -Returns the `DID` scheme. - -E.g. -- `"did:example:12345678" -> "did"` -- `"did:iota:main:12345678" -> "did"` - -**Kind**: instance method of [IotaDID](#IotaDID) - - -### iotaDID.authority() ⇒ string -Returns the `DID` authority: the method name and method-id. - -E.g. -- `"did:example:12345678" -> "example:12345678"` -- `"did:iota:main:12345678" -> "iota:main:12345678"` - -**Kind**: instance method of [IotaDID](#IotaDID) - - -### iotaDID.method() ⇒ string -Returns the `DID` method name. - -E.g. -- `"did:example:12345678" -> "example"` -- `"did:iota:main:12345678" -> "iota"` - -**Kind**: instance method of [IotaDID](#IotaDID) - - -### iotaDID.methodId() ⇒ string -Returns the `DID` method-specific ID. - -E.g. -- `"did:example:12345678" -> "12345678"` -- `"did:iota:main:12345678" -> "main:12345678"` - -**Kind**: instance method of [IotaDID](#IotaDID) - - -### iotaDID.join(segment) ⇒ [DIDUrl](#DIDUrl) -Construct a new `DIDUrl` by joining with a relative DID Url string. - -**Kind**: instance method of [IotaDID](#IotaDID) - -| Param | Type | -| --- | --- | -| segment | string | - - - -### iotaDID.toUrl() ⇒ [DIDUrl](#DIDUrl) -Clones the `IotaDID` into a `DIDUrl`. - -**Kind**: instance method of [IotaDID](#IotaDID) - - -### iotaDID.intoUrl() ⇒ [DIDUrl](#DIDUrl) -Converts the `IotaDID` into a `DIDUrl`, consuming it. - -**Kind**: instance method of [IotaDID](#IotaDID) - - -### iotaDID.toString() ⇒ string -Returns the `IotaDID` as a string. - -**Kind**: instance method of [IotaDID](#IotaDID) - - -### iotaDID.toJSON() ⇒ any -Serializes this to a JSON object. - -**Kind**: instance method of [IotaDID](#IotaDID) - - -### iotaDID.clone() ⇒ [IotaDID](#IotaDID) -Deep clones the object. - -**Kind**: instance method of [IotaDID](#IotaDID) - - -### IotaDID.METHOD ⇒ string -The IOTA DID method name (`"iota"`). - -**Kind**: static property of [IotaDID](#IotaDID) - - -### IotaDID.DEFAULT\_NETWORK ⇒ string -The default Tangle network (`"main"`). - -**Kind**: static property of [IotaDID](#IotaDID) - - -### IotaDID.parse(input) ⇒ [IotaDID](#IotaDID) -Parses a `IotaDID` from the input string. - -**Kind**: static method of [IotaDID](#IotaDID) - -| Param | Type | -| --- | --- | -| input | string | - - - -### IotaDID.fromJSON(json) ⇒ [IotaDID](#IotaDID) -Deserializes an instance from a JSON object. - -**Kind**: static method of [IotaDID](#IotaDID) - -| Param | Type | -| --- | --- | -| json | any | - - - -## KeyLocation -The storage location of a verification method key. - -A key is uniquely identified by the fragment and a hash of its public key. -Importantly, the fragment alone is insufficient to represent the storage location. -For example, when rotating a key, there will be two keys in storage for the -same identity with the same fragment. The `key_hash` disambiguates the keys in -situations like these. - -The string representation of that location can be obtained via `canonicalRepr`. - -**Kind**: global class - -* [KeyLocation](#KeyLocation) - * [new KeyLocation(keyType, fragment, publicKey)](#new_KeyLocation_new) - * _instance_ - * [.canonical()](#KeyLocation+canonical) ⇒ string - * [.keyType()](#KeyLocation+keyType) ⇒ number - * [.toString()](#KeyLocation+toString) ⇒ string - * [.toJSON()](#KeyLocation+toJSON) ⇒ any - * _static_ - * [.fromVerificationMethod(method)](#KeyLocation.fromVerificationMethod) ⇒ [KeyLocation](#KeyLocation) - * [.fromJSON(json)](#KeyLocation.fromJSON) ⇒ [KeyLocation](#KeyLocation) - - - -### new KeyLocation(keyType, fragment, publicKey) -Create a location from a `KeyType`, the fragment of a verification method -and the bytes of a public key. - - -| Param | Type | -| --- | --- | -| keyType | number | -| fragment | string | -| publicKey | Uint8Array | - - - -### keyLocation.canonical() ⇒ string -Returns the canonical string representation of the location. - -This should be used as the representation for storage keys. - -**Kind**: instance method of [KeyLocation](#KeyLocation) - - -### keyLocation.keyType() ⇒ number -Returns a copy of the key type of the key location. - -**Kind**: instance method of [KeyLocation](#KeyLocation) - - -### keyLocation.toString() ⇒ string -**Kind**: instance method of [KeyLocation](#KeyLocation) - - -### keyLocation.toJSON() ⇒ any -Serializes this to a JSON object. - -**Kind**: instance method of [KeyLocation](#KeyLocation) - - -### KeyLocation.fromVerificationMethod(method) ⇒ [KeyLocation](#KeyLocation) -Obtain the location of a verification method's key in storage. - -**Kind**: static method of [KeyLocation](#KeyLocation) - -| Param | Type | -| --- | --- | -| method | [VerificationMethod](#VerificationMethod) | - - - -### KeyLocation.fromJSON(json) ⇒ [KeyLocation](#KeyLocation) -Deserializes an instance from a JSON object. - -**Kind**: static method of [KeyLocation](#KeyLocation) - -| Param | Type | -| --- | --- | -| json | any | - - - -## KeyPair -**Kind**: global class - -* [KeyPair](#KeyPair) - * [new KeyPair(type_)](#new_KeyPair_new) - * _instance_ - * [.type()](#KeyPair+type) ⇒ number - * [.public()](#KeyPair+public) ⇒ Uint8Array - * [.private()](#KeyPair+private) ⇒ Uint8Array - * [.toJSON()](#KeyPair+toJSON) ⇒ any - * [.clone()](#KeyPair+clone) ⇒ [KeyPair](#KeyPair) - * _static_ - * [.fromKeys(type_, public_key, private_key)](#KeyPair.fromKeys) ⇒ [KeyPair](#KeyPair) - * [.tryFromPrivateKeyBytes(keyType, privateKeyBytes)](#KeyPair.tryFromPrivateKeyBytes) ⇒ [KeyPair](#KeyPair) - * [.fromJSON(json)](#KeyPair.fromJSON) ⇒ [KeyPair](#KeyPair) - - - -### new KeyPair(type_) -Generates a new `KeyPair` object. - - -| Param | Type | -| --- | --- | -| type_ | number | - - - -### keyPair.type() ⇒ number -Returns the `KeyType` of the `KeyPair` object. - -**Kind**: instance method of [KeyPair](#KeyPair) - - -### keyPair.public() ⇒ Uint8Array -Returns a copy of the public key as a `Uint8Array`. - -**Kind**: instance method of [KeyPair](#KeyPair) - - -### keyPair.private() ⇒ Uint8Array -Returns a copy of the private key as a `Uint8Array`. - -**Kind**: instance method of [KeyPair](#KeyPair) - - -### keyPair.toJSON() ⇒ any -Serializes a `KeyPair` object as a JSON object. - -**Kind**: instance method of [KeyPair](#KeyPair) - - -### keyPair.clone() ⇒ [KeyPair](#KeyPair) -Deep clones the object. - -**Kind**: instance method of [KeyPair](#KeyPair) - - -### KeyPair.fromKeys(type_, public_key, private_key) ⇒ [KeyPair](#KeyPair) -Parses a `KeyPair` object from the public/private keys. - -**Kind**: static method of [KeyPair](#KeyPair) - -| Param | Type | -| --- | --- | -| type_ | number | -| public_key | Uint8Array | -| private_key | Uint8Array | - - - -### KeyPair.tryFromPrivateKeyBytes(keyType, privateKeyBytes) ⇒ [KeyPair](#KeyPair) -Reconstructs a `KeyPair` from the bytes of a private key. - -The private key for `Ed25519` must be a 32-byte seed in compliance -with [RFC 8032](https://datatracker.ietf.org/doc/html/rfc8032#section-3.2). -Other implementations often use another format. See [this blog post](https://blog.mozilla.org/warner/2011/11/29/ed25519-keys/) for further explanation. - -**Kind**: static method of [KeyPair](#KeyPair) - -| Param | Type | -| --- | --- | -| keyType | number | -| privateKeyBytes | Uint8Array | - - - -### KeyPair.fromJSON(json) ⇒ [KeyPair](#KeyPair) -Deserializes a `KeyPair` object from a JSON object. - -**Kind**: static method of [KeyPair](#KeyPair) - -| Param | Type | -| --- | --- | -| json | any | - - - -## MethodContent -**Kind**: global class - -* [MethodContent](#MethodContent) - * _instance_ - * [.toJSON()](#MethodContent+toJSON) ⇒ any - * _static_ - * [.GenerateEd25519()](#MethodContent.GenerateEd25519) ⇒ [MethodContent](#MethodContent) - * [.PrivateEd25519(privateKey)](#MethodContent.PrivateEd25519) ⇒ [MethodContent](#MethodContent) - * [.PublicEd25519(publicKey)](#MethodContent.PublicEd25519) ⇒ [MethodContent](#MethodContent) - * [.GenerateX25519()](#MethodContent.GenerateX25519) ⇒ [MethodContent](#MethodContent) - * [.PrivateX25519(privateKey)](#MethodContent.PrivateX25519) ⇒ [MethodContent](#MethodContent) - * [.PublicX25519(publicKey)](#MethodContent.PublicX25519) ⇒ [MethodContent](#MethodContent) - * [.fromJSON(json)](#MethodContent.fromJSON) ⇒ [MethodContent](#MethodContent) - - - -### methodContent.toJSON() ⇒ any -Serializes this to a JSON object. - -**Kind**: instance method of [MethodContent](#MethodContent) - - -### MethodContent.GenerateEd25519() ⇒ [MethodContent](#MethodContent) -Generate and store a new Ed25519 keypair for a new `Ed25519VerificationKey2018` method. - -**Kind**: static method of [MethodContent](#MethodContent) - - -### MethodContent.PrivateEd25519(privateKey) ⇒ [MethodContent](#MethodContent) -Store an existing Ed25519 private key and derive a public key from it for a new -`Ed25519VerificationKey2018` method. - -**Kind**: static method of [MethodContent](#MethodContent) - -| Param | Type | -| --- | --- | -| privateKey | Uint8Array | - - - -### MethodContent.PublicEd25519(publicKey) ⇒ [MethodContent](#MethodContent) -Insert an existing Ed25519 public key into a new `Ed25519VerificationKey2018` method, -without generating or storing a private key. - -NOTE: the method will be unable to be used to sign anything without a private key. - -**Kind**: static method of [MethodContent](#MethodContent) - -| Param | Type | -| --- | --- | -| publicKey | Uint8Array | - - - -### MethodContent.GenerateX25519() ⇒ [MethodContent](#MethodContent) -Generate and store a new X25519 keypair for a new `X25519KeyAgreementKey2019` method. - -**Kind**: static method of [MethodContent](#MethodContent) - - -### MethodContent.PrivateX25519(privateKey) ⇒ [MethodContent](#MethodContent) -Store an existing X25519 private key and derive a public key from it for a new -`X25519KeyAgreementKey2019` method. - -**Kind**: static method of [MethodContent](#MethodContent) - -| Param | Type | -| --- | --- | -| privateKey | Uint8Array | - - - -### MethodContent.PublicX25519(publicKey) ⇒ [MethodContent](#MethodContent) -Insert an existing X25519 public key into a new `X25519KeyAgreementKey2019` method, -without generating or storing a private key. - -NOTE: the method will be unable to be used for key exchange without a private key. - -**Kind**: static method of [MethodContent](#MethodContent) - -| Param | Type | -| --- | --- | -| publicKey | Uint8Array | - - - -### MethodContent.fromJSON(json) ⇒ [MethodContent](#MethodContent) -Deserializes an instance from a JSON object. - -**Kind**: static method of [MethodContent](#MethodContent) - -| Param | Type | -| --- | --- | -| json | any | - - - -## MethodData -Supported verification method data formats. - -**Kind**: global class - -* [MethodData](#MethodData) - * _instance_ - * [.tryDecode()](#MethodData+tryDecode) ⇒ Uint8Array - * [.toJSON()](#MethodData+toJSON) ⇒ any - * [.clone()](#MethodData+clone) ⇒ [MethodData](#MethodData) - * _static_ - * [.newBase58(data)](#MethodData.newBase58) ⇒ [MethodData](#MethodData) - * [.newMultibase(data)](#MethodData.newMultibase) ⇒ [MethodData](#MethodData) - * [.fromJSON(json)](#MethodData.fromJSON) ⇒ [MethodData](#MethodData) - - - -### methodData.tryDecode() ⇒ Uint8Array -Returns a `Uint8Array` containing the decoded bytes of the `MethodData`. - -This is generally a public key identified by a `MethodData` value. - -### Errors -Decoding can fail if `MethodData` has invalid content or cannot be -represented as a vector of bytes. - -**Kind**: instance method of [MethodData](#MethodData) - - -### methodData.toJSON() ⇒ any -Serializes this to a JSON object. - -**Kind**: instance method of [MethodData](#MethodData) - - -### methodData.clone() ⇒ [MethodData](#MethodData) -Deep clones the object. - -**Kind**: instance method of [MethodData](#MethodData) - - -### MethodData.newBase58(data) ⇒ [MethodData](#MethodData) -Creates a new `MethodData` variant with Base58-BTC encoded content. - -**Kind**: static method of [MethodData](#MethodData) - -| Param | Type | -| --- | --- | -| data | Uint8Array | - - - -### MethodData.newMultibase(data) ⇒ [MethodData](#MethodData) -Creates a new `MethodData` variant with Multibase-encoded content. - -**Kind**: static method of [MethodData](#MethodData) - -| Param | Type | -| --- | --- | -| data | Uint8Array | - - - -### MethodData.fromJSON(json) ⇒ [MethodData](#MethodData) -Deserializes an instance from a JSON object. - -**Kind**: static method of [MethodData](#MethodData) - -| Param | Type | -| --- | --- | -| json | any | - - - -## MethodScope -Supported verification method types. - -**Kind**: global class - -* [MethodScope](#MethodScope) - * _instance_ - * [.toString()](#MethodScope+toString) ⇒ string - * [.toJSON()](#MethodScope+toJSON) ⇒ any - * [.clone()](#MethodScope+clone) ⇒ [MethodScope](#MethodScope) - * _static_ - * [.VerificationMethod()](#MethodScope.VerificationMethod) ⇒ [MethodScope](#MethodScope) - * [.Authentication()](#MethodScope.Authentication) ⇒ [MethodScope](#MethodScope) - * [.AssertionMethod()](#MethodScope.AssertionMethod) ⇒ [MethodScope](#MethodScope) - * [.KeyAgreement()](#MethodScope.KeyAgreement) ⇒ [MethodScope](#MethodScope) - * [.CapabilityDelegation()](#MethodScope.CapabilityDelegation) ⇒ [MethodScope](#MethodScope) - * [.CapabilityInvocation()](#MethodScope.CapabilityInvocation) ⇒ [MethodScope](#MethodScope) - * [.fromJSON(json)](#MethodScope.fromJSON) ⇒ [MethodScope](#MethodScope) - - - -### methodScope.toString() ⇒ string -Returns the `MethodScope` as a string. - -**Kind**: instance method of [MethodScope](#MethodScope) - - -### methodScope.toJSON() ⇒ any -Serializes this to a JSON object. - -**Kind**: instance method of [MethodScope](#MethodScope) - - -### methodScope.clone() ⇒ [MethodScope](#MethodScope) -Deep clones the object. - -**Kind**: instance method of [MethodScope](#MethodScope) - - -### MethodScope.VerificationMethod() ⇒ [MethodScope](#MethodScope) -**Kind**: static method of [MethodScope](#MethodScope) - - -### MethodScope.Authentication() ⇒ [MethodScope](#MethodScope) -**Kind**: static method of [MethodScope](#MethodScope) - - -### MethodScope.AssertionMethod() ⇒ [MethodScope](#MethodScope) -**Kind**: static method of [MethodScope](#MethodScope) - - -### MethodScope.KeyAgreement() ⇒ [MethodScope](#MethodScope) -**Kind**: static method of [MethodScope](#MethodScope) - - -### MethodScope.CapabilityDelegation() ⇒ [MethodScope](#MethodScope) -**Kind**: static method of [MethodScope](#MethodScope) - - -### MethodScope.CapabilityInvocation() ⇒ [MethodScope](#MethodScope) -**Kind**: static method of [MethodScope](#MethodScope) - - -### MethodScope.fromJSON(json) ⇒ [MethodScope](#MethodScope) -Deserializes an instance from a JSON object. - -**Kind**: static method of [MethodScope](#MethodScope) - -| Param | Type | -| --- | --- | -| json | any | - - - -## MethodType -Supported verification method types. - -**Kind**: global class - -* [MethodType](#MethodType) - * _instance_ - * [.toString()](#MethodType+toString) ⇒ string - * [.toJSON()](#MethodType+toJSON) ⇒ any - * [.clone()](#MethodType+clone) ⇒ [MethodType](#MethodType) - * _static_ - * [.Ed25519VerificationKey2018()](#MethodType.Ed25519VerificationKey2018) ⇒ [MethodType](#MethodType) - * [.X25519KeyAgreementKey2019()](#MethodType.X25519KeyAgreementKey2019) ⇒ [MethodType](#MethodType) - * [.fromJSON(json)](#MethodType.fromJSON) ⇒ [MethodType](#MethodType) - - - -### methodType.toString() ⇒ string -Returns the `MethodType` as a string. - -**Kind**: instance method of [MethodType](#MethodType) - - -### methodType.toJSON() ⇒ any -Serializes this to a JSON object. - -**Kind**: instance method of [MethodType](#MethodType) - - -### methodType.clone() ⇒ [MethodType](#MethodType) -Deep clones the object. - -**Kind**: instance method of [MethodType](#MethodType) - - -### MethodType.Ed25519VerificationKey2018() ⇒ [MethodType](#MethodType) -**Kind**: static method of [MethodType](#MethodType) - - -### MethodType.X25519KeyAgreementKey2019() ⇒ [MethodType](#MethodType) -**Kind**: static method of [MethodType](#MethodType) - +**Kind**: instance method of [Credential](#Credential) + -### MethodType.fromJSON(json) ⇒ [MethodType](#MethodType) -Deserializes an instance from a JSON object. +### credential.evidence() ⇒ Array.<Evidence> +Returns a copy of the human-readable evidence used to support the claims within the `Credential`. -**Kind**: static method of [MethodType](#MethodType) +**Kind**: instance method of [Credential](#Credential) + -| Param | Type | -| --- | --- | -| json | any | +### credential.nonTransferable() ⇒ boolean \| undefined +Returns whether or not the `Credential` must only be contained within a [Presentation](#Presentation) +with a proof issued from the `Credential` subject. - +**Kind**: instance method of [Credential](#Credential) + -## MixedResolver -Convenience type for resolving DID documents from different DID methods. +### credential.proof() ⇒ [Proof](#Proof) \| undefined +Returns a copy of the proof used to verify the `Credential`. -Also provides methods for resolving DID Documents associated with -verifiable `Credentials` and `Presentations`. +**Kind**: instance method of [Credential](#Credential) + -# Configuration -The resolver will only be able to resolve DID documents for methods it has been configured for in the constructor. +### credential.properties() ⇒ Map.<string, any> +Returns a copy of the miscellaneous properties on the `Credential`. -**Kind**: global class +**Kind**: instance method of [Credential](#Credential) + -* [MixedResolver](#MixedResolver) - * [new MixedResolver(config)](#new_MixedResolver_new) - * [.resolvePresentationIssuers(presentation)](#MixedResolver+resolvePresentationIssuers) ⇒ Promise.<Array.<(StardustDocument\|CoreDocument)>> - * [.resolvePresentationHolder(presentation)](#MixedResolver+resolvePresentationHolder) ⇒ Promise.<(StardustDocument\|CoreDocument)> - * [.verifyPresentation(presentation, options, fail_fast, holder, issuers)](#MixedResolver+verifyPresentation) ⇒ Promise.<void> - * [.resolve(did)](#MixedResolver+resolve) ⇒ Promise.<(StardustDocument\|CoreDocument)> +### credential.toJSON() ⇒ any +Serializes this to a JSON object. - +**Kind**: instance method of [Credential](#Credential) + -### new MixedResolver(config) -Constructs a new `MixedResolver`. +### credential.clone() ⇒ [Credential](#Credential) +Deep clones the object. -# Errors -If both a `client` is given and the `handlers` map contains the "iota" key the construction process -will throw an error as it is then ambiguous what should be . +**Kind**: instance method of [Credential](#Credential) + +### Credential.BaseContext() ⇒ string +Returns the base JSON-LD context. -| Param | Type | -| --- | --- | -| config | ResolverConfig | +**Kind**: static method of [Credential](#Credential) + - +### Credential.BaseType() ⇒ string +Returns the base type. -### mixedResolver.resolvePresentationIssuers(presentation) ⇒ Promise.<Array.<(StardustDocument\|CoreDocument)>> -Fetches all DID Documents of `Credential` issuers contained in a `Presentation`. -Issuer documents are returned in arbitrary order. +**Kind**: static method of [Credential](#Credential) + -# Errors -Errors if any issuer URL cannot be parsed to a DID whose associated method is supported by this Resolver, or -resolution fails. +### Credential.fromJSON(json) ⇒ [Credential](#Credential) +Deserializes an instance from a JSON object. -**Kind**: instance method of [MixedResolver](#MixedResolver) +**Kind**: static method of [Credential](#Credential) | Param | Type | | --- | --- | -| presentation | [Presentation](#Presentation) | +| json | any | - + -### mixedResolver.resolvePresentationHolder(presentation) ⇒ Promise.<(StardustDocument\|CoreDocument)> -Fetches the DID Document of the holder of a `Presentation`. +## CredentialValidationOptions +Options to declare validation criteria when validating credentials. -# Errors -Errors if the holder URL is missing, cannot be parsed to a valid DID whose method is supported by the resolver, or -DID resolution fails. +**Kind**: global class -**Kind**: instance method of [MixedResolver](#MixedResolver) +* [CredentialValidationOptions](#CredentialValidationOptions) + * [new CredentialValidationOptions(options)](#new_CredentialValidationOptions_new) + * _instance_ + * [.toJSON()](#CredentialValidationOptions+toJSON) ⇒ any + * [.clone()](#CredentialValidationOptions+clone) ⇒ [CredentialValidationOptions](#CredentialValidationOptions) + * _static_ + * [.default()](#CredentialValidationOptions.default) ⇒ [CredentialValidationOptions](#CredentialValidationOptions) + * [.fromJSON(json)](#CredentialValidationOptions.fromJSON) ⇒ [CredentialValidationOptions](#CredentialValidationOptions) -| Param | Type | -| --- | --- | -| presentation | [Presentation](#Presentation) | + - +### new CredentialValidationOptions(options) +Creates a new `CredentialValidationOptions` from the given fields. -### mixedResolver.verifyPresentation(presentation, options, fail_fast, holder, issuers) ⇒ Promise.<void> -Verifies a `Presentation`. +Throws an error if any of the options are invalid. -### Important -See `PresentationValidator::validate` for information about which properties get -validated and what is expected of the optional arguments `holder` and `issuer`. -### Resolution -The DID Documents for the `holder` and `issuers` are optionally resolved if not given. -If you already have up-to-date versions of these DID Documents, you may want -to use `PresentationValidator::validate`. -See also `Resolver::resolvePresentationIssuers` and `Resolver::resolvePresentationHolder`. +| Param | Type | +| --- | --- | +| options | ICredentialValidationOptions | -### Errors -Errors from resolving the holder and issuer DID Documents, if not provided, will be returned immediately. -Otherwise, errors from validating the presentation and its credentials will be returned -according to the `fail_fast` parameter. + -**Kind**: instance method of [MixedResolver](#MixedResolver) +### credentialValidationOptions.toJSON() ⇒ any +Serializes this to a JSON object. -| Param | Type | -| --- | --- | -| presentation | [Presentation](#Presentation) | -| options | [PresentationValidationOptions](#PresentationValidationOptions) | -| fail_fast | number | -| holder | [StardustDocument](#StardustDocument) \| [CoreDocument](#CoreDocument) \| undefined | -| issuers | Array.<(StardustDocument\|CoreDocument)> \| undefined | +**Kind**: instance method of [CredentialValidationOptions](#CredentialValidationOptions) + - +### credentialValidationOptions.clone() ⇒ [CredentialValidationOptions](#CredentialValidationOptions) +Deep clones the object. -### mixedResolver.resolve(did) ⇒ Promise.<(StardustDocument\|CoreDocument)> -Fetches the DID Document of the given DID. +**Kind**: instance method of [CredentialValidationOptions](#CredentialValidationOptions) + -### Errors +### CredentialValidationOptions.default() ⇒ [CredentialValidationOptions](#CredentialValidationOptions) +Creates a new `CredentialValidationOptions` with defaults. -Errors if the resolver has not been configured to handle the method -corresponding to the given DID or the resolution process itself fails. +**Kind**: static method of [CredentialValidationOptions](#CredentialValidationOptions) + -**Kind**: instance method of [MixedResolver](#MixedResolver) +### CredentialValidationOptions.fromJSON(json) ⇒ [CredentialValidationOptions](#CredentialValidationOptions) +Deserializes an instance from a JSON object. + +**Kind**: static method of [CredentialValidationOptions](#CredentialValidationOptions) | Param | Type | | --- | --- | -| did | string | +| json | any | - + -## Network +## CredentialValidator **Kind**: global class -* [Network](#Network) - * _instance_ - * [.name()](#Network+name) ⇒ string - * [.defaultNodeURL()](#Network+defaultNodeURL) ⇒ string \| undefined - * [.toString()](#Network+toString) ⇒ string - * [.toJSON()](#Network+toJSON) ⇒ any - * [.clone()](#Network+clone) ⇒ [Network](#Network) - * _static_ - * [.tryFromName(name)](#Network.tryFromName) ⇒ [Network](#Network) - * [.mainnet()](#Network.mainnet) ⇒ [Network](#Network) - * [.devnet()](#Network.devnet) ⇒ [Network](#Network) - * [.fromJSON(json)](#Network.fromJSON) ⇒ [Network](#Network) +* [CredentialValidator](#CredentialValidator) + * [.validate(credential, issuer, options, fail_fast)](#CredentialValidator.validate) + * [.checkStructure(credential)](#CredentialValidator.checkStructure) + * [.checkExpiresOnOrAfter(credential, timestamp)](#CredentialValidator.checkExpiresOnOrAfter) + * [.checkIssuedOnOrBefore(credential, timestamp)](#CredentialValidator.checkIssuedOnOrBefore) + * [.verifySignature(credential, trustedIssuers, options)](#CredentialValidator.verifySignature) + * [.checkSubjectHolderRelationship(credential, holder, relationship)](#CredentialValidator.checkSubjectHolderRelationship) + * [.checkStatus(credential, trustedIssuers, statusCheck)](#CredentialValidator.checkStatus) + * [.extractIssuer(credential)](#CredentialValidator.extractIssuer) ⇒ [CoreDID](#CoreDID) \| [IotaDID](#IotaDID) - + -### network.name() ⇒ string -Returns a copy of the network name. +### CredentialValidator.validate(credential, issuer, options, fail_fast) +Validates a `Credential`. -**Kind**: instance method of [Network](#Network) - +The following properties are validated according to `options`: +- the issuer's signature, +- the expiration date, +- the issuance date, +- the semantic structure. -### network.defaultNodeURL() ⇒ string \| undefined -Returns a copy of the node URL of the Tangle network. +### Warning +The lack of an error returned from this method is in of itself not enough to conclude that the credential can be +trusted. This section contains more information on additional checks that should be carried out before and after +calling this method. -**Kind**: instance method of [Network](#Network) - +#### The state of the issuer's DID Document +The caller must ensure that `issuer` represents an up-to-date DID Document. The convenience method +`Resolver::resolveCredentialIssuer` can help extract the latest available state of the issuer's DID Document. -### network.toString() ⇒ string -**Kind**: instance method of [Network](#Network) - +#### Properties that are not validated + There are many properties defined in [The Verifiable Credentials Data Model](https://www.w3.org/TR/vc-data-model/) that are **not** validated, such as: +`credentialStatus`, `type`, `credentialSchema`, `refreshService`, **and more**. +These should be manually checked after validation, according to your requirements. -### network.toJSON() ⇒ any -Serializes this to a JSON object. +### Errors +An error is returned whenever a validated condition is not satisfied. -**Kind**: instance method of [Network](#Network) - +**Kind**: static method of [CredentialValidator](#CredentialValidator) -### network.clone() ⇒ [Network](#Network) -Deep clones the object. +| Param | Type | +| --- | --- | +| credential | [Credential](#Credential) | +| issuer | [IotaDocument](#IotaDocument) \| [CoreDocument](#CoreDocument) | +| options | [CredentialValidationOptions](#CredentialValidationOptions) | +| fail_fast | number | -**Kind**: instance method of [Network](#Network) - + -### Network.tryFromName(name) ⇒ [Network](#Network) -Parses the provided string to a `Network`. +### CredentialValidator.checkStructure(credential) +Validates the semantic structure of the `Credential`. -Errors if the name is invalid. +### Warning +This does not validate against the credential's schema nor the structure of the subject claims. -**Kind**: static method of [Network](#Network) +**Kind**: static method of [CredentialValidator](#CredentialValidator) | Param | Type | | --- | --- | -| name | string | - - - -### Network.mainnet() ⇒ [Network](#Network) -**Kind**: static method of [Network](#Network) - +| credential | [Credential](#Credential) | -### Network.devnet() ⇒ [Network](#Network) -**Kind**: static method of [Network](#Network) - + -### Network.fromJSON(json) ⇒ [Network](#Network) -Deserializes an instance from a JSON object. +### CredentialValidator.checkExpiresOnOrAfter(credential, timestamp) +Validate that the credential expires on or after the specified timestamp. -**Kind**: static method of [Network](#Network) +**Kind**: static method of [CredentialValidator](#CredentialValidator) | Param | Type | | --- | --- | -| json | any | - - - -## Presentation -**Kind**: global class - -* [Presentation](#Presentation) - * [new Presentation(values)](#new_Presentation_new) - * _instance_ - * [.context()](#Presentation+context) ⇒ Array.<(string\|Record.<string, any>)> - * [.id()](#Presentation+id) ⇒ string \| undefined - * [.type()](#Presentation+type) ⇒ Array.<string> - * [.verifiableCredential()](#Presentation+verifiableCredential) ⇒ [Array.<Credential>](#Credential) - * [.holder()](#Presentation+holder) ⇒ string \| undefined - * [.refreshService()](#Presentation+refreshService) ⇒ Array.<RefreshService> - * [.termsOfUse()](#Presentation+termsOfUse) ⇒ Array.<Policy> - * [.proof()](#Presentation+proof) ⇒ [Proof](#Proof) \| undefined - * [.properties()](#Presentation+properties) ⇒ Map.<string, any> - * [.toJSON()](#Presentation+toJSON) ⇒ any - * [.clone()](#Presentation+clone) ⇒ [Presentation](#Presentation) - * _static_ - * [.BaseContext()](#Presentation.BaseContext) ⇒ string - * [.BaseType()](#Presentation.BaseType) ⇒ string - * [.fromJSON(json)](#Presentation.fromJSON) ⇒ [Presentation](#Presentation) +| credential | [Credential](#Credential) | +| timestamp | [Timestamp](#Timestamp) | - + -### new Presentation(values) -Constructs a new `Presentation`. +### CredentialValidator.checkIssuedOnOrBefore(credential, timestamp) +Validate that the credential is issued on or before the specified timestamp. +**Kind**: static method of [CredentialValidator](#CredentialValidator) | Param | Type | | --- | --- | -| values | IPresentation | - - +| credential | [Credential](#Credential) | +| timestamp | [Timestamp](#Timestamp) | -### presentation.context() ⇒ Array.<(string\|Record.<string, any>)> -Returns a copy of the JSON-LD context(s) applicable to the `Presentation`. + -**Kind**: instance method of [Presentation](#Presentation) - +### CredentialValidator.verifySignature(credential, trustedIssuers, options) +Verify the signature using the DID Document of a trusted issuer. -### presentation.id() ⇒ string \| undefined -Returns a copy of the unique `URI` identifying the `Presentation`. +# Warning +The caller must ensure that the DID Documents of the trusted issuers are up-to-date. +### Errors +This method immediately returns an error if +the credential issuer' url cannot be parsed to a DID belonging to one of the trusted issuers. Otherwise an attempt +to verify the credential's signature will be made and an error is returned upon failure. -**Kind**: instance method of [Presentation](#Presentation) - +**Kind**: static method of [CredentialValidator](#CredentialValidator) -### presentation.type() ⇒ Array.<string> -Returns a copy of the URIs defining the type of the `Presentation`. +| Param | Type | +| --- | --- | +| credential | [Credential](#Credential) | +| trustedIssuers | Array.<(IotaDocument\|CoreDocument)> | +| options | [VerifierOptions](#VerifierOptions) | -**Kind**: instance method of [Presentation](#Presentation) - + -### presentation.verifiableCredential() ⇒ [Array.<Credential>](#Credential) -Returns a copy of the [Credential](#Credential)(s) expressing the claims of the `Presentation`. +### CredentialValidator.checkSubjectHolderRelationship(credential, holder, relationship) +Validate that the relationship between the `holder` and the credential subjects is in accordance with +`relationship`. The `holder` parameter is expected to be the URL of the holder. -**Kind**: instance method of [Presentation](#Presentation) - +**Kind**: static method of [CredentialValidator](#CredentialValidator) -### presentation.holder() ⇒ string \| undefined -Returns a copy of the URI of the entity that generated the `Presentation`. +| Param | Type | +| --- | --- | +| credential | [Credential](#Credential) | +| holder | string | +| relationship | number | -**Kind**: instance method of [Presentation](#Presentation) - + -### presentation.refreshService() ⇒ Array.<RefreshService> -Returns a copy of the service(s) used to refresh an expired [Credential](#Credential) in the `Presentation`. +### CredentialValidator.checkStatus(credential, trustedIssuers, statusCheck) +Checks whether the credential status has been revoked. -**Kind**: instance method of [Presentation](#Presentation) - +Only supports `BitmapRevocation2022`. -### presentation.termsOfUse() ⇒ Array.<Policy> -Returns a copy of the terms-of-use specified by the `Presentation` holder +**Kind**: static method of [CredentialValidator](#CredentialValidator) -**Kind**: instance method of [Presentation](#Presentation) - +| Param | Type | +| --- | --- | +| credential | [Credential](#Credential) | +| trustedIssuers | Array.<(IotaDocument\|CoreDocument)> | +| statusCheck | number | -### presentation.proof() ⇒ [Proof](#Proof) \| undefined -Returns a copy of the proof used to verify the `Presentation`. + -**Kind**: instance method of [Presentation](#Presentation) - +### CredentialValidator.extractIssuer(credential) ⇒ [CoreDID](#CoreDID) \| [IotaDID](#IotaDID) +Utility for extracting the issuer field of a `Credential` as a DID. -### presentation.properties() ⇒ Map.<string, any> -Returns a copy of the miscellaneous properties on the `Presentation`. +### Errors -**Kind**: instance method of [Presentation](#Presentation) - +Fails if the issuer field is not a valid DID. -### presentation.toJSON() ⇒ any -Serializes this to a JSON object. +**Kind**: static method of [CredentialValidator](#CredentialValidator) -**Kind**: instance method of [Presentation](#Presentation) - +| Param | Type | +| --- | --- | +| credential | [Credential](#Credential) | -### presentation.clone() ⇒ [Presentation](#Presentation) -Deep clones the object. + -**Kind**: instance method of [Presentation](#Presentation) - +## Duration +A span of time. -### Presentation.BaseContext() ⇒ string -Returns the base JSON-LD context. +**Kind**: global class -**Kind**: static method of [Presentation](#Presentation) - +* [Duration](#Duration) + * _instance_ + * [.toJSON()](#Duration+toJSON) ⇒ any + * _static_ + * [.seconds(seconds)](#Duration.seconds) ⇒ [Duration](#Duration) + * [.minutes(minutes)](#Duration.minutes) ⇒ [Duration](#Duration) + * [.hours(hours)](#Duration.hours) ⇒ [Duration](#Duration) + * [.days(days)](#Duration.days) ⇒ [Duration](#Duration) + * [.weeks(weeks)](#Duration.weeks) ⇒ [Duration](#Duration) + * [.fromJSON(json)](#Duration.fromJSON) ⇒ [Duration](#Duration) -### Presentation.BaseType() ⇒ string -Returns the base type. + -**Kind**: static method of [Presentation](#Presentation) - +### duration.toJSON() ⇒ any +Serializes this to a JSON object. -### Presentation.fromJSON(json) ⇒ [Presentation](#Presentation) -Deserializes an instance from a JSON object. +**Kind**: instance method of [Duration](#Duration) + -**Kind**: static method of [Presentation](#Presentation) +### Duration.seconds(seconds) ⇒ [Duration](#Duration) +Create a new `Duration` with the given number of seconds. + +**Kind**: static method of [Duration](#Duration) | Param | Type | | --- | --- | -| json | any | - - +| seconds | number | -## PresentationValidationOptions -Options to declare validation criteria when validating presentation. + -**Kind**: global class +### Duration.minutes(minutes) ⇒ [Duration](#Duration) +Create a new `Duration` with the given number of minutes. -* [PresentationValidationOptions](#PresentationValidationOptions) - * [new PresentationValidationOptions(options)](#new_PresentationValidationOptions_new) - * _instance_ - * [.toJSON()](#PresentationValidationOptions+toJSON) ⇒ any - * [.clone()](#PresentationValidationOptions+clone) ⇒ [PresentationValidationOptions](#PresentationValidationOptions) - * _static_ - * [.default()](#PresentationValidationOptions.default) ⇒ [PresentationValidationOptions](#PresentationValidationOptions) - * [.fromJSON(json)](#PresentationValidationOptions.fromJSON) ⇒ [PresentationValidationOptions](#PresentationValidationOptions) +**Kind**: static method of [Duration](#Duration) - +| Param | Type | +| --- | --- | +| minutes | number | -### new PresentationValidationOptions(options) -Creates a new `PresentationValidationOptions` from the given fields. + -Throws an error if any of the options are invalid. +### Duration.hours(hours) ⇒ [Duration](#Duration) +Create a new `Duration` with the given number of hours. +**Kind**: static method of [Duration](#Duration) | Param | Type | | --- | --- | -| options | IPresentationValidationOptions | +| hours | number | - + -### presentationValidationOptions.toJSON() ⇒ any -Serializes this to a JSON object. +### Duration.days(days) ⇒ [Duration](#Duration) +Create a new `Duration` with the given number of days. -**Kind**: instance method of [PresentationValidationOptions](#PresentationValidationOptions) - +**Kind**: static method of [Duration](#Duration) -### presentationValidationOptions.clone() ⇒ [PresentationValidationOptions](#PresentationValidationOptions) -Deep clones the object. +| Param | Type | +| --- | --- | +| days | number | -**Kind**: instance method of [PresentationValidationOptions](#PresentationValidationOptions) - + -### PresentationValidationOptions.default() ⇒ [PresentationValidationOptions](#PresentationValidationOptions) -Creates a new `PresentationValidationOptions` with defaults. +### Duration.weeks(weeks) ⇒ [Duration](#Duration) +Create a new `Duration` with the given number of weeks. -**Kind**: static method of [PresentationValidationOptions](#PresentationValidationOptions) - +**Kind**: static method of [Duration](#Duration) -### PresentationValidationOptions.fromJSON(json) ⇒ [PresentationValidationOptions](#PresentationValidationOptions) +| Param | Type | +| --- | --- | +| weeks | number | + + + +### Duration.fromJSON(json) ⇒ [Duration](#Duration) Deserializes an instance from a JSON object. -**Kind**: static method of [PresentationValidationOptions](#PresentationValidationOptions) +**Kind**: static method of [Duration](#Duration) | Param | Type | | --- | --- | | json | any | - + -## PresentationValidator +## Ed25519 **Kind**: global class -* [PresentationValidator](#PresentationValidator) - * [.validate(presentation, holder, issuers, options, fail_fast)](#PresentationValidator.validate) - * [.verifyPresentationSignature(presentation, holder, options)](#PresentationValidator.verifyPresentationSignature) - * [.checkStructure(presentation)](#PresentationValidator.checkStructure) - * [.extractHolder(presentation)](#PresentationValidator.extractHolder) ⇒ [CoreDID](#CoreDID) \| [StardustDID](#StardustDID) +* [Ed25519](#Ed25519) + * [.PRIVATE_KEY_LENGTH()](#Ed25519.PRIVATE_KEY_LENGTH) ⇒ number + * [.PUBLIC_KEY_LENGTH()](#Ed25519.PUBLIC_KEY_LENGTH) ⇒ number + * [.SIGNATURE_LENGTH()](#Ed25519.SIGNATURE_LENGTH) ⇒ number + * [.sign(message, privateKey)](#Ed25519.sign) ⇒ Uint8Array + * [.verify(message, signature, publicKey)](#Ed25519.verify) - + -### PresentationValidator.validate(presentation, holder, issuers, options, fail_fast) -Validate a `Presentation`. +### Ed25519.PRIVATE\_KEY\_LENGTH() ⇒ number +Length in bytes of an Ed25519 private key. -The following properties are validated according to `options`: -- the semantic structure of the presentation, -- the holder's signature, -- the relationship between the holder and the credential subjects, -- the signatures and some properties of the constituent credentials (see -`CredentialValidator::validate`). +**Kind**: static method of [Ed25519](#Ed25519) + -### Warning -The lack of an error returned from this method is in of itself not enough to conclude that the presentation can be -trusted. This section contains more information on additional checks that should be carried out before and after -calling this method. +### Ed25519.PUBLIC\_KEY\_LENGTH() ⇒ number +Length in bytes of an Ed25519 public key. -#### The state of the supplied DID Documents. -The caller must ensure that the DID Documents in `holder` and `issuers` are up-to-date. The convenience methods -`Resolver::resolve_presentation_holder` and `Resolver::resolve_presentation_issuers` -can help extract the latest available states of these DID Documents. +**Kind**: static method of [Ed25519](#Ed25519) + -#### Properties that are not validated - There are many properties defined in [The Verifiable Credentials Data Model](https://www.w3.org/TR/vc-data-model/) that are **not** validated, such as: -`credentialStatus`, `type`, `credentialSchema`, `refreshService`, **and more**. -These should be manually checked after validation, according to your requirements. +### Ed25519.SIGNATURE\_LENGTH() ⇒ number +Length in bytes of an Ed25519 signature. -### Errors -An error is returned whenever a validated condition is not satisfied. +**Kind**: static method of [Ed25519](#Ed25519) + -**Kind**: static method of [PresentationValidator](#PresentationValidator) +### Ed25519.sign(message, privateKey) ⇒ Uint8Array +Computes an EdDSA signature using an Ed25519 private key. + +NOTE: this differs from [Document.signData](#Document+signData) which uses JCS +to canonicalize JSON messages. + +The private key must be a 32-byte seed in compliance with [RFC 8032](https://datatracker.ietf.org/doc/html/rfc8032#section-3.2). +Other implementations often use another format. See [this blog post](https://blog.mozilla.org/warner/2011/11/29/ed25519-keys/) for further explanation. + +**Kind**: static method of [Ed25519](#Ed25519) | Param | Type | | --- | --- | -| presentation | [Presentation](#Presentation) | -| holder | [StardustDocument](#StardustDocument) \| [CoreDocument](#CoreDocument) | -| issuers | Array.<(StardustDocument\|CoreDocument)> | -| options | [PresentationValidationOptions](#PresentationValidationOptions) | -| fail_fast | number | - - +| message | Uint8Array | +| privateKey | Uint8Array | -### PresentationValidator.verifyPresentationSignature(presentation, holder, options) -Verify the presentation's signature using the resolved document of the holder. + -### Warning -The caller must ensure that the DID Document of the holder is up-to-date. +### Ed25519.verify(message, signature, publicKey) +Verifies an EdDSA signature against an Ed25519 public key. -### Errors -Fails if the `holder` does not match the `presentation`'s holder property. -Fails if signature verification against the holder document fails. +NOTE: this differs from [Document.verifyData](#Document+verifyData) which uses JCS +to canonicalize JSON messages. -**Kind**: static method of [PresentationValidator](#PresentationValidator) +**Kind**: static method of [Ed25519](#Ed25519) | Param | Type | | --- | --- | -| presentation | [Presentation](#Presentation) | -| holder | [StardustDocument](#StardustDocument) \| [CoreDocument](#CoreDocument) | -| options | [VerifierOptions](#VerifierOptions) | - - +| message | Uint8Array | +| signature | Uint8Array | +| publicKey | Uint8Array | -### PresentationValidator.checkStructure(presentation) -Validates the semantic structure of the `Presentation`. + -**Kind**: static method of [PresentationValidator](#PresentationValidator) +## IotaDID +A DID conforming to the IOTA DID method specification. -| Param | Type | -| --- | --- | -| presentation | [Presentation](#Presentation) | +**Kind**: global class - +* [IotaDID](#IotaDID) + * [new IotaDID(bytes, network)](#new_IotaDID_new) + * _instance_ + * [.networkStr()](#IotaDID+networkStr) ⇒ string + * [.tag()](#IotaDID+tag) ⇒ string + * [.scheme()](#IotaDID+scheme) ⇒ string + * [.authority()](#IotaDID+authority) ⇒ string + * [.method()](#IotaDID+method) ⇒ string + * [.methodId()](#IotaDID+methodId) ⇒ string + * [.join(segment)](#IotaDID+join) ⇒ [IotaDIDUrl](#IotaDIDUrl) + * [.toUrl()](#IotaDID+toUrl) ⇒ [IotaDIDUrl](#IotaDIDUrl) + * [.intoUrl()](#IotaDID+intoUrl) ⇒ [IotaDIDUrl](#IotaDIDUrl) + * [.toString()](#IotaDID+toString) ⇒ string + * [.toJSON()](#IotaDID+toJSON) ⇒ any + * [.clone()](#IotaDID+clone) ⇒ [IotaDID](#IotaDID) + * _static_ + * [.METHOD](#IotaDID.METHOD) ⇒ string + * [.DEFAULT_NETWORK](#IotaDID.DEFAULT_NETWORK) ⇒ string + * [.placeholder(network)](#IotaDID.placeholder) ⇒ [IotaDID](#IotaDID) + * [.parse(input)](#IotaDID.parse) ⇒ [IotaDID](#IotaDID) + * [.fromJSON(json)](#IotaDID.fromJSON) ⇒ [IotaDID](#IotaDID) -### PresentationValidator.extractHolder(presentation) ⇒ [CoreDID](#CoreDID) \| [StardustDID](#StardustDID) -Utility for extracting the holder field of a `Presentation` as a DID. + -### Errors +### new IotaDID(bytes, network) +Constructs a new `IotaDID` from a byte representation of the tag and the given +network name. -Fails if the holder field is missing or not a valid DID. +See also [placeholder](#IotaDID.placeholder). -**Kind**: static method of [PresentationValidator](#PresentationValidator) | Param | Type | | --- | --- | -| presentation | [Presentation](#Presentation) | +| bytes | Uint8Array | +| network | string | - + -## Proof -A digital signature. +### did.networkStr() ⇒ string +Returns the Tangle network name of the `IotaDID`. -For field definitions see: https://w3c-ccg.github.io/security-vocab/ +**Kind**: instance method of [IotaDID](#IotaDID) + -**Kind**: global class +### did.tag() ⇒ string +Returns a copy of the unique tag of the `IotaDID`. -* [Proof](#Proof) - * _instance_ - * [.type()](#Proof+type) ⇒ string - * [.value()](#Proof+value) ⇒ string - * [.verificationMethod()](#Proof+verificationMethod) ⇒ string - * [.created()](#Proof+created) ⇒ [Timestamp](#Timestamp) \| undefined - * [.expires()](#Proof+expires) ⇒ [Timestamp](#Timestamp) \| undefined - * [.challenge()](#Proof+challenge) ⇒ string \| undefined - * [.domain()](#Proof+domain) ⇒ string \| undefined - * [.purpose()](#Proof+purpose) ⇒ [ProofPurpose](#ProofPurpose) \| undefined - * [.toJSON()](#Proof+toJSON) ⇒ any - * [.clone()](#Proof+clone) ⇒ [Proof](#Proof) - * _static_ - * [.fromJSON(json)](#Proof.fromJSON) ⇒ [Proof](#Proof) +**Kind**: instance method of [IotaDID](#IotaDID) + - +### did.scheme() ⇒ string +Returns the `DID` scheme. -### proof.type() ⇒ string -Returns a copy of the proof type. +E.g. +- `"did:example:12345678" -> "did"` +- `"did:iota:main:12345678" -> "did"` -**Kind**: instance method of [Proof](#Proof) - +**Kind**: instance method of [IotaDID](#IotaDID) + -### proof.value() ⇒ string -Returns a copy of the proof value string. +### did.authority() ⇒ string +Returns the `DID` authority: the method name and method-id. -**Kind**: instance method of [Proof](#Proof) - +E.g. +- `"did:example:12345678" -> "example:12345678"` +- `"did:iota:main:12345678" -> "iota:main:12345678"` -### proof.verificationMethod() ⇒ string -Returns a copy of the identifier of the DID method used to create this proof. +**Kind**: instance method of [IotaDID](#IotaDID) + -**Kind**: instance method of [Proof](#Proof) - +### did.method() ⇒ string +Returns the `DID` method name. -### proof.created() ⇒ [Timestamp](#Timestamp) \| undefined -When the proof was generated. +E.g. +- `"did:example:12345678" -> "example"` +- `"did:iota:main:12345678" -> "iota"` -**Kind**: instance method of [Proof](#Proof) - +**Kind**: instance method of [IotaDID](#IotaDID) + -### proof.expires() ⇒ [Timestamp](#Timestamp) \| undefined -When the proof expires. +### did.methodId() ⇒ string +Returns the `DID` method-specific ID. -**Kind**: instance method of [Proof](#Proof) - +E.g. +- `"did:example:12345678" -> "12345678"` +- `"did:iota:main:12345678" -> "main:12345678"` -### proof.challenge() ⇒ string \| undefined -Challenge from a proof requester to mitigate replay attacks. +**Kind**: instance method of [IotaDID](#IotaDID) + -**Kind**: instance method of [Proof](#Proof) - +### did.join(segment) ⇒ [IotaDIDUrl](#IotaDIDUrl) +Construct a new `DIDUrl` by joining with a relative DID Url string. -### proof.domain() ⇒ string \| undefined -Domain for which a proof is valid to mitigate replay attacks. +**Kind**: instance method of [IotaDID](#IotaDID) -**Kind**: instance method of [Proof](#Proof) - +| Param | Type | +| --- | --- | +| segment | string | -### proof.purpose() ⇒ [ProofPurpose](#ProofPurpose) \| undefined -Purpose for which the proof was generated. + -**Kind**: instance method of [Proof](#Proof) - +### did.toUrl() ⇒ [IotaDIDUrl](#IotaDIDUrl) +Clones the `DID` into a `DIDUrl`. -### proof.toJSON() ⇒ any -Serializes this to a JSON object. +**Kind**: instance method of [IotaDID](#IotaDID) + -**Kind**: instance method of [Proof](#Proof) - +### did.intoUrl() ⇒ [IotaDIDUrl](#IotaDIDUrl) +Converts the `DID` into a `DIDUrl`, consuming it. -### proof.clone() ⇒ [Proof](#Proof) -Deep clones the object. +**Kind**: instance method of [IotaDID](#IotaDID) + -**Kind**: instance method of [Proof](#Proof) - +### did.toString() ⇒ string +Returns the `DID` as a string. -### Proof.fromJSON(json) ⇒ [Proof](#Proof) -Deserializes an instance from a JSON object. +**Kind**: instance method of [IotaDID](#IotaDID) + -**Kind**: static method of [Proof](#Proof) +### did.toJSON() ⇒ any +Serializes this to a JSON object. -| Param | Type | -| --- | --- | -| json | any | +**Kind**: instance method of [IotaDID](#IotaDID) + - +### did.clone() ⇒ [IotaDID](#IotaDID) +Deep clones the object. -## ProofOptions -Holds additional options for creating signatures. -See `IProofOptions`. +**Kind**: instance method of [IotaDID](#IotaDID) + -**Kind**: global class +### IotaDID.METHOD ⇒ string +The IOTA DID method name (`"iota"`). -* [ProofOptions](#ProofOptions) - * [new ProofOptions(options)](#new_ProofOptions_new) - * _instance_ - * [.toJSON()](#ProofOptions+toJSON) ⇒ any - * [.clone()](#ProofOptions+clone) ⇒ [ProofOptions](#ProofOptions) - * _static_ - * [.default()](#ProofOptions.default) ⇒ [ProofOptions](#ProofOptions) - * [.fromJSON(json)](#ProofOptions.fromJSON) ⇒ [ProofOptions](#ProofOptions) +**Kind**: static property of [IotaDID](#IotaDID) + - +### IotaDID.DEFAULT\_NETWORK ⇒ string +The default Tangle network (`"iota"`). -### new ProofOptions(options) -Creates a new `ProofOptions` from the given fields. +**Kind**: static property of [IotaDID](#IotaDID) + -Throws an error if any of the options are invalid. +### IotaDID.placeholder(network) ⇒ [IotaDID](#IotaDID) +Creates a new placeholder [`IotaDID`] with the given network name. + +E.g. `did:iota:smr:0x0000000000000000000000000000000000000000000000000000000000000000`. +**Kind**: static method of [IotaDID](#IotaDID) | Param | Type | | --- | --- | -| options | IProofOptions | - - - -### proofOptions.toJSON() ⇒ any -Serializes this to a JSON object. +| network | string | -**Kind**: instance method of [ProofOptions](#ProofOptions) - + -### proofOptions.clone() ⇒ [ProofOptions](#ProofOptions) -Deep clones the object. +### IotaDID.parse(input) ⇒ [IotaDID](#IotaDID) +Parses a `IotaDID` from the input string. -**Kind**: instance method of [ProofOptions](#ProofOptions) - +**Kind**: static method of [IotaDID](#IotaDID) -### ProofOptions.default() ⇒ [ProofOptions](#ProofOptions) -Creates a new `ProofOptions` with default options. +| Param | Type | +| --- | --- | +| input | string | -**Kind**: static method of [ProofOptions](#ProofOptions) - + -### ProofOptions.fromJSON(json) ⇒ [ProofOptions](#ProofOptions) +### IotaDID.fromJSON(json) ⇒ [IotaDID](#IotaDID) Deserializes an instance from a JSON object. -**Kind**: static method of [ProofOptions](#ProofOptions) +**Kind**: static method of [IotaDID](#IotaDID) | Param | Type | | --- | --- | | json | any | - - -## ProofPurpose -Associates a purpose with a [Proof](#Proof). + -See https://w3c-ccg.github.io/security-vocab/#proofPurpose +## IotaDIDUrl +A DID URL conforming to the IOTA DID method specification. **Kind**: global class -* [ProofPurpose](#ProofPurpose) +* [IotaDIDUrl](#IotaDIDUrl) * _instance_ - * [.toJSON()](#ProofPurpose+toJSON) ⇒ any - * [.clone()](#ProofPurpose+clone) ⇒ [ProofPurpose](#ProofPurpose) + * [.did()](#IotaDIDUrl+did) ⇒ [IotaDID](#IotaDID) + * [.urlStr()](#IotaDIDUrl+urlStr) ⇒ string + * [.fragment()](#IotaDIDUrl+fragment) ⇒ string \| undefined + * [.setFragment(value)](#IotaDIDUrl+setFragment) + * [.path()](#IotaDIDUrl+path) ⇒ string \| undefined + * [.setPath(value)](#IotaDIDUrl+setPath) + * [.query()](#IotaDIDUrl+query) ⇒ string \| undefined + * [.setQuery(value)](#IotaDIDUrl+setQuery) + * [.join(segment)](#IotaDIDUrl+join) ⇒ [IotaDIDUrl](#IotaDIDUrl) + * [.toString()](#IotaDIDUrl+toString) ⇒ string + * [.toJSON()](#IotaDIDUrl+toJSON) ⇒ any + * [.clone()](#IotaDIDUrl+clone) ⇒ [IotaDIDUrl](#IotaDIDUrl) * _static_ - * [.assertionMethod()](#ProofPurpose.assertionMethod) ⇒ [ProofPurpose](#ProofPurpose) - * [.authentication()](#ProofPurpose.authentication) ⇒ [ProofPurpose](#ProofPurpose) - * [.fromJSON(json)](#ProofPurpose.fromJSON) ⇒ [ProofPurpose](#ProofPurpose) + * [.parse(input)](#IotaDIDUrl.parse) ⇒ [IotaDIDUrl](#IotaDIDUrl) + * [.fromJSON(json)](#IotaDIDUrl.fromJSON) ⇒ [IotaDIDUrl](#IotaDIDUrl) - + -### proofPurpose.toJSON() ⇒ any -Serializes this to a JSON object. +### iotaDIDUrl.did() ⇒ [IotaDID](#IotaDID) +Return a copy of the `IotaDID` section of the `IotaDIDUrl`. -**Kind**: instance method of [ProofPurpose](#ProofPurpose) - +**Kind**: instance method of [IotaDIDUrl](#IotaDIDUrl) + -### proofPurpose.clone() ⇒ [ProofPurpose](#ProofPurpose) -Deep clones the object. +### iotaDIDUrl.urlStr() ⇒ string +Return a copy of the relative DID Url as a string, including only the path, query, and fragment. -**Kind**: instance method of [ProofPurpose](#ProofPurpose) - +**Kind**: instance method of [IotaDIDUrl](#IotaDIDUrl) + -### ProofPurpose.assertionMethod() ⇒ [ProofPurpose](#ProofPurpose) -Purpose is to assert a claim. -See https://www.w3.org/TR/did-core/#assertion +### iotaDIDUrl.fragment() ⇒ string \| undefined +Returns a copy of the `IotaDIDUrl` method fragment, if any. Excludes the leading '#'. -**Kind**: static method of [ProofPurpose](#ProofPurpose) - +**Kind**: instance method of [IotaDIDUrl](#IotaDIDUrl) + -### ProofPurpose.authentication() ⇒ [ProofPurpose](#ProofPurpose) -Purpose is to authenticate the signer. -See https://www.w3.org/TR/did-core/#authentication +### iotaDIDUrl.setFragment(value) +Sets the `fragment` component of the `IotaDIDUrl`. -**Kind**: static method of [ProofPurpose](#ProofPurpose) - +**Kind**: instance method of [IotaDIDUrl](#IotaDIDUrl) -### ProofPurpose.fromJSON(json) ⇒ [ProofPurpose](#ProofPurpose) -Deserializes an instance from a JSON object. +| Param | Type | +| --- | --- | +| value | string \| undefined | -**Kind**: static method of [ProofPurpose](#ProofPurpose) + + +### iotaDIDUrl.path() ⇒ string \| undefined +Returns a copy of the `IotaDIDUrl` path. + +**Kind**: instance method of [IotaDIDUrl](#IotaDIDUrl) + + +### iotaDIDUrl.setPath(value) +Sets the `path` component of the `IotaDIDUrl`. + +**Kind**: instance method of [IotaDIDUrl](#IotaDIDUrl) | Param | Type | | --- | --- | -| json | any | +| value | string \| undefined | - + -## Receipt -**Kind**: global class +### iotaDIDUrl.query() ⇒ string \| undefined +Returns a copy of the `IotaDIDUrl` method query, if any. Excludes the leading '?'. -* [Receipt](#Receipt) - * _instance_ - * [.network()](#Receipt+network) ⇒ [Network](#Network) - * [.messageId()](#Receipt+messageId) ⇒ string - * [.networkId()](#Receipt+networkId) ⇒ string - * [.nonce()](#Receipt+nonce) ⇒ string - * [.toJSON()](#Receipt+toJSON) ⇒ any - * [.clone()](#Receipt+clone) ⇒ [Receipt](#Receipt) - * _static_ - * [.fromJSON(json)](#Receipt.fromJSON) ⇒ [Receipt](#Receipt) +**Kind**: instance method of [IotaDIDUrl](#IotaDIDUrl) + + +### iotaDIDUrl.setQuery(value) +Sets the `query` component of the `IotaDIDUrl`. + +**Kind**: instance method of [IotaDIDUrl](#IotaDIDUrl) + +| Param | Type | +| --- | --- | +| value | string \| undefined | + + + +### iotaDIDUrl.join(segment) ⇒ [IotaDIDUrl](#IotaDIDUrl) +Append a string representing a path, query, and/or fragment, returning a new `IotaDIDUrl`. + +Must begin with a valid delimiter character: '/', '?', '#'. Overwrites the existing URL +segment and any following segments in order of path, query, then fragment. - +I.e. +- joining a path will clear the query and fragment. +- joining a query will clear the fragment. +- joining a fragment will only overwrite the fragment. -### receipt.network() ⇒ [Network](#Network) -Returns a copy of the associated IOTA Tangle `Network`. +**Kind**: instance method of [IotaDIDUrl](#IotaDIDUrl) + +| Param | Type | +| --- | --- | +| segment | string | -**Kind**: instance method of [Receipt](#Receipt) - + -### receipt.messageId() ⇒ string -Returns a copy of the message `id`. +### iotaDIDUrl.toString() ⇒ string +Returns the `IotaDIDUrl` as a string. -**Kind**: instance method of [Receipt](#Receipt) - +**Kind**: instance method of [IotaDIDUrl](#IotaDIDUrl) + -### receipt.networkId() ⇒ string -Returns a copy of the message `network_id`. +### iotaDIDUrl.toJSON() ⇒ any +Serializes this to a JSON object. -**Kind**: instance method of [Receipt](#Receipt) - +**Kind**: instance method of [IotaDIDUrl](#IotaDIDUrl) + -### receipt.nonce() ⇒ string -Returns a copy of the message `nonce`. +### iotaDIDUrl.clone() ⇒ [IotaDIDUrl](#IotaDIDUrl) +Deep clones the object. -**Kind**: instance method of [Receipt](#Receipt) - +**Kind**: instance method of [IotaDIDUrl](#IotaDIDUrl) + -### receipt.toJSON() ⇒ any -Serializes this to a JSON object. +### IotaDIDUrl.parse(input) ⇒ [IotaDIDUrl](#IotaDIDUrl) +Parses a `IotaDIDUrl` from the input string. -**Kind**: instance method of [Receipt](#Receipt) - +**Kind**: static method of [IotaDIDUrl](#IotaDIDUrl) -### receipt.clone() ⇒ [Receipt](#Receipt) -Deep clones the object. +| Param | Type | +| --- | --- | +| input | string | -**Kind**: instance method of [Receipt](#Receipt) - + -### Receipt.fromJSON(json) ⇒ [Receipt](#Receipt) +### IotaDIDUrl.fromJSON(json) ⇒ [IotaDIDUrl](#IotaDIDUrl) Deserializes an instance from a JSON object. -**Kind**: static method of [Receipt](#Receipt) +**Kind**: static method of [IotaDIDUrl](#IotaDIDUrl) | Param | Type | | --- | --- | | json | any | - - -## ResolvedDocument -An IOTA DID document resolved from the Tangle. Represents an integration chain message possibly -merged with one or more `DiffMessages`. + +## IotaDocument **Kind**: global class -* [ResolvedDocument](#ResolvedDocument) +* [IotaDocument](#IotaDocument) + * [new IotaDocument(network)](#new_IotaDocument_new) * _instance_ - * ~~[.mergeDiffMessage(diff_message)](#ResolvedDocument+mergeDiffMessage)~~ - * [.document()](#ResolvedDocument+document) ⇒ [Document](#Document) - * [.intoDocument()](#ResolvedDocument+intoDocument) ⇒ [Document](#Document) - * ~~[.diffMessageId()](#ResolvedDocument+diffMessageId) ⇒ string~~ - * ~~[.setDiffMessageId(value)](#ResolvedDocument+setDiffMessageId)~~ - * [.integrationMessageId()](#ResolvedDocument+integrationMessageId) ⇒ string - * [.setIntegrationMessageId(value)](#ResolvedDocument+setIntegrationMessageId) - * [.toJSON()](#ResolvedDocument+toJSON) ⇒ any - * [.clone()](#ResolvedDocument+clone) ⇒ [ResolvedDocument](#ResolvedDocument) + * [.id()](#IotaDocument+id) ⇒ [IotaDID](#IotaDID) + * [.controller()](#IotaDocument+controller) ⇒ [Array.<IotaDID>](#IotaDID) + * [.alsoKnownAs()](#IotaDocument+alsoKnownAs) ⇒ Array.<string> + * [.setAlsoKnownAs(urls)](#IotaDocument+setAlsoKnownAs) + * [.properties()](#IotaDocument+properties) ⇒ Map.<string, any> + * [.setPropertyUnchecked(key, value)](#IotaDocument+setPropertyUnchecked) + * [.service()](#IotaDocument+service) ⇒ [Array.<IotaService>](#IotaService) + * [.insertService(service)](#IotaDocument+insertService) ⇒ boolean + * [.removeService(did)](#IotaDocument+removeService) ⇒ boolean + * [.resolveService(query)](#IotaDocument+resolveService) ⇒ [IotaService](#IotaService) \| undefined + * [.methods()](#IotaDocument+methods) ⇒ [Array.<IotaVerificationMethod>](#IotaVerificationMethod) + * [.insertMethod(method, scope)](#IotaDocument+insertMethod) + * [.removeMethod(did)](#IotaDocument+removeMethod) + * [.resolveMethod(query, scope)](#IotaDocument+resolveMethod) ⇒ [IotaVerificationMethod](#IotaVerificationMethod) \| undefined + * [.attachMethodRelationship(didUrl, relationship)](#IotaDocument+attachMethodRelationship) ⇒ boolean + * [.detachMethodRelationship(didUrl, relationship)](#IotaDocument+detachMethodRelationship) ⇒ boolean + * [.signCredential(credential, privateKey, methodQuery, options)](#IotaDocument+signCredential) ⇒ [Credential](#Credential) + * [.signPresentation(presentation, privateKey, methodQuery, options)](#IotaDocument+signPresentation) ⇒ [Presentation](#Presentation) + * [.signData(data, privateKey, methodQuery, options)](#IotaDocument+signData) ⇒ any + * [.verifyData(data, options)](#IotaDocument+verifyData) ⇒ boolean + * [.pack()](#IotaDocument+pack) ⇒ Uint8Array + * [.packWithEncoding(encoding)](#IotaDocument+packWithEncoding) ⇒ Uint8Array + * [.metadata()](#IotaDocument+metadata) ⇒ [IotaDocumentMetadata](#IotaDocumentMetadata) + * [.metadataCreated()](#IotaDocument+metadataCreated) ⇒ [Timestamp](#Timestamp) \| undefined + * [.setMetadataCreated(timestamp)](#IotaDocument+setMetadataCreated) + * [.metadataUpdated()](#IotaDocument+metadataUpdated) ⇒ [Timestamp](#Timestamp) \| undefined + * [.setMetadataUpdated(timestamp)](#IotaDocument+setMetadataUpdated) + * [.metadataDeactivated()](#IotaDocument+metadataDeactivated) ⇒ boolean \| undefined + * [.setMetadataDeactivated(deactivated)](#IotaDocument+setMetadataDeactivated) + * [.setMetadataPropertyUnchecked(key, value)](#IotaDocument+setMetadataPropertyUnchecked) + * [.revokeCredentials(serviceQuery, indices)](#IotaDocument+revokeCredentials) + * [.unrevokeCredentials(serviceQuery, indices)](#IotaDocument+unrevokeCredentials) + * [.toJSON()](#IotaDocument+toJSON) ⇒ any + * [.clone()](#IotaDocument+clone) ⇒ [IotaDocument](#IotaDocument) * _static_ - * [.fromJSON(json)](#ResolvedDocument.fromJSON) ⇒ [ResolvedDocument](#ResolvedDocument) + * [.newWithId(id)](#IotaDocument.newWithId) ⇒ [IotaDocument](#IotaDocument) + * [.unpack(did, stateMetadata, allowEmpty)](#IotaDocument.unpack) ⇒ [IotaDocument](#IotaDocument) + * [.unpackFromBlock(network, block)](#IotaDocument.unpackFromBlock) ⇒ [Array.<IotaDocument>](#IotaDocument) + * [.fromJSON(json)](#IotaDocument.fromJSON) ⇒ [IotaDocument](#IotaDocument) - + -### ~~resolvedDocument.mergeDiffMessage(diff_message)~~ -***Deprecated*** - -Attempts to merge changes from a `DiffMessage` into this document and -updates the `ResolvedDocument::diffMessageId`. +### new IotaDocument(network) +Constructs an empty DID Document with a [placeholder](#IotaDID.placeholder) identifier +for the given `network`. -If merging fails the document remains unmodified, otherwise this represents -the merged document state. -See `Document::mergeDiff`. +| Param | Type | +| --- | --- | +| network | string | -# Errors + -Fails if the merge operation or signature verification on the diff fails. +### iotaDocument.id() ⇒ [IotaDID](#IotaDID) +Returns a copy of the DID Document `id`. -**Kind**: instance method of [ResolvedDocument](#ResolvedDocument) +**Kind**: instance method of [IotaDocument](#IotaDocument) + -| Param | Type | -| --- | --- | -| diff_message | [DiffMessage](#DiffMessage) | +### iotaDocument.controller() ⇒ [Array.<IotaDID>](#IotaDID) +Returns a copy of the list of document controllers. - +NOTE: controllers are determined by the `state_controller` unlock condition of the output +during resolution and are omitted when publishing. -### resolvedDocument.document() ⇒ [Document](#Document) -Returns a copy of the inner DID document. +**Kind**: instance method of [IotaDocument](#IotaDocument) + -NOTE: If the `ResolvedDocument` is no longer needed after calling this method -then consider using `intoDocument()` for efficiency. +### iotaDocument.alsoKnownAs() ⇒ Array.<string> +Returns a copy of the document's `alsoKnownAs` set. -**Kind**: instance method of [ResolvedDocument](#ResolvedDocument) - +**Kind**: instance method of [IotaDocument](#IotaDocument) + -### resolvedDocument.intoDocument() ⇒ [Document](#Document) -Consumes this object and returns the inner DID document. +### iotaDocument.setAlsoKnownAs(urls) +Sets the `alsoKnownAs` property in the DID document. -NOTE: trying to use the `ResolvedDocument` after calling this will throw an error. +**Kind**: instance method of [IotaDocument](#IotaDocument) -**Kind**: instance method of [ResolvedDocument](#ResolvedDocument) - +| Param | Type | +| --- | --- | +| urls | string \| Array.<string> \| null | -### ~~resolvedDocument.diffMessageId() ⇒ string~~ -***Deprecated*** + -Returns a copy of the diff chain message id. +### iotaDocument.properties() ⇒ Map.<string, any> +Returns a copy of the custom DID Document properties. -**Kind**: instance method of [ResolvedDocument](#ResolvedDocument) - +**Kind**: instance method of [IotaDocument](#IotaDocument) + -### ~~resolvedDocument.setDiffMessageId(value)~~ -***Deprecated*** +### iotaDocument.setPropertyUnchecked(key, value) +Sets a custom property in the DID Document. +If the value is set to `null`, the custom property will be removed. -Sets the diff chain message id. +### WARNING +This method can overwrite existing properties like `id` and result in an invalid document. -**Kind**: instance method of [ResolvedDocument](#ResolvedDocument) +**Kind**: instance method of [IotaDocument](#IotaDocument) | Param | Type | | --- | --- | -| value | string | +| key | string | +| value | any | + + - +### iotaDocument.service() ⇒ [Array.<IotaService>](#IotaService) +Return a set of all [IotaService](#IotaService) in the document. -### resolvedDocument.integrationMessageId() ⇒ string -Returns a copy of the integration chain message id. +**Kind**: instance method of [IotaDocument](#IotaDocument) + -**Kind**: instance method of [ResolvedDocument](#ResolvedDocument) - +### iotaDocument.insertService(service) ⇒ boolean +Add a new [IotaService](#IotaService) to the document. -### resolvedDocument.setIntegrationMessageId(value) -Sets the integration chain message id. +Returns `true` if the service was added. -**Kind**: instance method of [ResolvedDocument](#ResolvedDocument) +**Kind**: instance method of [IotaDocument](#IotaDocument) | Param | Type | | --- | --- | -| value | string | - - - -### resolvedDocument.toJSON() ⇒ any -Serializes this to a JSON object. - -**Kind**: instance method of [ResolvedDocument](#ResolvedDocument) - +| service | [IotaService](#IotaService) | -### resolvedDocument.clone() ⇒ [ResolvedDocument](#ResolvedDocument) -Deep clones the object. + -**Kind**: instance method of [ResolvedDocument](#ResolvedDocument) - +### iotaDocument.removeService(did) ⇒ boolean +Remove a [IotaService](#IotaService) identified by the given [IotaDIDUrl](#IotaDIDUrl) from the document. -### ResolvedDocument.fromJSON(json) ⇒ [ResolvedDocument](#ResolvedDocument) -Deserializes an instance from a JSON object. +Returns `true` if a service was removed. -**Kind**: static method of [ResolvedDocument](#ResolvedDocument) +**Kind**: instance method of [IotaDocument](#IotaDocument) | Param | Type | | --- | --- | -| json | any | +| did | [IotaDIDUrl](#IotaDIDUrl) | - + -## Resolver -**Kind**: global class +### iotaDocument.resolveService(query) ⇒ [IotaService](#IotaService) \| undefined +Returns the first [IotaService](#IotaService) with an `id` property matching the provided `query`, +if present. -* [Resolver](#Resolver) - * [new Resolver()](#new_Resolver_new) - * _instance_ - * [.getClient(network_name)](#Resolver+getClient) ⇒ [Client](#Client) \| undefined - * [.resolve(did)](#Resolver+resolve) ⇒ [Promise.<ResolvedDocument>](#ResolvedDocument) - * [.resolveHistory(did)](#Resolver+resolveHistory) ⇒ [Promise.<DocumentHistory>](#DocumentHistory) - * ~~[.resolveDiffHistory(document)](#Resolver+resolveDiffHistory) ⇒ [Promise.<DiffChainHistory>](#DiffChainHistory)~~ - * [.resolveCredentialIssuer(credential)](#Resolver+resolveCredentialIssuer) ⇒ [Promise.<ResolvedDocument>](#ResolvedDocument) - * [.resolvePresentationIssuers(presentation)](#Resolver+resolvePresentationIssuers) ⇒ Promise.<Array.<ResolvedDocument>> - * [.resolvePresentationHolder(presentation)](#Resolver+resolvePresentationHolder) ⇒ [Promise.<ResolvedDocument>](#ResolvedDocument) - * [.verifyPresentation(presentation, options, fail_fast, holder, issuers)](#Resolver+verifyPresentation) ⇒ Promise.<void> - * _static_ - * [.builder()](#Resolver.builder) ⇒ [ResolverBuilder](#ResolverBuilder) +**Kind**: instance method of [IotaDocument](#IotaDocument) - +| Param | Type | +| --- | --- | +| query | [IotaDIDUrl](#IotaDIDUrl) \| string | -### new Resolver() -Constructs a new `Resolver` with a default `Client` for -the `Mainnet`. + - +### iotaDocument.methods() ⇒ [Array.<IotaVerificationMethod>](#IotaVerificationMethod) +Returns a list of all [IotaVerificationMethod](#IotaVerificationMethod) in the DID Document. -### resolver.getClient(network_name) ⇒ [Client](#Client) \| undefined -Returns the `Client` corresponding to the given network name if one exists. +**Kind**: instance method of [IotaDocument](#IotaDocument) + + +### iotaDocument.insertMethod(method, scope) +Adds a new `method` to the document in the given `scope`. -**Kind**: instance method of [Resolver](#Resolver) +**Kind**: instance method of [IotaDocument](#IotaDocument) | Param | Type | | --- | --- | -| network_name | string | +| method | [IotaVerificationMethod](#IotaVerificationMethod) | +| scope | [MethodScope](#MethodScope) | - + -### resolver.resolve(did) ⇒ [Promise.<ResolvedDocument>](#ResolvedDocument) -Fetches the `Document` of the given `DID`. +### iotaDocument.removeMethod(did) +Removes all references to the specified Verification Method. -**Kind**: instance method of [Resolver](#Resolver) +**Kind**: instance method of [IotaDocument](#IotaDocument) | Param | Type | | --- | --- | -| did | [IotaDID](#IotaDID) \| string | +| did | [IotaDIDUrl](#IotaDIDUrl) | - + -### resolver.resolveHistory(did) ⇒ [Promise.<DocumentHistory>](#DocumentHistory) -Fetches the `DocumentHistory` of the given `DID`. +### iotaDocument.resolveMethod(query, scope) ⇒ [IotaVerificationMethod](#IotaVerificationMethod) \| undefined +Returns a copy of the first verification method with an `id` property +matching the provided `query` and the verification relationship +specified by `scope`, if present. -**Kind**: instance method of [Resolver](#Resolver) +**Kind**: instance method of [IotaDocument](#IotaDocument) | Param | Type | | --- | --- | -| did | [IotaDID](#IotaDID) \| string | - - +| query | [IotaDIDUrl](#IotaDIDUrl) \| string | +| scope | [MethodScope](#MethodScope) \| undefined | -### ~~resolver.resolveDiffHistory(document) ⇒ [Promise.<DiffChainHistory>](#DiffChainHistory)~~ -***Deprecated*** + -Returns the `DiffChainHistory` of a diff chain starting from a `Document` on the -integration chain. +### iotaDocument.attachMethodRelationship(didUrl, relationship) ⇒ boolean +Attaches the relationship to the given method, if the method exists. -NOTE: the document must have been published to the Tangle and have a valid message id. +Note: The method needs to be in the set of verification methods, +so it cannot be an embedded one. -**Kind**: instance method of [Resolver](#Resolver) +**Kind**: instance method of [IotaDocument](#IotaDocument) | Param | Type | | --- | --- | -| document | [ResolvedDocument](#ResolvedDocument) | - - - -### resolver.resolveCredentialIssuer(credential) ⇒ [Promise.<ResolvedDocument>](#ResolvedDocument) -Fetches the DID Document of the issuer on a `Credential`. +| didUrl | [IotaDIDUrl](#IotaDIDUrl) | +| relationship | number | -### Errors + -Errors if the issuer URL is not a valid `DID` or document resolution fails. +### iotaDocument.detachMethodRelationship(didUrl, relationship) ⇒ boolean +Detaches the given relationship from the given method, if the method exists. -**Kind**: instance method of [Resolver](#Resolver) +**Kind**: instance method of [IotaDocument](#IotaDocument) | Param | Type | | --- | --- | -| credential | [Credential](#Credential) | - - - -### resolver.resolvePresentationIssuers(presentation) ⇒ Promise.<Array.<ResolvedDocument>> -Fetches all DID Documents of `Credential` issuers contained in a `Presentation`. -Issuer documents are returned in arbitrary order. +| didUrl | [IotaDIDUrl](#IotaDIDUrl) | +| relationship | number | -### Errors + -Errors if any issuer URL is not a valid `DID` or document resolution fails. +### iotaDocument.signCredential(credential, privateKey, methodQuery, options) ⇒ [Credential](#Credential) +Creates a signature for the given `Credential` with the specified DID Document +Verification Method. -**Kind**: instance method of [Resolver](#Resolver) +**Kind**: instance method of [IotaDocument](#IotaDocument) | Param | Type | | --- | --- | -| presentation | [Presentation](#Presentation) | - - - -### resolver.resolvePresentationHolder(presentation) ⇒ [Promise.<ResolvedDocument>](#ResolvedDocument) -Fetches the DID Document of the holder of a `Presentation`. +| credential | [Credential](#Credential) | +| privateKey | Uint8Array | +| methodQuery | [IotaDIDUrl](#IotaDIDUrl) \| string | +| options | [ProofOptions](#ProofOptions) | -### Errors + -Errors if the holder URL is missing, is not a valid `DID`, or document resolution fails. +### iotaDocument.signPresentation(presentation, privateKey, methodQuery, options) ⇒ [Presentation](#Presentation) +Creates a signature for the given `Presentation` with the specified DID Document +Verification Method. -**Kind**: instance method of [Resolver](#Resolver) +**Kind**: instance method of [IotaDocument](#IotaDocument) | Param | Type | | --- | --- | | presentation | [Presentation](#Presentation) | +| privateKey | Uint8Array | +| methodQuery | [IotaDIDUrl](#IotaDIDUrl) \| string | +| options | [ProofOptions](#ProofOptions) | - - -### resolver.verifyPresentation(presentation, options, fail_fast, holder, issuers) ⇒ Promise.<void> -Verifies a `Presentation`. - -### Important -See `PresentationValidator::validate` for information about which properties get -validated and what is expected of the optional arguments `holder` and `issuer`. + -### Resolution -The DID Documents for the `holder` and `issuers` are optionally resolved if not given. -If you already have up-to-date versions of these DID Documents, you may want -to use `PresentationValidator::validate`. -See also `Resolver::resolvePresentationIssuers` and `Resolver::resolvePresentationHolder`. +### iotaDocument.signData(data, privateKey, methodQuery, options) ⇒ any +Creates a signature for the given `data` with the specified DID Document +Verification Method. -### Errors -Errors from resolving the holder and issuer DID Documents, if not provided, will be returned immediately. -Otherwise, errors from validating the presentation and its credentials will be returned -according to the `fail_fast` parameter. +NOTE: use `signSelf` or `signDocument` for DID Documents. -**Kind**: instance method of [Resolver](#Resolver) +**Kind**: instance method of [IotaDocument](#IotaDocument) | Param | Type | | --- | --- | -| presentation | [Presentation](#Presentation) | -| options | [PresentationValidationOptions](#PresentationValidationOptions) | -| fail_fast | number | -| holder | [Document](#Document) \| [ResolvedDocument](#ResolvedDocument) \| undefined | -| issuers | Array.<(Document\|ResolvedDocument)> \| undefined | - - - -### Resolver.builder() ⇒ [ResolverBuilder](#ResolverBuilder) -Returns a [ResolverBuilder](#ResolverBuilder) to construct a new `Resolver`. - -**Kind**: static method of [Resolver](#Resolver) - +| data | any | +| privateKey | Uint8Array | +| methodQuery | [IotaDIDUrl](#IotaDIDUrl) \| string | +| options | [ProofOptions](#ProofOptions) | -## ResolverBuilder -Builder for configuring [`Clients`][Client] when constructing a [`Resolver`]. + -**Kind**: global class +### iotaDocument.verifyData(data, options) ⇒ boolean +Verifies the authenticity of `data` using the target verification method. -* [ResolverBuilder](#ResolverBuilder) - * [new ResolverBuilder()](#new_ResolverBuilder_new) - * [.client(client)](#ResolverBuilder+client) ⇒ [ResolverBuilder](#ResolverBuilder) - * [.clientConfig(config)](#ResolverBuilder+clientConfig) ⇒ [ResolverBuilder](#ResolverBuilder) - * [.build()](#ResolverBuilder+build) ⇒ [Promise.<Resolver>](#Resolver) +**Kind**: instance method of [IotaDocument](#IotaDocument) - +| Param | Type | +| --- | --- | +| data | any | +| options | [VerifierOptions](#VerifierOptions) | -### new ResolverBuilder() -Constructs a new `ResolverBuilder` with no `Clients` configured. + - +### iotaDocument.pack() ⇒ Uint8Array +Serializes the document for inclusion in an Alias Output's state metadata +with the default [StateMetadataEncoding](#StateMetadataEncoding). -### resolverBuilder.client(client) ⇒ [ResolverBuilder](#ResolverBuilder) -Inserts a `Client`. +**Kind**: instance method of [IotaDocument](#IotaDocument) + -NOTE: replaces any previous `Client` or `Config` with the same network name. +### iotaDocument.packWithEncoding(encoding) ⇒ Uint8Array +Serializes the document for inclusion in an Alias Output's state metadata. -**Kind**: instance method of [ResolverBuilder](#ResolverBuilder) +**Kind**: instance method of [IotaDocument](#IotaDocument) | Param | Type | | --- | --- | -| client | [Client](#Client) | - - - -### resolverBuilder.clientConfig(config) ⇒ [ResolverBuilder](#ResolverBuilder) -Inserts a `Config` used to create a `Client`. +| encoding | number | -NOTE: replaces any previous `Client` or `Config` with the same network name. + -**Kind**: instance method of [ResolverBuilder](#ResolverBuilder) +### iotaDocument.metadata() ⇒ [IotaDocumentMetadata](#IotaDocumentMetadata) +Returns a copy of the metadata associated with this document. -| Param | Type | -| --- | --- | -| config | IClientConfig | +NOTE: Copies all the metadata. See also `metadataCreated`, `metadataUpdated`, +`metadataPreviousMessageId`, `metadataProof` if only a subset of the metadata required. - +**Kind**: instance method of [IotaDocument](#IotaDocument) + -### resolverBuilder.build() ⇒ [Promise.<Resolver>](#Resolver) -Constructs a new [`Resolver`] based on the builder configuration. +### iotaDocument.metadataCreated() ⇒ [Timestamp](#Timestamp) \| undefined +Returns a copy of the timestamp of when the DID document was created. -**Kind**: instance method of [ResolverBuilder](#ResolverBuilder) - +**Kind**: instance method of [IotaDocument](#IotaDocument) + -## RevocationBitmap -A compressed bitmap for managing credential revocation. +### iotaDocument.setMetadataCreated(timestamp) +Sets the timestamp of when the DID document was created. -**Kind**: global class +**Kind**: instance method of [IotaDocument](#IotaDocument) -* [RevocationBitmap](#RevocationBitmap) - * [new RevocationBitmap()](#new_RevocationBitmap_new) - * _instance_ - * [.isRevoked(index)](#RevocationBitmap+isRevoked) ⇒ boolean - * [.revoke(index)](#RevocationBitmap+revoke) ⇒ boolean - * [.unrevoke(index)](#RevocationBitmap+unrevoke) ⇒ boolean - * [.len()](#RevocationBitmap+len) ⇒ number - * [.toEndpoint()](#RevocationBitmap+toEndpoint) ⇒ string \| Array.<string> \| Map.<string, Array.<string>> - * _static_ - * [.type()](#RevocationBitmap.type) ⇒ string - * [.fromEndpoint(endpoint)](#RevocationBitmap.fromEndpoint) ⇒ [RevocationBitmap](#RevocationBitmap) +| Param | Type | +| --- | --- | +| timestamp | [Timestamp](#Timestamp) \| undefined | - + -### new RevocationBitmap() -Creates a new `RevocationBitmap` instance. +### iotaDocument.metadataUpdated() ⇒ [Timestamp](#Timestamp) \| undefined +Returns a copy of the timestamp of the last DID document update. - +**Kind**: instance method of [IotaDocument](#IotaDocument) + -### revocationBitmap.isRevoked(index) ⇒ boolean -Returns `true` if the credential at the given `index` is revoked. +### iotaDocument.setMetadataUpdated(timestamp) +Sets the timestamp of the last DID document update. -**Kind**: instance method of [RevocationBitmap](#RevocationBitmap) +**Kind**: instance method of [IotaDocument](#IotaDocument) | Param | Type | | --- | --- | -| index | number | +| timestamp | [Timestamp](#Timestamp) \| undefined | - + -### revocationBitmap.revoke(index) ⇒ boolean -Mark the given index as revoked. +### iotaDocument.metadataDeactivated() ⇒ boolean \| undefined +Returns a copy of the deactivated status of the DID document. -Returns true if the index was absent from the set. +**Kind**: instance method of [IotaDocument](#IotaDocument) + -**Kind**: instance method of [RevocationBitmap](#RevocationBitmap) +### iotaDocument.setMetadataDeactivated(deactivated) +Sets the deactivated status of the DID document. + +**Kind**: instance method of [IotaDocument](#IotaDocument) | Param | Type | | --- | --- | -| index | number | - - +| deactivated | boolean \| undefined | -### revocationBitmap.unrevoke(index) ⇒ boolean -Mark the index as not revoked. + -Returns true if the index was present in the set. +### iotaDocument.setMetadataPropertyUnchecked(key, value) +Sets a custom property in the document metadata. +If the value is set to `null`, the custom property will be removed. -**Kind**: instance method of [RevocationBitmap](#RevocationBitmap) +**Kind**: instance method of [IotaDocument](#IotaDocument) | Param | Type | | --- | --- | -| index | number | - - - -### revocationBitmap.len() ⇒ number -Returns the number of revoked credentials. +| key | string | +| value | any | -**Kind**: instance method of [RevocationBitmap](#RevocationBitmap) - + -### revocationBitmap.toEndpoint() ⇒ string \| Array.<string> \| Map.<string, Array.<string>> -Return the bitmap as a data url embedded in a service endpoint. +### iotaDocument.revokeCredentials(serviceQuery, indices) +If the document has a `RevocationBitmap` service identified by `serviceQuery`, +revoke all specified `indices`. -**Kind**: instance method of [RevocationBitmap](#RevocationBitmap) - +**Kind**: instance method of [IotaDocument](#IotaDocument) -### RevocationBitmap.type() ⇒ string -The name of the service type. +| Param | Type | +| --- | --- | +| serviceQuery | [IotaDIDUrl](#IotaDIDUrl) \| string | +| indices | number \| Array.<number> | -**Kind**: static method of [RevocationBitmap](#RevocationBitmap) - + -### RevocationBitmap.fromEndpoint(endpoint) ⇒ [RevocationBitmap](#RevocationBitmap) -Construct a `RevocationBitmap` from a data `url`. +### iotaDocument.unrevokeCredentials(serviceQuery, indices) +If the document has a `RevocationBitmap` service identified by `serviceQuery`, +unrevoke all specified `indices`. -**Kind**: static method of [RevocationBitmap](#RevocationBitmap) +**Kind**: instance method of [IotaDocument](#IotaDocument) | Param | Type | | --- | --- | -| endpoint | string \| Array.<string> \| Map.<string, Array.<string>> | +| serviceQuery | [IotaDIDUrl](#IotaDIDUrl) \| string | +| indices | number \| Array.<number> | - + -## Service -A DID Document Service used to enable trusted interactions associated -with a DID subject. +### iotaDocument.toJSON() ⇒ any +Serializes this to a JSON object. -See: https://www.w3.org/TR/did-core/#services +**Kind**: instance method of [IotaDocument](#IotaDocument) + -**Kind**: global class +### iotaDocument.clone() ⇒ [IotaDocument](#IotaDocument) +Deep clones the object. -* [Service](#Service) - * [new Service(service)](#new_Service_new) - * _instance_ - * [.id()](#Service+id) ⇒ [DIDUrl](#DIDUrl) - * [.type()](#Service+type) ⇒ Array.<string> - * [.serviceEndpoint()](#Service+serviceEndpoint) ⇒ string \| Array.<string> \| Map.<string, Array.<string>> - * [.properties()](#Service+properties) ⇒ Map.<string, any> - * [.toJSON()](#Service+toJSON) ⇒ any - * [.clone()](#Service+clone) ⇒ [Service](#Service) - * _static_ - * [.fromJSON(json)](#Service.fromJSON) ⇒ [Service](#Service) +**Kind**: instance method of [IotaDocument](#IotaDocument) + - +### IotaDocument.newWithId(id) ⇒ [IotaDocument](#IotaDocument) +Constructs an empty DID Document with the given identifier. -### new Service(service) +**Kind**: static method of [IotaDocument](#IotaDocument) | Param | Type | | --- | --- | -| service | IIotaService | +| id | [IotaDID](#IotaDID) | - - -### service.id() ⇒ [DIDUrl](#DIDUrl) -Returns a copy of the `Service` id. + -**Kind**: instance method of [Service](#Service) - +### IotaDocument.unpack(did, stateMetadata, allowEmpty) ⇒ [IotaDocument](#IotaDocument) +Deserializes the document from the state metadata bytes of an Alias Output. -### service.type() ⇒ Array.<string> -Returns a copy of the `Service` type. +If `allowEmpty` is true, this will return an empty DID document marked as `deactivated` +if `stateMetadata` is empty. -**Kind**: instance method of [Service](#Service) - +NOTE: `did` is required since it is omitted from the serialized DID Document and +cannot be inferred from the state metadata. It also indicates the network, which is not +encoded in the `AliasId` alone. -### service.serviceEndpoint() ⇒ string \| Array.<string> \| Map.<string, Array.<string>> -Returns a copy of the `Service` endpoint. +**Kind**: static method of [IotaDocument](#IotaDocument) -**Kind**: instance method of [Service](#Service) - +| Param | Type | +| --- | --- | +| did | [IotaDID](#IotaDID) | +| stateMetadata | Uint8Array | +| allowEmpty | boolean | -### service.properties() ⇒ Map.<string, any> -Returns a copy of the custom properties on the `Service`. + -**Kind**: instance method of [Service](#Service) - +### IotaDocument.unpackFromBlock(network, block) ⇒ [Array.<IotaDocument>](#IotaDocument) +Returns all DID documents of the Alias Outputs contained in the block's transaction payload +outputs, if any. -### service.toJSON() ⇒ any -Serializes this to a JSON object. +Errors if any Alias Output does not contain a valid or empty DID Document. -**Kind**: instance method of [Service](#Service) - +**Kind**: static method of [IotaDocument](#IotaDocument) -### service.clone() ⇒ [Service](#Service) -Deep clones the object. +| Param | Type | +| --- | --- | +| network | string | +| block | IBlock | -**Kind**: instance method of [Service](#Service) - + -### Service.fromJSON(json) ⇒ [Service](#Service) +### IotaDocument.fromJSON(json) ⇒ [IotaDocument](#IotaDocument) Deserializes an instance from a JSON object. -**Kind**: static method of [Service](#Service) +**Kind**: static method of [IotaDocument](#IotaDocument) | Param | Type | | --- | --- | | json | any | - + -## Signature -A digital signature. +## IotaDocumentMetadata +Additional attributes related to an IOTA DID Document. **Kind**: global class -* [Signature](#Signature) - * [new Signature(data)](#new_Signature_new) +* [IotaDocumentMetadata](#IotaDocumentMetadata) * _instance_ - * [.asBytes()](#Signature+asBytes) ⇒ Uint8Array - * [.toJSON()](#Signature+toJSON) ⇒ any + * [.created()](#IotaDocumentMetadata+created) ⇒ [Timestamp](#Timestamp) \| undefined + * [.updated()](#IotaDocumentMetadata+updated) ⇒ [Timestamp](#Timestamp) \| undefined + * [.deactivated()](#IotaDocumentMetadata+deactivated) ⇒ boolean \| undefined + * [.properties()](#IotaDocumentMetadata+properties) ⇒ Map.<string, any> + * [.toJSON()](#IotaDocumentMetadata+toJSON) ⇒ any + * [.clone()](#IotaDocumentMetadata+clone) ⇒ [IotaDocumentMetadata](#IotaDocumentMetadata) * _static_ - * [.fromJSON(json)](#Signature.fromJSON) ⇒ [Signature](#Signature) + * [.fromJSON(json)](#IotaDocumentMetadata.fromJSON) ⇒ [IotaDocumentMetadata](#IotaDocumentMetadata) - + -### new Signature(data) -Creates a new `Signature`. +### iotaDocumentMetadata.created() ⇒ [Timestamp](#Timestamp) \| undefined +Returns a copy of the timestamp of when the DID document was created. +**Kind**: instance method of [IotaDocumentMetadata](#IotaDocumentMetadata) + -| Param | Type | -| --- | --- | -| data | Uint8Array | +### iotaDocumentMetadata.updated() ⇒ [Timestamp](#Timestamp) \| undefined +Returns a copy of the timestamp of the last DID document update. + +**Kind**: instance method of [IotaDocumentMetadata](#IotaDocumentMetadata) + + +### iotaDocumentMetadata.deactivated() ⇒ boolean \| undefined +Returns a copy of the deactivated status of the DID document. + +**Kind**: instance method of [IotaDocumentMetadata](#IotaDocumentMetadata) + + +### iotaDocumentMetadata.properties() ⇒ Map.<string, any> +Returns a copy of the custom metadata properties. - +**Kind**: instance method of [IotaDocumentMetadata](#IotaDocumentMetadata) + -### signature.asBytes() ⇒ Uint8Array -Returns a copy of the signature as a `UInt8Array`. +### iotaDocumentMetadata.toJSON() ⇒ any +Serializes this to a JSON object. -**Kind**: instance method of [Signature](#Signature) - +**Kind**: instance method of [IotaDocumentMetadata](#IotaDocumentMetadata) + -### signature.toJSON() ⇒ any -Serializes this to a JSON object. +### iotaDocumentMetadata.clone() ⇒ [IotaDocumentMetadata](#IotaDocumentMetadata) +Deep clones the object. -**Kind**: instance method of [Signature](#Signature) - +**Kind**: instance method of [IotaDocumentMetadata](#IotaDocumentMetadata) + -### Signature.fromJSON(json) ⇒ [Signature](#Signature) +### IotaDocumentMetadata.fromJSON(json) ⇒ [IotaDocumentMetadata](#IotaDocumentMetadata) Deserializes an instance from a JSON object. -**Kind**: static method of [Signature](#Signature) +**Kind**: static method of [IotaDocumentMetadata](#IotaDocumentMetadata) | Param | Type | | --- | --- | | json | any | - + -## StardustDID -A DID conforming to the IOTA UTXO DID method specification. +## IotaIdentityClientExt +An extension interface that provides helper functions for publication +and resolution of DID documents in Alias Outputs. **Kind**: global class -* [StardustDID](#StardustDID) - * [new StardustDID(bytes, network)](#new_StardustDID_new) - * _instance_ - * [.networkStr()](#StardustDID+networkStr) ⇒ string - * [.tag()](#StardustDID+tag) ⇒ string - * [.scheme()](#StardustDID+scheme) ⇒ string - * [.authority()](#StardustDID+authority) ⇒ string - * [.method()](#StardustDID+method) ⇒ string - * [.methodId()](#StardustDID+methodId) ⇒ string - * [.join(segment)](#StardustDID+join) ⇒ [StardustDIDUrl](#StardustDIDUrl) - * [.toUrl()](#StardustDID+toUrl) ⇒ [StardustDIDUrl](#StardustDIDUrl) - * [.intoUrl()](#StardustDID+intoUrl) ⇒ [StardustDIDUrl](#StardustDIDUrl) - * [.toString()](#StardustDID+toString) ⇒ string - * [.toJSON()](#StardustDID+toJSON) ⇒ any - * [.clone()](#StardustDID+clone) ⇒ [StardustDID](#StardustDID) - * _static_ - * [.METHOD](#StardustDID.METHOD) ⇒ string - * [.DEFAULT_NETWORK](#StardustDID.DEFAULT_NETWORK) ⇒ string - * [.placeholder(network)](#StardustDID.placeholder) ⇒ [StardustDID](#StardustDID) - * [.parse(input)](#StardustDID.parse) ⇒ [StardustDID](#StardustDID) - * [.fromJSON(json)](#StardustDID.fromJSON) ⇒ [StardustDID](#StardustDID) +* [IotaIdentityClientExt](#IotaIdentityClientExt) + * [.newDidOutput(client, address, document, rentStructure)](#IotaIdentityClientExt.newDidOutput) ⇒ Promise.<IAliasOutput> + * [.updateDidOutput(client, document)](#IotaIdentityClientExt.updateDidOutput) ⇒ Promise.<IAliasOutput> + * [.deactivateDidOutput(client, did)](#IotaIdentityClientExt.deactivateDidOutput) ⇒ Promise.<IAliasOutput> + * [.resolveDid(client, did)](#IotaIdentityClientExt.resolveDid) ⇒ [Promise.<IotaDocument>](#IotaDocument) + * [.resolveDidOutput(client, did)](#IotaIdentityClientExt.resolveDidOutput) ⇒ Promise.<IAliasOutput> - + -### new StardustDID(bytes, network) -Constructs a new `StardustDID` from a byte representation of the tag and the given -network name. +### IotaIdentityClientExt.newDidOutput(client, address, document, rentStructure) ⇒ Promise.<IAliasOutput> +Create a DID with a new Alias Output containing the given `document`. + +The `address` will be set as the state controller and governor unlock conditions. +The minimum required token deposit amount will be set according to the given +`rent_structure`, which will be fetched from the node if not provided. +The returned Alias Output can be further customised before publication, if desired. -See also [placeholder](#StardustDID.placeholder). +NOTE: this does *not* publish the Alias Output. +**Kind**: static method of [IotaIdentityClientExt](#IotaIdentityClientExt) | Param | Type | | --- | --- | -| bytes | Uint8Array | -| network | string | - - - -### did.networkStr() ⇒ string -Returns the Tangle network name of the `StardustDID`. - -**Kind**: instance method of [StardustDID](#StardustDID) - - -### did.tag() ⇒ string -Returns a copy of the unique tag of the `StardustDID`. - -**Kind**: instance method of [StardustDID](#StardustDID) - +| client | IIotaIdentityClient | +| address | AddressTypes | +| document | [IotaDocument](#IotaDocument) | +| rentStructure | IRent \| undefined | -### did.scheme() ⇒ string -Returns the `DID` scheme. + -E.g. -- `"did:example:12345678" -> "did"` -- `"did:iota:main:12345678" -> "did"` +### IotaIdentityClientExt.updateDidOutput(client, document) ⇒ Promise.<IAliasOutput> +Fetches the associated Alias Output and updates it with `document` in its state metadata. +The storage deposit on the output is left unchanged. If the size of the document increased, +the amount should be increased manually. -**Kind**: instance method of [StardustDID](#StardustDID) - +NOTE: this does *not* publish the updated Alias Output. -### did.authority() ⇒ string -Returns the `DID` authority: the method name and method-id. +**Kind**: static method of [IotaIdentityClientExt](#IotaIdentityClientExt) -E.g. -- `"did:example:12345678" -> "example:12345678"` -- `"did:iota:main:12345678" -> "iota:main:12345678"` +| Param | Type | +| --- | --- | +| client | IIotaIdentityClient | +| document | [IotaDocument](#IotaDocument) | -**Kind**: instance method of [StardustDID](#StardustDID) - + -### did.method() ⇒ string -Returns the `DID` method name. +### IotaIdentityClientExt.deactivateDidOutput(client, did) ⇒ Promise.<IAliasOutput> +Removes the DID document from the state metadata of its Alias Output, +effectively deactivating it. The storage deposit on the output is left unchanged, +and should be reallocated manually. -E.g. -- `"did:example:12345678" -> "example"` -- `"did:iota:main:12345678" -> "iota"` +Deactivating does not destroy the output. Hence, it can be re-activated by publishing +an update containing a DID document. -**Kind**: instance method of [StardustDID](#StardustDID) - +NOTE: this does *not* publish the updated Alias Output. -### did.methodId() ⇒ string -Returns the `DID` method-specific ID. +**Kind**: static method of [IotaIdentityClientExt](#IotaIdentityClientExt) -E.g. -- `"did:example:12345678" -> "12345678"` -- `"did:iota:main:12345678" -> "main:12345678"` +| Param | Type | +| --- | --- | +| client | IIotaIdentityClient | +| did | [IotaDID](#IotaDID) | -**Kind**: instance method of [StardustDID](#StardustDID) - + -### did.join(segment) ⇒ [StardustDIDUrl](#StardustDIDUrl) -Construct a new `DIDUrl` by joining with a relative DID Url string. +### IotaIdentityClientExt.resolveDid(client, did) ⇒ [Promise.<IotaDocument>](#IotaDocument) +Resolve a [IotaDocument](#IotaDocument). Returns an empty, deactivated document if the state metadata +of the Alias Output is empty. -**Kind**: instance method of [StardustDID](#StardustDID) +**Kind**: static method of [IotaIdentityClientExt](#IotaIdentityClientExt) | Param | Type | | --- | --- | -| segment | string | +| client | IIotaIdentityClient | +| did | [IotaDID](#IotaDID) | - + -### did.toUrl() ⇒ [StardustDIDUrl](#StardustDIDUrl) -Clones the `DID` into a `DIDUrl`. +### IotaIdentityClientExt.resolveDidOutput(client, did) ⇒ Promise.<IAliasOutput> +Fetches the `IAliasOutput` associated with the given DID. -**Kind**: instance method of [StardustDID](#StardustDID) - +**Kind**: static method of [IotaIdentityClientExt](#IotaIdentityClientExt) -### did.intoUrl() ⇒ [StardustDIDUrl](#StardustDIDUrl) -Converts the `DID` into a `DIDUrl`, consuming it. +| Param | Type | +| --- | --- | +| client | IIotaIdentityClient | +| did | [IotaDID](#IotaDID) | -**Kind**: instance method of [StardustDID](#StardustDID) - + -### did.toString() ⇒ string -Returns the `DID` as a string. +## IotaService +A `Service` adhering to the IOTA DID method specification. -**Kind**: instance method of [StardustDID](#StardustDID) - +**Kind**: global class -### did.toJSON() ⇒ any -Serializes this to a JSON object. +* [IotaService](#IotaService) + * [new IotaService(service)](#new_IotaService_new) + * _instance_ + * [.id()](#IotaService+id) ⇒ [IotaDIDUrl](#IotaDIDUrl) + * [.type()](#IotaService+type) ⇒ Array.<string> + * [.serviceEndpoint()](#IotaService+serviceEndpoint) ⇒ string \| Array.<string> \| Map.<string, Array.<string>> + * [.properties()](#IotaService+properties) ⇒ Map.<string, any> + * [.toJSON()](#IotaService+toJSON) ⇒ any + * [.clone()](#IotaService+clone) ⇒ [IotaService](#IotaService) + * _static_ + * [.fromJSON(json)](#IotaService.fromJSON) ⇒ [IotaService](#IotaService) -**Kind**: instance method of [StardustDID](#StardustDID) - + -### did.clone() ⇒ [StardustDID](#StardustDID) -Deep clones the object. +### new IotaService(service) -**Kind**: instance method of [StardustDID](#StardustDID) - +| Param | Type | +| --- | --- | +| service | IIotaService | -### StardustDID.METHOD ⇒ string -The IOTA UTXO DID method name (`"iota"`). + -**Kind**: static property of [StardustDID](#StardustDID) - +### iotaService.id() ⇒ [IotaDIDUrl](#IotaDIDUrl) +Returns a copy of the `Service` id. -### StardustDID.DEFAULT\_NETWORK ⇒ string -The default Tangle network (`"main"`). +**Kind**: instance method of [IotaService](#IotaService) + -**Kind**: static property of [StardustDID](#StardustDID) - +### iotaService.type() ⇒ Array.<string> +Returns a copy of the `Service` type. -### StardustDID.placeholder(network) ⇒ [StardustDID](#StardustDID) -Creates a new placeholder [`StardustDID`] with the given network name. +**Kind**: instance method of [IotaService](#IotaService) + -E.g. `did:iota:smr:0x0000000000000000000000000000000000000000000000000000000000000000`. +### iotaService.serviceEndpoint() ⇒ string \| Array.<string> \| Map.<string, Array.<string>> +Returns a copy of the `Service` endpoint. -**Kind**: static method of [StardustDID](#StardustDID) +**Kind**: instance method of [IotaService](#IotaService) + -| Param | Type | -| --- | --- | -| network | string | +### iotaService.properties() ⇒ Map.<string, any> +Returns a copy of the custom properties on the `Service`. - +**Kind**: instance method of [IotaService](#IotaService) + -### StardustDID.parse(input) ⇒ [StardustDID](#StardustDID) -Parses a `StardustDID` from the input string. +### iotaService.toJSON() ⇒ any +Serializes this to a JSON object. -**Kind**: static method of [StardustDID](#StardustDID) +**Kind**: instance method of [IotaService](#IotaService) + -| Param | Type | -| --- | --- | -| input | string | +### iotaService.clone() ⇒ [IotaService](#IotaService) +Deep clones the object. - +**Kind**: instance method of [IotaService](#IotaService) + -### StardustDID.fromJSON(json) ⇒ [StardustDID](#StardustDID) +### IotaService.fromJSON(json) ⇒ [IotaService](#IotaService) Deserializes an instance from a JSON object. -**Kind**: static method of [StardustDID](#StardustDID) +**Kind**: static method of [IotaService](#IotaService) | Param | Type | | --- | --- | | json | any | - - -## StardustDIDUrl -A DID URL conforming to the IOTA Stardust UTXO DID method specification. + +## IotaVerificationMethod **Kind**: global class -* [StardustDIDUrl](#StardustDIDUrl) +* [IotaVerificationMethod](#IotaVerificationMethod) + * [new IotaVerificationMethod(did, keyType, publicKey, fragment)](#new_IotaVerificationMethod_new) * _instance_ - * [.did()](#StardustDIDUrl+did) ⇒ [StardustDID](#StardustDID) - * [.urlStr()](#StardustDIDUrl+urlStr) ⇒ string - * [.fragment()](#StardustDIDUrl+fragment) ⇒ string \| undefined - * [.setFragment(value)](#StardustDIDUrl+setFragment) - * [.path()](#StardustDIDUrl+path) ⇒ string \| undefined - * [.setPath(value)](#StardustDIDUrl+setPath) - * [.query()](#StardustDIDUrl+query) ⇒ string \| undefined - * [.setQuery(value)](#StardustDIDUrl+setQuery) - * [.join(segment)](#StardustDIDUrl+join) ⇒ [StardustDIDUrl](#StardustDIDUrl) - * [.toString()](#StardustDIDUrl+toString) ⇒ string - * [.toJSON()](#StardustDIDUrl+toJSON) ⇒ any - * [.clone()](#StardustDIDUrl+clone) ⇒ [StardustDIDUrl](#StardustDIDUrl) + * [.id()](#IotaVerificationMethod+id) ⇒ [IotaDIDUrl](#IotaDIDUrl) + * [.setId(id)](#IotaVerificationMethod+setId) + * [.controller()](#IotaVerificationMethod+controller) ⇒ [IotaDID](#IotaDID) + * [.setController(did)](#IotaVerificationMethod+setController) + * [.setType(type_)](#IotaVerificationMethod+setType) + * [.type()](#IotaVerificationMethod+type) ⇒ [MethodType](#MethodType) + * [.data()](#IotaVerificationMethod+data) ⇒ [MethodData](#MethodData) + * [.setData(data)](#IotaVerificationMethod+setData) + * [.properties()](#IotaVerificationMethod+properties) ⇒ Map.<string, any> + * [.setPropertyUnchecked(key, value)](#IotaVerificationMethod+setPropertyUnchecked) + * [.toJSON()](#IotaVerificationMethod+toJSON) ⇒ any + * [.clone()](#IotaVerificationMethod+clone) ⇒ [IotaVerificationMethod](#IotaVerificationMethod) * _static_ - * [.parse(input)](#StardustDIDUrl.parse) ⇒ [StardustDIDUrl](#StardustDIDUrl) - * [.fromJSON(json)](#StardustDIDUrl.fromJSON) ⇒ [StardustDIDUrl](#StardustDIDUrl) + * [.fromJSON(json)](#IotaVerificationMethod.fromJSON) ⇒ [IotaVerificationMethod](#IotaVerificationMethod) - + -### stardustDIDUrl.did() ⇒ [StardustDID](#StardustDID) -Return a copy of the `StardustDID` section of the `StardustDIDUrl`. +### new IotaVerificationMethod(did, keyType, publicKey, fragment) +Creates a new `IotaVerificationMethod` from the given `did` and public key. -**Kind**: instance method of [StardustDIDUrl](#StardustDIDUrl) - -### stardustDIDUrl.urlStr() ⇒ string -Return a copy of the relative DID Url as a string, including only the path, query, and fragment. +| Param | Type | +| --- | --- | +| did | [IotaDID](#IotaDID) | +| keyType | number | +| publicKey | Uint8Array | +| fragment | string | -**Kind**: instance method of [StardustDIDUrl](#StardustDIDUrl) - + -### stardustDIDUrl.fragment() ⇒ string \| undefined -Returns a copy of the `StardustDIDUrl` method fragment, if any. Excludes the leading '#'. +### iotaVerificationMethod.id() ⇒ [IotaDIDUrl](#IotaDIDUrl) +Returns a reference to the `IotaVerificationMethod` id. -**Kind**: instance method of [StardustDIDUrl](#StardustDIDUrl) - +**Kind**: instance method of [IotaVerificationMethod](#IotaVerificationMethod) + -### stardustDIDUrl.setFragment(value) -Sets the `fragment` component of the `StardustDIDUrl`. +### iotaVerificationMethod.setId(id) +Sets the id of the `IotaVerificationMethod`. -**Kind**: instance method of [StardustDIDUrl](#StardustDIDUrl) +**Kind**: instance method of [IotaVerificationMethod](#IotaVerificationMethod) | Param | Type | | --- | --- | -| value | string \| undefined | +| id | [IotaDIDUrl](#IotaDIDUrl) | - + -### stardustDIDUrl.path() ⇒ string \| undefined -Returns a copy of the `StardustDIDUrl` path. +### iotaVerificationMethod.controller() ⇒ [IotaDID](#IotaDID) +Returns a copy of the `controller` `DID` of the `IotaVerificationMethod`. -**Kind**: instance method of [StardustDIDUrl](#StardustDIDUrl) - +**Kind**: instance method of [IotaVerificationMethod](#IotaVerificationMethod) + -### stardustDIDUrl.setPath(value) -Sets the `path` component of the `StardustDIDUrl`. +### iotaVerificationMethod.setController(did) +Sets the `controller` `DID` of the `IotaVerificationMethod`. -**Kind**: instance method of [StardustDIDUrl](#StardustDIDUrl) +**Kind**: instance method of [IotaVerificationMethod](#IotaVerificationMethod) | Param | Type | | --- | --- | -| value | string \| undefined | - - - -### stardustDIDUrl.query() ⇒ string \| undefined -Returns a copy of the `StardustDIDUrl` method query, if any. Excludes the leading '?'. +| did | [IotaDID](#IotaDID) | -**Kind**: instance method of [StardustDIDUrl](#StardustDIDUrl) - + -### stardustDIDUrl.setQuery(value) -Sets the `query` component of the `StardustDIDUrl`. +### iotaVerificationMethod.setType(type_) +Sets the `IotaVerificationMethod` type. -**Kind**: instance method of [StardustDIDUrl](#StardustDIDUrl) +**Kind**: instance method of [IotaVerificationMethod](#IotaVerificationMethod) | Param | Type | | --- | --- | -| value | string \| undefined | +| type_ | [MethodType](#MethodType) | - + -### stardustDIDUrl.join(segment) ⇒ [StardustDIDUrl](#StardustDIDUrl) -Append a string representing a path, query, and/or fragment, returning a new `StardustDIDUrl`. +### iotaVerificationMethod.type() ⇒ [MethodType](#MethodType) +Returns a copy of the `IotaVerificationMethod` type. -Must begin with a valid delimiter character: '/', '?', '#'. Overwrites the existing URL -segment and any following segments in order of path, query, then fragment. +**Kind**: instance method of [IotaVerificationMethod](#IotaVerificationMethod) + -I.e. -- joining a path will clear the query and fragment. -- joining a query will clear the fragment. -- joining a fragment will only overwrite the fragment. +### iotaVerificationMethod.data() ⇒ [MethodData](#MethodData) +Returns a copy of the `IotaVerificationMethod` public key data. -**Kind**: instance method of [StardustDIDUrl](#StardustDIDUrl) +**Kind**: instance method of [IotaVerificationMethod](#IotaVerificationMethod) + + +### iotaVerificationMethod.setData(data) +Sets `IotaVerificationMethod` public key data. + +**Kind**: instance method of [IotaVerificationMethod](#IotaVerificationMethod) | Param | Type | | --- | --- | -| segment | string | +| data | [MethodData](#MethodData) | - + -### stardustDIDUrl.toString() ⇒ string -Returns the `StardustDIDUrl` as a string. +### iotaVerificationMethod.properties() ⇒ Map.<string, any> +Get custom properties of the Verification Method. -**Kind**: instance method of [StardustDIDUrl](#StardustDIDUrl) - +**Kind**: instance method of [IotaVerificationMethod](#IotaVerificationMethod) + -### stardustDIDUrl.toJSON() ⇒ any -Serializes this to a JSON object. +### iotaVerificationMethod.setPropertyUnchecked(key, value) +Adds a custom property to the Verification Method. +If the value is set to `null`, the custom property will be removed. -**Kind**: instance method of [StardustDIDUrl](#StardustDIDUrl) - +### WARNING +This method can overwrite existing properties like `id` and result +in an invalid Verification Method. -### stardustDIDUrl.clone() ⇒ [StardustDIDUrl](#StardustDIDUrl) -Deep clones the object. +**Kind**: instance method of [IotaVerificationMethod](#IotaVerificationMethod) -**Kind**: instance method of [StardustDIDUrl](#StardustDIDUrl) - +| Param | Type | +| --- | --- | +| key | string | +| value | any | -### StardustDIDUrl.parse(input) ⇒ [StardustDIDUrl](#StardustDIDUrl) -Parses a `StardustDIDUrl` from the input string. + -**Kind**: static method of [StardustDIDUrl](#StardustDIDUrl) +### iotaVerificationMethod.toJSON() ⇒ any +Serializes this to a JSON object. -| Param | Type | -| --- | --- | -| input | string | +**Kind**: instance method of [IotaVerificationMethod](#IotaVerificationMethod) + - +### iotaVerificationMethod.clone() ⇒ [IotaVerificationMethod](#IotaVerificationMethod) +Deep clones the object. + +**Kind**: instance method of [IotaVerificationMethod](#IotaVerificationMethod) + -### StardustDIDUrl.fromJSON(json) ⇒ [StardustDIDUrl](#StardustDIDUrl) +### IotaVerificationMethod.fromJSON(json) ⇒ [IotaVerificationMethod](#IotaVerificationMethod) Deserializes an instance from a JSON object. -**Kind**: static method of [StardustDIDUrl](#StardustDIDUrl) +**Kind**: static method of [IotaVerificationMethod](#IotaVerificationMethod) | Param | Type | | --- | --- | | json | any | - + -## StardustDocument +## KeyPair **Kind**: global class -* [StardustDocument](#StardustDocument) - * [new StardustDocument(network)](#new_StardustDocument_new) +* [KeyPair](#KeyPair) + * [new KeyPair(type_)](#new_KeyPair_new) * _instance_ - * [.id()](#StardustDocument+id) ⇒ [StardustDID](#StardustDID) - * [.controller()](#StardustDocument+controller) ⇒ [Array.<StardustDID>](#StardustDID) - * [.alsoKnownAs()](#StardustDocument+alsoKnownAs) ⇒ Array.<string> - * [.setAlsoKnownAs(urls)](#StardustDocument+setAlsoKnownAs) - * [.properties()](#StardustDocument+properties) ⇒ Map.<string, any> - * [.setPropertyUnchecked(key, value)](#StardustDocument+setPropertyUnchecked) - * [.service()](#StardustDocument+service) ⇒ [Array.<StardustService>](#StardustService) - * [.insertService(service)](#StardustDocument+insertService) ⇒ boolean - * [.removeService(did)](#StardustDocument+removeService) ⇒ boolean - * [.resolveService(query)](#StardustDocument+resolveService) ⇒ [StardustService](#StardustService) \| undefined - * [.methods()](#StardustDocument+methods) ⇒ [Array.<StardustVerificationMethod>](#StardustVerificationMethod) - * [.insertMethod(method, scope)](#StardustDocument+insertMethod) - * [.removeMethod(did)](#StardustDocument+removeMethod) - * [.resolveMethod(query, scope)](#StardustDocument+resolveMethod) ⇒ [StardustVerificationMethod](#StardustVerificationMethod) \| undefined - * [.attachMethodRelationship(didUrl, relationship)](#StardustDocument+attachMethodRelationship) ⇒ boolean - * [.detachMethodRelationship(didUrl, relationship)](#StardustDocument+detachMethodRelationship) ⇒ boolean - * [.signCredential(credential, privateKey, methodQuery, options)](#StardustDocument+signCredential) ⇒ [Credential](#Credential) - * [.signPresentation(presentation, privateKey, methodQuery, options)](#StardustDocument+signPresentation) ⇒ [Presentation](#Presentation) - * [.signData(data, privateKey, methodQuery, options)](#StardustDocument+signData) ⇒ any - * [.verifyData(data, options)](#StardustDocument+verifyData) ⇒ boolean - * [.pack()](#StardustDocument+pack) ⇒ Uint8Array - * [.packWithEncoding(encoding)](#StardustDocument+packWithEncoding) ⇒ Uint8Array - * [.metadata()](#StardustDocument+metadata) ⇒ [StardustDocumentMetadata](#StardustDocumentMetadata) - * [.metadataCreated()](#StardustDocument+metadataCreated) ⇒ [Timestamp](#Timestamp) \| undefined - * [.setMetadataCreated(timestamp)](#StardustDocument+setMetadataCreated) - * [.metadataUpdated()](#StardustDocument+metadataUpdated) ⇒ [Timestamp](#Timestamp) \| undefined - * [.setMetadataUpdated(timestamp)](#StardustDocument+setMetadataUpdated) - * [.metadataDeactivated()](#StardustDocument+metadataDeactivated) ⇒ boolean \| undefined - * [.setMetadataDeactivated(deactivated)](#StardustDocument+setMetadataDeactivated) - * [.setMetadataPropertyUnchecked(key, value)](#StardustDocument+setMetadataPropertyUnchecked) - * [.revokeCredentials(serviceQuery, indices)](#StardustDocument+revokeCredentials) - * [.unrevokeCredentials(serviceQuery, indices)](#StardustDocument+unrevokeCredentials) - * [.toJSON()](#StardustDocument+toJSON) ⇒ any - * [.clone()](#StardustDocument+clone) ⇒ [StardustDocument](#StardustDocument) + * [.type()](#KeyPair+type) ⇒ number + * [.public()](#KeyPair+public) ⇒ Uint8Array + * [.private()](#KeyPair+private) ⇒ Uint8Array + * [.toJSON()](#KeyPair+toJSON) ⇒ any + * [.clone()](#KeyPair+clone) ⇒ [KeyPair](#KeyPair) * _static_ - * [.newWithId(id)](#StardustDocument.newWithId) ⇒ [StardustDocument](#StardustDocument) - * [.unpack(did, stateMetadata, allowEmpty)](#StardustDocument.unpack) ⇒ [StardustDocument](#StardustDocument) - * [.unpackFromBlock(network, block)](#StardustDocument.unpackFromBlock) ⇒ [Array.<StardustDocument>](#StardustDocument) - * [.fromJSON(json)](#StardustDocument.fromJSON) ⇒ [StardustDocument](#StardustDocument) + * [.fromKeys(type_, public_key, private_key)](#KeyPair.fromKeys) ⇒ [KeyPair](#KeyPair) + * [.tryFromPrivateKeyBytes(keyType, privateKeyBytes)](#KeyPair.tryFromPrivateKeyBytes) ⇒ [KeyPair](#KeyPair) + * [.fromJSON(json)](#KeyPair.fromJSON) ⇒ [KeyPair](#KeyPair) - + -### new StardustDocument(network) -Constructs an empty DID Document with a [placeholder](#StardustDID.placeholder) identifier -for the given `network`. +### new KeyPair(type_) +Generates a new `KeyPair` object. | Param | Type | | --- | --- | -| network | string | +| type_ | number | - + -### stardustDocument.id() ⇒ [StardustDID](#StardustDID) -Returns a copy of the DID Document `id`. +### keyPair.type() ⇒ number +Returns the `KeyType` of the `KeyPair` object. -**Kind**: instance method of [StardustDocument](#StardustDocument) - +**Kind**: instance method of [KeyPair](#KeyPair) + -### stardustDocument.controller() ⇒ [Array.<StardustDID>](#StardustDID) -Returns a copy of the list of document controllers. +### keyPair.public() ⇒ Uint8Array +Returns a copy of the public key as a `Uint8Array`. -NOTE: controllers are determined by the `state_controller` unlock condition of the output -during resolution and are omitted when publishing. +**Kind**: instance method of [KeyPair](#KeyPair) + -**Kind**: instance method of [StardustDocument](#StardustDocument) - +### keyPair.private() ⇒ Uint8Array +Returns a copy of the private key as a `Uint8Array`. -### stardustDocument.alsoKnownAs() ⇒ Array.<string> -Returns a copy of the document's `alsoKnownAs` set. +**Kind**: instance method of [KeyPair](#KeyPair) + + +### keyPair.toJSON() ⇒ any +Serializes a `KeyPair` object as a JSON object. -**Kind**: instance method of [StardustDocument](#StardustDocument) - +**Kind**: instance method of [KeyPair](#KeyPair) + -### stardustDocument.setAlsoKnownAs(urls) -Sets the `alsoKnownAs` property in the DID document. +### keyPair.clone() ⇒ [KeyPair](#KeyPair) +Deep clones the object. + +**Kind**: instance method of [KeyPair](#KeyPair) + + +### KeyPair.fromKeys(type_, public_key, private_key) ⇒ [KeyPair](#KeyPair) +Parses a `KeyPair` object from the public/private keys. -**Kind**: instance method of [StardustDocument](#StardustDocument) +**Kind**: static method of [KeyPair](#KeyPair) | Param | Type | | --- | --- | -| urls | string \| Array.<string> \| null | +| type_ | number | +| public_key | Uint8Array | +| private_key | Uint8Array | - + -### stardustDocument.properties() ⇒ Map.<string, any> -Returns a copy of the custom DID Document properties. +### KeyPair.tryFromPrivateKeyBytes(keyType, privateKeyBytes) ⇒ [KeyPair](#KeyPair) +Reconstructs a `KeyPair` from the bytes of a private key. -**Kind**: instance method of [StardustDocument](#StardustDocument) - +The private key for `Ed25519` must be a 32-byte seed in compliance +with [RFC 8032](https://datatracker.ietf.org/doc/html/rfc8032#section-3.2). +Other implementations often use another format. See [this blog post](https://blog.mozilla.org/warner/2011/11/29/ed25519-keys/) for further explanation. -### stardustDocument.setPropertyUnchecked(key, value) -Sets a custom property in the DID Document. -If the value is set to `null`, the custom property will be removed. +**Kind**: static method of [KeyPair](#KeyPair) -### WARNING -This method can overwrite existing properties like `id` and result in an invalid document. +| Param | Type | +| --- | --- | +| keyType | number | +| privateKeyBytes | Uint8Array | + + + +### KeyPair.fromJSON(json) ⇒ [KeyPair](#KeyPair) +Deserializes a `KeyPair` object from a JSON object. -**Kind**: instance method of [StardustDocument](#StardustDocument) +**Kind**: static method of [KeyPair](#KeyPair) | Param | Type | | --- | --- | -| key | string | -| value | any | +| json | any | + + + +## MethodData +Supported verification method data formats. + +**Kind**: global class + +* [MethodData](#MethodData) + * _instance_ + * [.tryDecode()](#MethodData+tryDecode) ⇒ Uint8Array + * [.toJSON()](#MethodData+toJSON) ⇒ any + * [.clone()](#MethodData+clone) ⇒ [MethodData](#MethodData) + * _static_ + * [.newBase58(data)](#MethodData.newBase58) ⇒ [MethodData](#MethodData) + * [.newMultibase(data)](#MethodData.newMultibase) ⇒ [MethodData](#MethodData) + * [.fromJSON(json)](#MethodData.fromJSON) ⇒ [MethodData](#MethodData) + + + +### methodData.tryDecode() ⇒ Uint8Array +Returns a `Uint8Array` containing the decoded bytes of the `MethodData`. - +This is generally a public key identified by a `MethodData` value. + +### Errors +Decoding can fail if `MethodData` has invalid content or cannot be +represented as a vector of bytes. + +**Kind**: instance method of [MethodData](#MethodData) + -### stardustDocument.service() ⇒ [Array.<StardustService>](#StardustService) -Return a set of all [StardustService](#StardustService) in the document. +### methodData.toJSON() ⇒ any +Serializes this to a JSON object. + +**Kind**: instance method of [MethodData](#MethodData) + -**Kind**: instance method of [StardustDocument](#StardustDocument) - +### methodData.clone() ⇒ [MethodData](#MethodData) +Deep clones the object. -### stardustDocument.insertService(service) ⇒ boolean -Add a new [StardustService](#StardustService) to the document. +**Kind**: instance method of [MethodData](#MethodData) + -Returns `true` if the service was added. +### MethodData.newBase58(data) ⇒ [MethodData](#MethodData) +Creates a new `MethodData` variant with Base58-BTC encoded content. -**Kind**: instance method of [StardustDocument](#StardustDocument) +**Kind**: static method of [MethodData](#MethodData) | Param | Type | | --- | --- | -| service | [StardustService](#StardustService) | - - +| data | Uint8Array | -### stardustDocument.removeService(did) ⇒ boolean -Remove a [StardustService](#StardustService) identified by the given [StardustDIDUrl](#StardustDIDUrl) from the document. + -Returns `true` if a service was removed. +### MethodData.newMultibase(data) ⇒ [MethodData](#MethodData) +Creates a new `MethodData` variant with Multibase-encoded content. -**Kind**: instance method of [StardustDocument](#StardustDocument) +**Kind**: static method of [MethodData](#MethodData) | Param | Type | | --- | --- | -| did | [StardustDIDUrl](#StardustDIDUrl) | +| data | Uint8Array | - + -### stardustDocument.resolveService(query) ⇒ [StardustService](#StardustService) \| undefined -Returns the first [StardustService](#StardustService) with an `id` property matching the provided `query`, -if present. +### MethodData.fromJSON(json) ⇒ [MethodData](#MethodData) +Deserializes an instance from a JSON object. -**Kind**: instance method of [StardustDocument](#StardustDocument) +**Kind**: static method of [MethodData](#MethodData) | Param | Type | | --- | --- | -| query | [StardustDIDUrl](#StardustDIDUrl) \| string | +| json | any | - + -### stardustDocument.methods() ⇒ [Array.<StardustVerificationMethod>](#StardustVerificationMethod) -Returns a list of all [StardustVerificationMethod](#StardustVerificationMethod) in the DID Document. +## MethodScope +Supported verification method types. -**Kind**: instance method of [StardustDocument](#StardustDocument) - +**Kind**: global class -### stardustDocument.insertMethod(method, scope) -Adds a new `method` to the document in the given `scope`. +* [MethodScope](#MethodScope) + * _instance_ + * [.toString()](#MethodScope+toString) ⇒ string + * [.toJSON()](#MethodScope+toJSON) ⇒ any + * [.clone()](#MethodScope+clone) ⇒ [MethodScope](#MethodScope) + * _static_ + * [.VerificationMethod()](#MethodScope.VerificationMethod) ⇒ [MethodScope](#MethodScope) + * [.Authentication()](#MethodScope.Authentication) ⇒ [MethodScope](#MethodScope) + * [.AssertionMethod()](#MethodScope.AssertionMethod) ⇒ [MethodScope](#MethodScope) + * [.KeyAgreement()](#MethodScope.KeyAgreement) ⇒ [MethodScope](#MethodScope) + * [.CapabilityDelegation()](#MethodScope.CapabilityDelegation) ⇒ [MethodScope](#MethodScope) + * [.CapabilityInvocation()](#MethodScope.CapabilityInvocation) ⇒ [MethodScope](#MethodScope) + * [.fromJSON(json)](#MethodScope.fromJSON) ⇒ [MethodScope](#MethodScope) -**Kind**: instance method of [StardustDocument](#StardustDocument) + -| Param | Type | -| --- | --- | -| method | [StardustVerificationMethod](#StardustVerificationMethod) | -| scope | [MethodScope](#MethodScope) | +### methodScope.toString() ⇒ string +Returns the `MethodScope` as a string. - +**Kind**: instance method of [MethodScope](#MethodScope) + -### stardustDocument.removeMethod(did) -Removes all references to the specified Verification Method. +### methodScope.toJSON() ⇒ any +Serializes this to a JSON object. -**Kind**: instance method of [StardustDocument](#StardustDocument) +**Kind**: instance method of [MethodScope](#MethodScope) + -| Param | Type | -| --- | --- | -| did | [StardustDIDUrl](#StardustDIDUrl) | +### methodScope.clone() ⇒ [MethodScope](#MethodScope) +Deep clones the object. + +**Kind**: instance method of [MethodScope](#MethodScope) + - +### MethodScope.VerificationMethod() ⇒ [MethodScope](#MethodScope) +**Kind**: static method of [MethodScope](#MethodScope) + -### stardustDocument.resolveMethod(query, scope) ⇒ [StardustVerificationMethod](#StardustVerificationMethod) \| undefined -Returns a copy of the first verification method with an `id` property -matching the provided `query` and the verification relationship -specified by `scope`, if present. +### MethodScope.Authentication() ⇒ [MethodScope](#MethodScope) +**Kind**: static method of [MethodScope](#MethodScope) + -**Kind**: instance method of [StardustDocument](#StardustDocument) +### MethodScope.AssertionMethod() ⇒ [MethodScope](#MethodScope) +**Kind**: static method of [MethodScope](#MethodScope) + -| Param | Type | -| --- | --- | -| query | [StardustDIDUrl](#StardustDIDUrl) \| string | -| scope | [MethodScope](#MethodScope) \| undefined | +### MethodScope.KeyAgreement() ⇒ [MethodScope](#MethodScope) +**Kind**: static method of [MethodScope](#MethodScope) + - +### MethodScope.CapabilityDelegation() ⇒ [MethodScope](#MethodScope) +**Kind**: static method of [MethodScope](#MethodScope) + -### stardustDocument.attachMethodRelationship(didUrl, relationship) ⇒ boolean -Attaches the relationship to the given method, if the method exists. +### MethodScope.CapabilityInvocation() ⇒ [MethodScope](#MethodScope) +**Kind**: static method of [MethodScope](#MethodScope) + -Note: The method needs to be in the set of verification methods, -so it cannot be an embedded one. +### MethodScope.fromJSON(json) ⇒ [MethodScope](#MethodScope) +Deserializes an instance from a JSON object. -**Kind**: instance method of [StardustDocument](#StardustDocument) +**Kind**: static method of [MethodScope](#MethodScope) | Param | Type | | --- | --- | -| didUrl | [StardustDIDUrl](#StardustDIDUrl) | -| relationship | number | - - +| json | any | -### stardustDocument.detachMethodRelationship(didUrl, relationship) ⇒ boolean -Detaches the given relationship from the given method, if the method exists. + -**Kind**: instance method of [StardustDocument](#StardustDocument) +## MethodType +Supported verification method types. -| Param | Type | -| --- | --- | -| didUrl | [StardustDIDUrl](#StardustDIDUrl) | -| relationship | number | +**Kind**: global class - +* [MethodType](#MethodType) + * _instance_ + * [.toString()](#MethodType+toString) ⇒ string + * [.toJSON()](#MethodType+toJSON) ⇒ any + * [.clone()](#MethodType+clone) ⇒ [MethodType](#MethodType) + * _static_ + * [.Ed25519VerificationKey2018()](#MethodType.Ed25519VerificationKey2018) ⇒ [MethodType](#MethodType) + * [.X25519KeyAgreementKey2019()](#MethodType.X25519KeyAgreementKey2019) ⇒ [MethodType](#MethodType) + * [.fromJSON(json)](#MethodType.fromJSON) ⇒ [MethodType](#MethodType) -### stardustDocument.signCredential(credential, privateKey, methodQuery, options) ⇒ [Credential](#Credential) -Creates a signature for the given `Credential` with the specified DID Document -Verification Method. + -**Kind**: instance method of [StardustDocument](#StardustDocument) +### methodType.toString() ⇒ string +Returns the `MethodType` as a string. -| Param | Type | -| --- | --- | -| credential | [Credential](#Credential) | -| privateKey | Uint8Array | -| methodQuery | [StardustDIDUrl](#StardustDIDUrl) \| string | -| options | [ProofOptions](#ProofOptions) | +**Kind**: instance method of [MethodType](#MethodType) + - +### methodType.toJSON() ⇒ any +Serializes this to a JSON object. -### stardustDocument.signPresentation(presentation, privateKey, methodQuery, options) ⇒ [Presentation](#Presentation) -Creates a signature for the given `Presentation` with the specified DID Document -Verification Method. +**Kind**: instance method of [MethodType](#MethodType) + -**Kind**: instance method of [StardustDocument](#StardustDocument) +### methodType.clone() ⇒ [MethodType](#MethodType) +Deep clones the object. -| Param | Type | -| --- | --- | -| presentation | [Presentation](#Presentation) | -| privateKey | Uint8Array | -| methodQuery | [StardustDIDUrl](#StardustDIDUrl) \| string | -| options | [ProofOptions](#ProofOptions) | +**Kind**: instance method of [MethodType](#MethodType) + - +### MethodType.Ed25519VerificationKey2018() ⇒ [MethodType](#MethodType) +**Kind**: static method of [MethodType](#MethodType) + -### stardustDocument.signData(data, privateKey, methodQuery, options) ⇒ any -Creates a signature for the given `data` with the specified DID Document -Verification Method. +### MethodType.X25519KeyAgreementKey2019() ⇒ [MethodType](#MethodType) +**Kind**: static method of [MethodType](#MethodType) + -NOTE: use `signSelf` or `signDocument` for DID Documents. +### MethodType.fromJSON(json) ⇒ [MethodType](#MethodType) +Deserializes an instance from a JSON object. -**Kind**: instance method of [StardustDocument](#StardustDocument) +**Kind**: static method of [MethodType](#MethodType) | Param | Type | | --- | --- | -| data | any | -| privateKey | Uint8Array | -| methodQuery | [StardustDIDUrl](#StardustDIDUrl) \| string | -| options | [ProofOptions](#ProofOptions) | +| json | any | - + -### stardustDocument.verifyData(data, options) ⇒ boolean -Verifies the authenticity of `data` using the target verification method. +## MixedResolver +Convenience type for resolving DID documents from different DID methods. -**Kind**: instance method of [StardustDocument](#StardustDocument) +Also provides methods for resolving DID Documents associated with +verifiable `Credentials` and `Presentations`. -| Param | Type | -| --- | --- | -| data | any | -| options | [VerifierOptions](#VerifierOptions) | +# Configuration +The resolver will only be able to resolve DID documents for methods it has been configured for in the constructor. - +**Kind**: global class -### stardustDocument.pack() ⇒ Uint8Array -Serializes the document for inclusion in an Alias Output's state metadata -with the default [StateMetadataEncoding](#StateMetadataEncoding). +* [MixedResolver](#MixedResolver) + * [new MixedResolver(config)](#new_MixedResolver_new) + * [.resolvePresentationIssuers(presentation)](#MixedResolver+resolvePresentationIssuers) ⇒ Promise.<Array.<(IotaDocument\|CoreDocument)>> + * [.resolvePresentationHolder(presentation)](#MixedResolver+resolvePresentationHolder) ⇒ Promise.<(IotaDocument\|CoreDocument)> + * [.verifyPresentation(presentation, options, fail_fast, holder, issuers)](#MixedResolver+verifyPresentation) ⇒ Promise.<void> + * [.resolve(did)](#MixedResolver+resolve) ⇒ Promise.<(IotaDocument\|CoreDocument)> -**Kind**: instance method of [StardustDocument](#StardustDocument) - + -### stardustDocument.packWithEncoding(encoding) ⇒ Uint8Array -Serializes the document for inclusion in an Alias Output's state metadata. +### new MixedResolver(config) +Constructs a new `MixedResolver`. + +# Errors +If both a `client` is given and the `handlers` map contains the "iota" key the construction process +will throw an error as it is then ambiguous what should be . -**Kind**: instance method of [StardustDocument](#StardustDocument) | Param | Type | | --- | --- | -| encoding | number | +| config | ResolverConfig | - + -### stardustDocument.metadata() ⇒ [StardustDocumentMetadata](#StardustDocumentMetadata) -Returns a copy of the metadata associated with this document. +### mixedResolver.resolvePresentationIssuers(presentation) ⇒ Promise.<Array.<(IotaDocument\|CoreDocument)>> +Fetches all DID Documents of `Credential` issuers contained in a `Presentation`. +Issuer documents are returned in arbitrary order. -NOTE: Copies all the metadata. See also `metadataCreated`, `metadataUpdated`, -`metadataPreviousMessageId`, `metadataProof` if only a subset of the metadata required. +# Errors +Errors if any issuer URL cannot be parsed to a DID whose associated method is supported by this Resolver, or +resolution fails. -**Kind**: instance method of [StardustDocument](#StardustDocument) - +**Kind**: instance method of [MixedResolver](#MixedResolver) -### stardustDocument.metadataCreated() ⇒ [Timestamp](#Timestamp) \| undefined -Returns a copy of the timestamp of when the DID document was created. +| Param | Type | +| --- | --- | +| presentation | [Presentation](#Presentation) | -**Kind**: instance method of [StardustDocument](#StardustDocument) - + -### stardustDocument.setMetadataCreated(timestamp) -Sets the timestamp of when the DID document was created. +### mixedResolver.resolvePresentationHolder(presentation) ⇒ Promise.<(IotaDocument\|CoreDocument)> +Fetches the DID Document of the holder of a `Presentation`. + +# Errors +Errors if the holder URL is missing, cannot be parsed to a valid DID whose method is supported by the resolver, or +DID resolution fails. -**Kind**: instance method of [StardustDocument](#StardustDocument) +**Kind**: instance method of [MixedResolver](#MixedResolver) | Param | Type | | --- | --- | -| timestamp | [Timestamp](#Timestamp) \| undefined | +| presentation | [Presentation](#Presentation) | - + -### stardustDocument.metadataUpdated() ⇒ [Timestamp](#Timestamp) \| undefined -Returns a copy of the timestamp of the last DID document update. +### mixedResolver.verifyPresentation(presentation, options, fail_fast, holder, issuers) ⇒ Promise.<void> +Verifies a `Presentation`. + +### Important +See `PresentationValidator::validate` for information about which properties get +validated and what is expected of the optional arguments `holder` and `issuer`. -**Kind**: instance method of [StardustDocument](#StardustDocument) - +### Resolution +The DID Documents for the `holder` and `issuers` are optionally resolved if not given. +If you already have up-to-date versions of these DID Documents, you may want +to use `PresentationValidator::validate`. +See also `Resolver::resolvePresentationIssuers` and `Resolver::resolvePresentationHolder`. -### stardustDocument.setMetadataUpdated(timestamp) -Sets the timestamp of the last DID document update. +### Errors +Errors from resolving the holder and issuer DID Documents, if not provided, will be returned immediately. +Otherwise, errors from validating the presentation and its credentials will be returned +according to the `fail_fast` parameter. -**Kind**: instance method of [StardustDocument](#StardustDocument) +**Kind**: instance method of [MixedResolver](#MixedResolver) | Param | Type | | --- | --- | -| timestamp | [Timestamp](#Timestamp) \| undefined | +| presentation | [Presentation](#Presentation) | +| options | [PresentationValidationOptions](#PresentationValidationOptions) | +| fail_fast | number | +| holder | [IotaDocument](#IotaDocument) \| [CoreDocument](#CoreDocument) \| undefined | +| issuers | Array.<(IotaDocument\|CoreDocument)> \| undefined | - + -### stardustDocument.metadataDeactivated() ⇒ boolean \| undefined -Returns a copy of the deactivated status of the DID document. +### mixedResolver.resolve(did) ⇒ Promise.<(IotaDocument\|CoreDocument)> +Fetches the DID Document of the given DID. -**Kind**: instance method of [StardustDocument](#StardustDocument) - +### Errors -### stardustDocument.setMetadataDeactivated(deactivated) -Sets the deactivated status of the DID document. +Errors if the resolver has not been configured to handle the method +corresponding to the given DID or the resolution process itself fails. -**Kind**: instance method of [StardustDocument](#StardustDocument) +**Kind**: instance method of [MixedResolver](#MixedResolver) | Param | Type | | --- | --- | -| deactivated | boolean \| undefined | +| did | string | + + + +## Presentation +**Kind**: global class + +* [Presentation](#Presentation) + * [new Presentation(values)](#new_Presentation_new) + * _instance_ + * [.context()](#Presentation+context) ⇒ Array.<(string\|Record.<string, any>)> + * [.id()](#Presentation+id) ⇒ string \| undefined + * [.type()](#Presentation+type) ⇒ Array.<string> + * [.verifiableCredential()](#Presentation+verifiableCredential) ⇒ [Array.<Credential>](#Credential) + * [.holder()](#Presentation+holder) ⇒ string \| undefined + * [.refreshService()](#Presentation+refreshService) ⇒ Array.<RefreshService> + * [.termsOfUse()](#Presentation+termsOfUse) ⇒ Array.<Policy> + * [.proof()](#Presentation+proof) ⇒ [Proof](#Proof) \| undefined + * [.properties()](#Presentation+properties) ⇒ Map.<string, any> + * [.toJSON()](#Presentation+toJSON) ⇒ any + * [.clone()](#Presentation+clone) ⇒ [Presentation](#Presentation) + * _static_ + * [.BaseContext()](#Presentation.BaseContext) ⇒ string + * [.BaseType()](#Presentation.BaseType) ⇒ string + * [.fromJSON(json)](#Presentation.fromJSON) ⇒ [Presentation](#Presentation) - + -### stardustDocument.setMetadataPropertyUnchecked(key, value) -Sets a custom property in the document metadata. -If the value is set to `null`, the custom property will be removed. +### new Presentation(values) +Constructs a new `Presentation`. -**Kind**: instance method of [StardustDocument](#StardustDocument) | Param | Type | | --- | --- | -| key | string | -| value | any | - - +| values | IPresentation | -### stardustDocument.revokeCredentials(serviceQuery, indices) -If the document has a `RevocationBitmap` service identified by `serviceQuery`, -revoke all specified `indices`. + -**Kind**: instance method of [StardustDocument](#StardustDocument) +### presentation.context() ⇒ Array.<(string\|Record.<string, any>)> +Returns a copy of the JSON-LD context(s) applicable to the `Presentation`. -| Param | Type | -| --- | --- | -| serviceQuery | [StardustDIDUrl](#StardustDIDUrl) \| string | -| indices | number \| Array.<number> | +**Kind**: instance method of [Presentation](#Presentation) + - +### presentation.id() ⇒ string \| undefined +Returns a copy of the unique `URI` identifying the `Presentation`. -### stardustDocument.unrevokeCredentials(serviceQuery, indices) -If the document has a `RevocationBitmap` service identified by `serviceQuery`, -unrevoke all specified `indices`. +**Kind**: instance method of [Presentation](#Presentation) + -**Kind**: instance method of [StardustDocument](#StardustDocument) +### presentation.type() ⇒ Array.<string> +Returns a copy of the URIs defining the type of the `Presentation`. -| Param | Type | -| --- | --- | -| serviceQuery | [StardustDIDUrl](#StardustDIDUrl) \| string | -| indices | number \| Array.<number> | +**Kind**: instance method of [Presentation](#Presentation) + - +### presentation.verifiableCredential() ⇒ [Array.<Credential>](#Credential) +Returns a copy of the [Credential](#Credential)(s) expressing the claims of the `Presentation`. -### stardustDocument.toJSON() ⇒ any -Serializes this to a JSON object. +**Kind**: instance method of [Presentation](#Presentation) + -**Kind**: instance method of [StardustDocument](#StardustDocument) - +### presentation.holder() ⇒ string \| undefined +Returns a copy of the URI of the entity that generated the `Presentation`. -### stardustDocument.clone() ⇒ [StardustDocument](#StardustDocument) -Deep clones the object. +**Kind**: instance method of [Presentation](#Presentation) + -**Kind**: instance method of [StardustDocument](#StardustDocument) - +### presentation.refreshService() ⇒ Array.<RefreshService> +Returns a copy of the service(s) used to refresh an expired [Credential](#Credential) in the `Presentation`. -### StardustDocument.newWithId(id) ⇒ [StardustDocument](#StardustDocument) -Constructs an empty DID Document with the given identifier. +**Kind**: instance method of [Presentation](#Presentation) + -**Kind**: static method of [StardustDocument](#StardustDocument) +### presentation.termsOfUse() ⇒ Array.<Policy> +Returns a copy of the terms-of-use specified by the `Presentation` holder -| Param | Type | -| --- | --- | -| id | [StardustDID](#StardustDID) | +**Kind**: instance method of [Presentation](#Presentation) + - +### presentation.proof() ⇒ [Proof](#Proof) \| undefined +Returns a copy of the proof used to verify the `Presentation`. -### StardustDocument.unpack(did, stateMetadata, allowEmpty) ⇒ [StardustDocument](#StardustDocument) -Deserializes the document from the state metadata bytes of an Alias Output. +**Kind**: instance method of [Presentation](#Presentation) + -If `allowEmpty` is true, this will return an empty DID document marked as `deactivated` -if `stateMetadata` is empty. +### presentation.properties() ⇒ Map.<string, any> +Returns a copy of the miscellaneous properties on the `Presentation`. -NOTE: `did` is required since it is omitted from the serialized DID Document and -cannot be inferred from the state metadata. It also indicates the network, which is not -encoded in the `AliasId` alone. +**Kind**: instance method of [Presentation](#Presentation) + -**Kind**: static method of [StardustDocument](#StardustDocument) +### presentation.toJSON() ⇒ any +Serializes this to a JSON object. -| Param | Type | -| --- | --- | -| did | [StardustDID](#StardustDID) | -| stateMetadata | Uint8Array | -| allowEmpty | boolean | +**Kind**: instance method of [Presentation](#Presentation) + - +### presentation.clone() ⇒ [Presentation](#Presentation) +Deep clones the object. -### StardustDocument.unpackFromBlock(network, block) ⇒ [Array.<StardustDocument>](#StardustDocument) -Returns all DID documents of the Alias Outputs contained in the block's transaction payload -outputs, if any. +**Kind**: instance method of [Presentation](#Presentation) + -Errors if any Alias Output does not contain a valid or empty DID Document. +### Presentation.BaseContext() ⇒ string +Returns the base JSON-LD context. -**Kind**: static method of [StardustDocument](#StardustDocument) +**Kind**: static method of [Presentation](#Presentation) + -| Param | Type | -| --- | --- | -| network | string | -| block | IBlock | +### Presentation.BaseType() ⇒ string +Returns the base type. - +**Kind**: static method of [Presentation](#Presentation) + -### StardustDocument.fromJSON(json) ⇒ [StardustDocument](#StardustDocument) +### Presentation.fromJSON(json) ⇒ [Presentation](#Presentation) Deserializes an instance from a JSON object. -**Kind**: static method of [StardustDocument](#StardustDocument) +**Kind**: static method of [Presentation](#Presentation) | Param | Type | | --- | --- | | json | any | - + -## StardustDocumentMetadata -Additional attributes related to an IOTA DID Document. +## PresentationValidationOptions +Options to declare validation criteria when validating presentation. **Kind**: global class -* [StardustDocumentMetadata](#StardustDocumentMetadata) +* [PresentationValidationOptions](#PresentationValidationOptions) + * [new PresentationValidationOptions(options)](#new_PresentationValidationOptions_new) * _instance_ - * [.created()](#StardustDocumentMetadata+created) ⇒ [Timestamp](#Timestamp) \| undefined - * [.updated()](#StardustDocumentMetadata+updated) ⇒ [Timestamp](#Timestamp) \| undefined - * [.deactivated()](#StardustDocumentMetadata+deactivated) ⇒ boolean \| undefined - * [.properties()](#StardustDocumentMetadata+properties) ⇒ Map.<string, any> - * [.toJSON()](#StardustDocumentMetadata+toJSON) ⇒ any - * [.clone()](#StardustDocumentMetadata+clone) ⇒ [StardustDocumentMetadata](#StardustDocumentMetadata) + * [.toJSON()](#PresentationValidationOptions+toJSON) ⇒ any + * [.clone()](#PresentationValidationOptions+clone) ⇒ [PresentationValidationOptions](#PresentationValidationOptions) * _static_ - * [.fromJSON(json)](#StardustDocumentMetadata.fromJSON) ⇒ [StardustDocumentMetadata](#StardustDocumentMetadata) - - - -### stardustDocumentMetadata.created() ⇒ [Timestamp](#Timestamp) \| undefined -Returns a copy of the timestamp of when the DID document was created. - -**Kind**: instance method of [StardustDocumentMetadata](#StardustDocumentMetadata) - + * [.default()](#PresentationValidationOptions.default) ⇒ [PresentationValidationOptions](#PresentationValidationOptions) + * [.fromJSON(json)](#PresentationValidationOptions.fromJSON) ⇒ [PresentationValidationOptions](#PresentationValidationOptions) -### stardustDocumentMetadata.updated() ⇒ [Timestamp](#Timestamp) \| undefined -Returns a copy of the timestamp of the last DID document update. + -**Kind**: instance method of [StardustDocumentMetadata](#StardustDocumentMetadata) - +### new PresentationValidationOptions(options) +Creates a new `PresentationValidationOptions` from the given fields. -### stardustDocumentMetadata.deactivated() ⇒ boolean \| undefined -Returns a copy of the deactivated status of the DID document. +Throws an error if any of the options are invalid. -**Kind**: instance method of [StardustDocumentMetadata](#StardustDocumentMetadata) - -### stardustDocumentMetadata.properties() ⇒ Map.<string, any> -Returns a copy of the custom metadata properties. +| Param | Type | +| --- | --- | +| options | IPresentationValidationOptions | -**Kind**: instance method of [StardustDocumentMetadata](#StardustDocumentMetadata) - + -### stardustDocumentMetadata.toJSON() ⇒ any +### presentationValidationOptions.toJSON() ⇒ any Serializes this to a JSON object. -**Kind**: instance method of [StardustDocumentMetadata](#StardustDocumentMetadata) - +**Kind**: instance method of [PresentationValidationOptions](#PresentationValidationOptions) + -### stardustDocumentMetadata.clone() ⇒ [StardustDocumentMetadata](#StardustDocumentMetadata) +### presentationValidationOptions.clone() ⇒ [PresentationValidationOptions](#PresentationValidationOptions) Deep clones the object. -**Kind**: instance method of [StardustDocumentMetadata](#StardustDocumentMetadata) - +**Kind**: instance method of [PresentationValidationOptions](#PresentationValidationOptions) + + +### PresentationValidationOptions.default() ⇒ [PresentationValidationOptions](#PresentationValidationOptions) +Creates a new `PresentationValidationOptions` with defaults. + +**Kind**: static method of [PresentationValidationOptions](#PresentationValidationOptions) + -### StardustDocumentMetadata.fromJSON(json) ⇒ [StardustDocumentMetadata](#StardustDocumentMetadata) +### PresentationValidationOptions.fromJSON(json) ⇒ [PresentationValidationOptions](#PresentationValidationOptions) Deserializes an instance from a JSON object. -**Kind**: static method of [StardustDocumentMetadata](#StardustDocumentMetadata) +**Kind**: static method of [PresentationValidationOptions](#PresentationValidationOptions) | Param | Type | | --- | --- | | json | any | - - -## StardustIdentityClientExt -An extension interface that provides helper functions for publication -and resolution of DID documents in Alias Outputs. + +## PresentationValidator **Kind**: global class -* [StardustIdentityClientExt](#StardustIdentityClientExt) - * [.newDidOutput(client, address, document, rentStructure)](#StardustIdentityClientExt.newDidOutput) ⇒ Promise.<IAliasOutput> - * [.updateDidOutput(client, document)](#StardustIdentityClientExt.updateDidOutput) ⇒ Promise.<IAliasOutput> - * [.deactivateDidOutput(client, did)](#StardustIdentityClientExt.deactivateDidOutput) ⇒ Promise.<IAliasOutput> - * [.resolveDid(client, did)](#StardustIdentityClientExt.resolveDid) ⇒ [Promise.<StardustDocument>](#StardustDocument) - * [.resolveDidOutput(client, did)](#StardustIdentityClientExt.resolveDidOutput) ⇒ Promise.<IAliasOutput> - - - -### StardustIdentityClientExt.newDidOutput(client, address, document, rentStructure) ⇒ Promise.<IAliasOutput> -Create a DID with a new Alias Output containing the given `document`. +* [PresentationValidator](#PresentationValidator) + * [.validate(presentation, holder, issuers, options, fail_fast)](#PresentationValidator.validate) + * [.verifyPresentationSignature(presentation, holder, options)](#PresentationValidator.verifyPresentationSignature) + * [.checkStructure(presentation)](#PresentationValidator.checkStructure) + * [.extractHolder(presentation)](#PresentationValidator.extractHolder) ⇒ [CoreDID](#CoreDID) \| [IotaDID](#IotaDID) -The `address` will be set as the state controller and governor unlock conditions. -The minimum required token deposit amount will be set according to the given -`rent_structure`, which will be fetched from the node if not provided. -The returned Alias Output can be further customised before publication, if desired. + -NOTE: this does *not* publish the Alias Output. +### PresentationValidator.validate(presentation, holder, issuers, options, fail_fast) +Validate a `Presentation`. -**Kind**: static method of [StardustIdentityClientExt](#StardustIdentityClientExt) +The following properties are validated according to `options`: +- the semantic structure of the presentation, +- the holder's signature, +- the relationship between the holder and the credential subjects, +- the signatures and some properties of the constituent credentials (see +`CredentialValidator::validate`). -| Param | Type | -| --- | --- | -| client | IStardustIdentityClient | -| address | AddressTypes | -| document | [StardustDocument](#StardustDocument) | -| rentStructure | IRent \| undefined | +### Warning +The lack of an error returned from this method is in of itself not enough to conclude that the presentation can be +trusted. This section contains more information on additional checks that should be carried out before and after +calling this method. - +#### The state of the supplied DID Documents. +The caller must ensure that the DID Documents in `holder` and `issuers` are up-to-date. The convenience methods +`Resolver::resolve_presentation_holder` and `Resolver::resolve_presentation_issuers` +can help extract the latest available states of these DID Documents. -### StardustIdentityClientExt.updateDidOutput(client, document) ⇒ Promise.<IAliasOutput> -Fetches the associated Alias Output and updates it with `document` in its state metadata. -The storage deposit on the output is left unchanged. If the size of the document increased, -the amount should be increased manually. +#### Properties that are not validated + There are many properties defined in [The Verifiable Credentials Data Model](https://www.w3.org/TR/vc-data-model/) that are **not** validated, such as: +`credentialStatus`, `type`, `credentialSchema`, `refreshService`, **and more**. +These should be manually checked after validation, according to your requirements. -NOTE: this does *not* publish the updated Alias Output. +### Errors +An error is returned whenever a validated condition is not satisfied. -**Kind**: static method of [StardustIdentityClientExt](#StardustIdentityClientExt) +**Kind**: static method of [PresentationValidator](#PresentationValidator) | Param | Type | | --- | --- | -| client | IStardustIdentityClient | -| document | [StardustDocument](#StardustDocument) | +| presentation | [Presentation](#Presentation) | +| holder | [IotaDocument](#IotaDocument) \| [CoreDocument](#CoreDocument) | +| issuers | Array.<(IotaDocument\|CoreDocument)> | +| options | [PresentationValidationOptions](#PresentationValidationOptions) | +| fail_fast | number | - + -### StardustIdentityClientExt.deactivateDidOutput(client, did) ⇒ Promise.<IAliasOutput> -Removes the DID document from the state metadata of its Alias Output, -effectively deactivating it. The storage deposit on the output is left unchanged, -and should be reallocated manually. +### PresentationValidator.verifyPresentationSignature(presentation, holder, options) +Verify the presentation's signature using the resolved document of the holder. -Deactivating does not destroy the output. Hence, it can be re-activated by publishing -an update containing a DID document. +### Warning +The caller must ensure that the DID Document of the holder is up-to-date. -NOTE: this does *not* publish the updated Alias Output. +### Errors +Fails if the `holder` does not match the `presentation`'s holder property. +Fails if signature verification against the holder document fails. -**Kind**: static method of [StardustIdentityClientExt](#StardustIdentityClientExt) +**Kind**: static method of [PresentationValidator](#PresentationValidator) | Param | Type | | --- | --- | -| client | IStardustIdentityClient | -| did | [StardustDID](#StardustDID) | +| presentation | [Presentation](#Presentation) | +| holder | [IotaDocument](#IotaDocument) \| [CoreDocument](#CoreDocument) | +| options | [VerifierOptions](#VerifierOptions) | - + -### StardustIdentityClientExt.resolveDid(client, did) ⇒ [Promise.<StardustDocument>](#StardustDocument) -Resolve a [StardustDocument](#StardustDocument). Returns an empty, deactivated document if the state metadata -of the Alias Output is empty. +### PresentationValidator.checkStructure(presentation) +Validates the semantic structure of the `Presentation`. -**Kind**: static method of [StardustIdentityClientExt](#StardustIdentityClientExt) +**Kind**: static method of [PresentationValidator](#PresentationValidator) | Param | Type | | --- | --- | -| client | IStardustIdentityClient | -| did | [StardustDID](#StardustDID) | +| presentation | [Presentation](#Presentation) | - + -### StardustIdentityClientExt.resolveDidOutput(client, did) ⇒ Promise.<IAliasOutput> -Fetches the `IAliasOutput` associated with the given DID. +### PresentationValidator.extractHolder(presentation) ⇒ [CoreDID](#CoreDID) \| [IotaDID](#IotaDID) +Utility for extracting the holder field of a `Presentation` as a DID. -**Kind**: static method of [StardustIdentityClientExt](#StardustIdentityClientExt) +### Errors + +Fails if the holder field is missing or not a valid DID. + +**Kind**: static method of [PresentationValidator](#PresentationValidator) | Param | Type | | --- | --- | -| client | IStardustIdentityClient | -| did | [StardustDID](#StardustDID) | +| presentation | [Presentation](#Presentation) | + + - +## Proof +A digital signature. -## StardustService -A `Service` adhering to the IOTA UTXO DID method specification. +For field definitions see: https://w3c-ccg.github.io/security-vocab/ **Kind**: global class -* [StardustService](#StardustService) - * [new StardustService(service)](#new_StardustService_new) +* [Proof](#Proof) * _instance_ - * [.id()](#StardustService+id) ⇒ [StardustDIDUrl](#StardustDIDUrl) - * [.type()](#StardustService+type) ⇒ Array.<string> - * [.serviceEndpoint()](#StardustService+serviceEndpoint) ⇒ string \| Array.<string> \| Map.<string, Array.<string>> - * [.properties()](#StardustService+properties) ⇒ Map.<string, any> - * [.toJSON()](#StardustService+toJSON) ⇒ any - * [.clone()](#StardustService+clone) ⇒ [StardustService](#StardustService) + * [.type()](#Proof+type) ⇒ string + * [.value()](#Proof+value) ⇒ string + * [.verificationMethod()](#Proof+verificationMethod) ⇒ string + * [.created()](#Proof+created) ⇒ [Timestamp](#Timestamp) \| undefined + * [.expires()](#Proof+expires) ⇒ [Timestamp](#Timestamp) \| undefined + * [.challenge()](#Proof+challenge) ⇒ string \| undefined + * [.domain()](#Proof+domain) ⇒ string \| undefined + * [.purpose()](#Proof+purpose) ⇒ [ProofPurpose](#ProofPurpose) \| undefined + * [.toJSON()](#Proof+toJSON) ⇒ any + * [.clone()](#Proof+clone) ⇒ [Proof](#Proof) * _static_ - * [.fromJSON(json)](#StardustService.fromJSON) ⇒ [StardustService](#StardustService) + * [.fromJSON(json)](#Proof.fromJSON) ⇒ [Proof](#Proof) + + + +### proof.type() ⇒ string +Returns a copy of the proof type. + +**Kind**: instance method of [Proof](#Proof) + + +### proof.value() ⇒ string +Returns a copy of the proof value string. + +**Kind**: instance method of [Proof](#Proof) + - +### proof.verificationMethod() ⇒ string +Returns a copy of the identifier of the DID method used to create this proof. -### new StardustService(service) +**Kind**: instance method of [Proof](#Proof) + -| Param | Type | -| --- | --- | -| service | IStardustService | +### proof.created() ⇒ [Timestamp](#Timestamp) \| undefined +When the proof was generated. - +**Kind**: instance method of [Proof](#Proof) + -### stardustService.id() ⇒ [StardustDIDUrl](#StardustDIDUrl) -Returns a copy of the `Service` id. +### proof.expires() ⇒ [Timestamp](#Timestamp) \| undefined +When the proof expires. -**Kind**: instance method of [StardustService](#StardustService) - +**Kind**: instance method of [Proof](#Proof) + -### stardustService.type() ⇒ Array.<string> -Returns a copy of the `Service` type. +### proof.challenge() ⇒ string \| undefined +Challenge from a proof requester to mitigate replay attacks. -**Kind**: instance method of [StardustService](#StardustService) - +**Kind**: instance method of [Proof](#Proof) + -### stardustService.serviceEndpoint() ⇒ string \| Array.<string> \| Map.<string, Array.<string>> -Returns a copy of the `Service` endpoint. +### proof.domain() ⇒ string \| undefined +Domain for which a proof is valid to mitigate replay attacks. -**Kind**: instance method of [StardustService](#StardustService) - +**Kind**: instance method of [Proof](#Proof) + -### stardustService.properties() ⇒ Map.<string, any> -Returns a copy of the custom properties on the `Service`. +### proof.purpose() ⇒ [ProofPurpose](#ProofPurpose) \| undefined +Purpose for which the proof was generated. -**Kind**: instance method of [StardustService](#StardustService) - +**Kind**: instance method of [Proof](#Proof) + -### stardustService.toJSON() ⇒ any +### proof.toJSON() ⇒ any Serializes this to a JSON object. -**Kind**: instance method of [StardustService](#StardustService) - +**Kind**: instance method of [Proof](#Proof) + -### stardustService.clone() ⇒ [StardustService](#StardustService) +### proof.clone() ⇒ [Proof](#Proof) Deep clones the object. -**Kind**: instance method of [StardustService](#StardustService) - +**Kind**: instance method of [Proof](#Proof) + -### StardustService.fromJSON(json) ⇒ [StardustService](#StardustService) +### Proof.fromJSON(json) ⇒ [Proof](#Proof) Deserializes an instance from a JSON object. -**Kind**: static method of [StardustService](#StardustService) +**Kind**: static method of [Proof](#Proof) | Param | Type | | --- | --- | | json | any | - + + +## ProofOptions +Holds additional options for creating signatures. +See `IProofOptions`. -## StardustVerificationMethod **Kind**: global class -* [StardustVerificationMethod](#StardustVerificationMethod) - * [new StardustVerificationMethod(did, keyType, publicKey, fragment)](#new_StardustVerificationMethod_new) +* [ProofOptions](#ProofOptions) + * [new ProofOptions(options)](#new_ProofOptions_new) * _instance_ - * [.id()](#StardustVerificationMethod+id) ⇒ [StardustDIDUrl](#StardustDIDUrl) - * [.setId(id)](#StardustVerificationMethod+setId) - * [.controller()](#StardustVerificationMethod+controller) ⇒ [StardustDID](#StardustDID) - * [.setController(did)](#StardustVerificationMethod+setController) - * [.type()](#StardustVerificationMethod+type) ⇒ [MethodType](#MethodType) - * [.setType(type_)](#StardustVerificationMethod+setType) - * [.data()](#StardustVerificationMethod+data) ⇒ [MethodData](#MethodData) - * [.setData(data)](#StardustVerificationMethod+setData) - * [.properties()](#StardustVerificationMethod+properties) ⇒ Map.<string, any> - * [.setPropertyUnchecked(key, value)](#StardustVerificationMethod+setPropertyUnchecked) - * [.toJSON()](#StardustVerificationMethod+toJSON) ⇒ any - * [.clone()](#StardustVerificationMethod+clone) ⇒ [StardustVerificationMethod](#StardustVerificationMethod) + * [.toJSON()](#ProofOptions+toJSON) ⇒ any + * [.clone()](#ProofOptions+clone) ⇒ [ProofOptions](#ProofOptions) * _static_ - * [.fromJSON(json)](#StardustVerificationMethod.fromJSON) ⇒ [StardustVerificationMethod](#StardustVerificationMethod) - - - -### new StardustVerificationMethod(did, keyType, publicKey, fragment) -Creates a new `StardustVerificationMethod` from the given `did` and public key. - - -| Param | Type | -| --- | --- | -| did | [StardustDID](#StardustDID) | -| keyType | number | -| publicKey | Uint8Array | -| fragment | string | - - + * [.default()](#ProofOptions.default) ⇒ [ProofOptions](#ProofOptions) + * [.fromJSON(json)](#ProofOptions.fromJSON) ⇒ [ProofOptions](#ProofOptions) -### stardustVerificationMethod.id() ⇒ [StardustDIDUrl](#StardustDIDUrl) -Returns a copy of the `StardustVerificationMethod` id. + -**Kind**: instance method of [StardustVerificationMethod](#StardustVerificationMethod) - +### new ProofOptions(options) +Creates a new `ProofOptions` from the given fields. -### stardustVerificationMethod.setId(id) -Sets the id of the `StardustVerificationMethod`. +Throws an error if any of the options are invalid. -**Kind**: instance method of [StardustVerificationMethod](#StardustVerificationMethod) | Param | Type | | --- | --- | -| id | [StardustDIDUrl](#StardustDIDUrl) | - - - -### stardustVerificationMethod.controller() ⇒ [StardustDID](#StardustDID) -Returns a copy of the `controller` `DID` of the `StardustVerificationMethod`. +| options | IProofOptions | -**Kind**: instance method of [StardustVerificationMethod](#StardustVerificationMethod) - + -### stardustVerificationMethod.setController(did) -Sets the `controller` `DID` of the `StardustVerificationMethod`. +### proofOptions.toJSON() ⇒ any +Serializes this to a JSON object. -**Kind**: instance method of [StardustVerificationMethod](#StardustVerificationMethod) +**Kind**: instance method of [ProofOptions](#ProofOptions) + -| Param | Type | -| --- | --- | -| did | [StardustDID](#StardustDID) | +### proofOptions.clone() ⇒ [ProofOptions](#ProofOptions) +Deep clones the object. - +**Kind**: instance method of [ProofOptions](#ProofOptions) + -### stardustVerificationMethod.type() ⇒ [MethodType](#MethodType) -Returns a copy of the `StardustVerificationMethod` type. +### ProofOptions.default() ⇒ [ProofOptions](#ProofOptions) +Creates a new `ProofOptions` with default options. -**Kind**: instance method of [StardustVerificationMethod](#StardustVerificationMethod) - +**Kind**: static method of [ProofOptions](#ProofOptions) + -### stardustVerificationMethod.setType(type_) -Sets the `StardustVerificationMethod` type. +### ProofOptions.fromJSON(json) ⇒ [ProofOptions](#ProofOptions) +Deserializes an instance from a JSON object. -**Kind**: instance method of [StardustVerificationMethod](#StardustVerificationMethod) +**Kind**: static method of [ProofOptions](#ProofOptions) | Param | Type | | --- | --- | -| type_ | [MethodType](#MethodType) | - - - -### stardustVerificationMethod.data() ⇒ [MethodData](#MethodData) -Returns a copy of the `StardustVerificationMethod` public key data. - -**Kind**: instance method of [StardustVerificationMethod](#StardustVerificationMethod) - - -### stardustVerificationMethod.setData(data) -Sets `StardustVerificationMethod` public key data. +| json | any | -**Kind**: instance method of [StardustVerificationMethod](#StardustVerificationMethod) + -| Param | Type | -| --- | --- | -| data | [MethodData](#MethodData) | +## ProofPurpose +Associates a purpose with a [Proof](#Proof). - +See https://w3c-ccg.github.io/security-vocab/#proofPurpose -### stardustVerificationMethod.properties() ⇒ Map.<string, any> -Get custom properties of the Verification Method. +**Kind**: global class -**Kind**: instance method of [StardustVerificationMethod](#StardustVerificationMethod) - +* [ProofPurpose](#ProofPurpose) + * _instance_ + * [.toJSON()](#ProofPurpose+toJSON) ⇒ any + * [.clone()](#ProofPurpose+clone) ⇒ [ProofPurpose](#ProofPurpose) + * _static_ + * [.assertionMethod()](#ProofPurpose.assertionMethod) ⇒ [ProofPurpose](#ProofPurpose) + * [.authentication()](#ProofPurpose.authentication) ⇒ [ProofPurpose](#ProofPurpose) + * [.fromJSON(json)](#ProofPurpose.fromJSON) ⇒ [ProofPurpose](#ProofPurpose) -### stardustVerificationMethod.setPropertyUnchecked(key, value) -Adds a custom property to the Verification Method. -If the value is set to `null`, the custom property will be removed. + -### WARNING -This method can overwrite existing properties like `id` and result -in an invalid Verification Method. +### proofPurpose.toJSON() ⇒ any +Serializes this to a JSON object. -**Kind**: instance method of [StardustVerificationMethod](#StardustVerificationMethod) +**Kind**: instance method of [ProofPurpose](#ProofPurpose) + -| Param | Type | -| --- | --- | -| key | string | -| value | any | +### proofPurpose.clone() ⇒ [ProofPurpose](#ProofPurpose) +Deep clones the object. - +**Kind**: instance method of [ProofPurpose](#ProofPurpose) + -### stardustVerificationMethod.toJSON() ⇒ any -Serializes this to a JSON object. +### ProofPurpose.assertionMethod() ⇒ [ProofPurpose](#ProofPurpose) +Purpose is to assert a claim. +See https://www.w3.org/TR/did-core/#assertion -**Kind**: instance method of [StardustVerificationMethod](#StardustVerificationMethod) - +**Kind**: static method of [ProofPurpose](#ProofPurpose) + -### stardustVerificationMethod.clone() ⇒ [StardustVerificationMethod](#StardustVerificationMethod) -Deep clones the object. +### ProofPurpose.authentication() ⇒ [ProofPurpose](#ProofPurpose) +Purpose is to authenticate the signer. +See https://www.w3.org/TR/did-core/#authentication -**Kind**: instance method of [StardustVerificationMethod](#StardustVerificationMethod) - +**Kind**: static method of [ProofPurpose](#ProofPurpose) + -### StardustVerificationMethod.fromJSON(json) ⇒ [StardustVerificationMethod](#StardustVerificationMethod) +### ProofPurpose.fromJSON(json) ⇒ [ProofPurpose](#ProofPurpose) Deserializes an instance from a JSON object. -**Kind**: static method of [StardustVerificationMethod](#StardustVerificationMethod) +**Kind**: static method of [ProofPurpose](#ProofPurpose) | Param | Type | | --- | --- | | json | any | - - -## StorageTestSuite -A test suite for the `Storage` interface. - -This module contains a set of tests that a correct storage implementation -should pass. Note that not every edge case is tested. + -Tests usually rely on multiple interface methods being implemented, so they should only -be run on a fully implemented version. That's why there is not a single test case for every -interface method. +## RevocationBitmap +A compressed bitmap for managing credential revocation. **Kind**: global class -* [StorageTestSuite](#StorageTestSuite) - * [.didCreateGenerateKeyTest(storage)](#StorageTestSuite.didCreateGenerateKeyTest) ⇒ Promise.<void> - * [.didCreatePrivateKeyTest(storage)](#StorageTestSuite.didCreatePrivateKeyTest) ⇒ Promise.<void> - * [.didListTest(storage)](#StorageTestSuite.didListTest) ⇒ Promise.<void> - * [.didPurgeTest(storage)](#StorageTestSuite.didPurgeTest) ⇒ Promise.<void> - * [.keyGenerateTest(storage)](#StorageTestSuite.keyGenerateTest) ⇒ Promise.<void> - * [.keyDeleteTest(storage)](#StorageTestSuite.keyDeleteTest) ⇒ Promise.<void> - * [.keyInsertTest(storage)](#StorageTestSuite.keyInsertTest) ⇒ Promise.<void> - * [.keySignEd25519Test(storage)](#StorageTestSuite.keySignEd25519Test) ⇒ Promise.<void> - * [.encryptionTest(alice_storage, bob_storage)](#StorageTestSuite.encryptionTest) ⇒ Promise.<void> +* [RevocationBitmap](#RevocationBitmap) + * [new RevocationBitmap()](#new_RevocationBitmap_new) + * _instance_ + * [.isRevoked(index)](#RevocationBitmap+isRevoked) ⇒ boolean + * [.revoke(index)](#RevocationBitmap+revoke) ⇒ boolean + * [.unrevoke(index)](#RevocationBitmap+unrevoke) ⇒ boolean + * [.len()](#RevocationBitmap+len) ⇒ number + * [.toEndpoint()](#RevocationBitmap+toEndpoint) ⇒ string \| Array.<string> \| Map.<string, Array.<string>> + * _static_ + * [.type()](#RevocationBitmap.type) ⇒ string + * [.fromEndpoint(endpoint)](#RevocationBitmap.fromEndpoint) ⇒ [RevocationBitmap](#RevocationBitmap) - + -### StorageTestSuite.didCreateGenerateKeyTest(storage) ⇒ Promise.<void> -**Kind**: static method of [StorageTestSuite](#StorageTestSuite) +### new RevocationBitmap() +Creates a new `RevocationBitmap` instance. -| Param | Type | -| --- | --- | -| storage | Storage | + - +### revocationBitmap.isRevoked(index) ⇒ boolean +Returns `true` if the credential at the given `index` is revoked. -### StorageTestSuite.didCreatePrivateKeyTest(storage) ⇒ Promise.<void> -**Kind**: static method of [StorageTestSuite](#StorageTestSuite) +**Kind**: instance method of [RevocationBitmap](#RevocationBitmap) | Param | Type | | --- | --- | -| storage | Storage | - - +| index | number | -### StorageTestSuite.didListTest(storage) ⇒ Promise.<void> -**Kind**: static method of [StorageTestSuite](#StorageTestSuite) + -| Param | Type | -| --- | --- | -| storage | Storage | +### revocationBitmap.revoke(index) ⇒ boolean +Mark the given index as revoked. - +Returns true if the index was absent from the set. -### StorageTestSuite.didPurgeTest(storage) ⇒ Promise.<void> -**Kind**: static method of [StorageTestSuite](#StorageTestSuite) +**Kind**: instance method of [RevocationBitmap](#RevocationBitmap) | Param | Type | | --- | --- | -| storage | Storage | - - +| index | number | -### StorageTestSuite.keyGenerateTest(storage) ⇒ Promise.<void> -**Kind**: static method of [StorageTestSuite](#StorageTestSuite) + -| Param | Type | -| --- | --- | -| storage | Storage | +### revocationBitmap.unrevoke(index) ⇒ boolean +Mark the index as not revoked. - +Returns true if the index was present in the set. -### StorageTestSuite.keyDeleteTest(storage) ⇒ Promise.<void> -**Kind**: static method of [StorageTestSuite](#StorageTestSuite) +**Kind**: instance method of [RevocationBitmap](#RevocationBitmap) | Param | Type | | --- | --- | -| storage | Storage | +| index | number | - + -### StorageTestSuite.keyInsertTest(storage) ⇒ Promise.<void> -**Kind**: static method of [StorageTestSuite](#StorageTestSuite) +### revocationBitmap.len() ⇒ number +Returns the number of revoked credentials. -| Param | Type | -| --- | --- | -| storage | Storage | +**Kind**: instance method of [RevocationBitmap](#RevocationBitmap) + - +### revocationBitmap.toEndpoint() ⇒ string \| Array.<string> \| Map.<string, Array.<string>> +Return the bitmap as a data url embedded in a service endpoint. -### StorageTestSuite.keySignEd25519Test(storage) ⇒ Promise.<void> -**Kind**: static method of [StorageTestSuite](#StorageTestSuite) +**Kind**: instance method of [RevocationBitmap](#RevocationBitmap) + -| Param | Type | -| --- | --- | -| storage | Storage | +### RevocationBitmap.type() ⇒ string +The name of the service type. + +**Kind**: static method of [RevocationBitmap](#RevocationBitmap) + - +### RevocationBitmap.fromEndpoint(endpoint) ⇒ [RevocationBitmap](#RevocationBitmap) +Construct a `RevocationBitmap` from a data `url`. -### StorageTestSuite.encryptionTest(alice_storage, bob_storage) ⇒ Promise.<void> -**Kind**: static method of [StorageTestSuite](#StorageTestSuite) +**Kind**: static method of [RevocationBitmap](#RevocationBitmap) | Param | Type | | --- | --- | -| alice_storage | Storage | -| bob_storage | Storage | +| endpoint | string \| Array.<string> \| Map.<string, Array.<string>> | @@ -7113,95 +3861,6 @@ Deserializes an instance from a JSON object. | --- | --- | | json | any | - - -## VerificationMethod -**Kind**: global class - -* [VerificationMethod](#VerificationMethod) - * [new VerificationMethod(did, keyType, publicKey, fragment)](#new_VerificationMethod_new) - * _instance_ - * [.id()](#VerificationMethod+id) ⇒ [DIDUrl](#DIDUrl) - * [.controller()](#VerificationMethod+controller) ⇒ [IotaDID](#IotaDID) - * [.setController(did)](#VerificationMethod+setController) - * [.type()](#VerificationMethod+type) ⇒ [MethodType](#MethodType) - * [.data()](#VerificationMethod+data) ⇒ [MethodData](#MethodData) - * [.toJSON()](#VerificationMethod+toJSON) ⇒ any - * [.clone()](#VerificationMethod+clone) ⇒ [VerificationMethod](#VerificationMethod) - * _static_ - * [.fromJSON(json)](#VerificationMethod.fromJSON) ⇒ [VerificationMethod](#VerificationMethod) - - - -### new VerificationMethod(did, keyType, publicKey, fragment) -Creates a new `VerificationMethod` from the given `did` and public key. - - -| Param | Type | -| --- | --- | -| did | [IotaDID](#IotaDID) | -| keyType | number | -| publicKey | Uint8Array | -| fragment | string | - - - -### verificationMethod.id() ⇒ [DIDUrl](#DIDUrl) -Returns a copy of the `id` `DIDUrl` of the `VerificationMethod`. - -**Kind**: instance method of [VerificationMethod](#VerificationMethod) - - -### verificationMethod.controller() ⇒ [IotaDID](#IotaDID) -Returns a copy of the `controller` `DID` of the `VerificationMethod`. - -**Kind**: instance method of [VerificationMethod](#VerificationMethod) - - -### verificationMethod.setController(did) -Sets the `controller` `DID` of the `VerificationMethod` object. - -**Kind**: instance method of [VerificationMethod](#VerificationMethod) - -| Param | Type | -| --- | --- | -| did | [IotaDID](#IotaDID) | - - - -### verificationMethod.type() ⇒ [MethodType](#MethodType) -Returns a copy of the `VerificationMethod` type. - -**Kind**: instance method of [VerificationMethod](#VerificationMethod) - - -### verificationMethod.data() ⇒ [MethodData](#MethodData) -Returns a copy of the `VerificationMethod` public key data. - -**Kind**: instance method of [VerificationMethod](#VerificationMethod) - - -### verificationMethod.toJSON() ⇒ any -Serializes this to a JSON object. - -**Kind**: instance method of [VerificationMethod](#VerificationMethod) - - -### verificationMethod.clone() ⇒ [VerificationMethod](#VerificationMethod) -Deep clones the object. - -**Kind**: instance method of [VerificationMethod](#VerificationMethod) - - -### VerificationMethod.fromJSON(json) ⇒ [VerificationMethod](#VerificationMethod) -Deserializes an instance from a JSON object. - -**Kind**: static method of [VerificationMethod](#VerificationMethod) - -| Param | Type | -| --- | --- | -| json | any | - ## VerifierOptions @@ -7325,19 +3984,13 @@ This is possible because Ed25519 is birationally equivalent to Curve25519 used b | --- | --- | | publicKey | Uint8Array | - - -## StateMetadataEncoding -**Kind**: global variable ## MethodRelationship **Kind**: global variable - - -## DIDType -Supported types representing a DID that can be generated by the storage interface. + +## StateMetadataEncoding **Kind**: global variable @@ -7421,10 +4074,6 @@ Return after the first error occurs. ## KeyType **Kind**: global variable - - -## DIDMessageEncoding -**Kind**: global variable ## start() diff --git a/bindings/wasm/examples-stardust/README.md b/bindings/wasm/examples-stardust/README.md deleted file mode 100644 index e693126871..0000000000 --- a/bindings/wasm/examples-stardust/README.md +++ /dev/null @@ -1,45 +0,0 @@ -![banner](./../../../documentation/static/img/Banner/banner_identity.svg) - -## IOTA Identity UTXO Examples - -The following code examples demonstrate how to use the IOTA Identity Wasm bindings in JavaScript/TypeScript. - -The examples are written in TypeScript and can be run with Node.js. - -### Node.js - -Install the dependencies: - -```bash -npm install -``` - -Build the bindings: - -```bash -npm run build -``` - -Then, run an example using: - -```bash -npm run example:stardust -- -``` - -For instance, to run the `ex0_create_did` example execute: - -```bash -npm run example:stardust -- ex0_create_did -``` - -| # | Name | Details | -|-----|-------------------------------------------------------|------------------------------------------------------------------| -| 0 | [ex0_create_did](src/ex0_create_did.ts) | Create a DID Document and publish it in a new Alias Output. | -| 1 | [ex1_update_did](src/ex1_update_did.ts) | Update a DID document in an existing Alias Output. | -| 2 | [ex2_resolve_did](src/ex2_resolve_did.ts) | Resolve an existing DID in an Alias Output. | -| 3 | [ex3_deactivate_did](src/ex3_deactivate_did.ts) | Deactivate a DID in an Alias Output. | -| 4 | [ex4_delete_did](src/ex4_delete_did.ts) | Delete a DID in an Alias Output, reclaiming the storage deposit. | - -## Browser - -While the examples should work in a browser environment, we do not provide browser examples yet. diff --git a/bindings/wasm/examples/.gitignore b/bindings/wasm/examples/.gitignore deleted file mode 100644 index 331f36626e..0000000000 --- a/bindings/wasm/examples/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -!* -dist/ diff --git a/bindings/wasm/examples/README.md b/bindings/wasm/examples/README.md index 09d6771208..d0d688183c 100644 --- a/bindings/wasm/examples/README.md +++ b/bindings/wasm/examples/README.md @@ -2,66 +2,44 @@ ## IOTA Identity Examples -This folder provides code examples for you to learn how the IOTA Identity WASM bindings can be used in JavaScript. +The following code examples demonstrate how to use the IOTA Identity Wasm bindings in JavaScript/TypeScript. -These examples are compiled with webpack for convenience but can be run independently. If you intend to use any code -examples with a published version of this package, replace `@iota/identity-wasm` imports with -`@iota/identity-wasm/node` for Node.js or `@iota/identity-wasm/web` for use in the browser. +The examples are written in TypeScript and can be run with Node.js. -If you are writing code against the test network then, most function calls will need to include information about the -network, since this is not automatically inferred from the arguments in all cases currently. +### Node.js -We recommend that you **always** use a `CLIENT_CONFIG` parameter that you define when calling any functions that take a -`ClientConfig` object. This will ensure that all the API calls use a consistent node and network throughout. If you -mismatch the network across calls you will encounter errors. +Install the dependencies: -A `ClientConfig` is a record consisting of two string fields: `network` and `node`. There is an example client config -that can be found in the `config.js` file for node and in `main.js` for the browser. +```bash +npm install +``` -### Node.js Examples +Build the bindings: -Before running the examples, make sure you have [built the bindings](../README.md#Build) for `node.js`. +```bash +npm run build +``` -- To build the examples use: - ```bash - npm run build:examples - ``` +Then, run an example using: -- You can then run each example with: - ```bash - npm run example:node -- - ``` +```bash +npm run example:node -- +``` -- For instance, to run the `create_did` example use: - ```bash - npm run example:node -- create_did - ``` +For instance, to run the `ex0_create_did` example execute: -The following examples are currently available: +```bash +npm run example:node -- ex0_create_did +``` -| # | Name | Information | -| :-: | :---------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| 1 | [create_did](src/create_did.js) | Generates and publishes a DID Document, the fundamental building block for decentralized identity. | -| 2 | [manipulate_did](src/manipulate_did.js) | Add verification methods and service endpoints to a DID Document and update an already existing DID Document. | | -| 3 | [resolve_history](src/resolve_history.js) | Advanced example that performs multiple updates and demonstrates how to resolve the DID Document history to view them. | -| 4 | [resolve_did](src/resolve_did.js) | Resolves an existing DID to return the latest DID Document. | -| 5 | [key_exchange](src/key_exchange.js) | Demonstrates Elliptic-curve Diffie-Hellman (ECDH) cryptographic key exchange with DID Documents. | | -| 6 | [private_tangle](src/private_tangle.js) | Showcases the same procedure as `create_did`, but on a private tangle - a locally running hornet node. | +| # | Name | Details | +|-----|-------------------------------------------------|------------------------------------------------------------------| +| 0 | [ex0_create_did](src/ex0_create_did.ts) | Create a DID Document and publish it in a new Alias Output. | +| 1 | [ex1_update_did](src/ex1_update_did.ts) | Update a DID document in an existing Alias Output. | +| 2 | [ex2_resolve_did](src/ex2_resolve_did.ts) | Resolve an existing DID in an Alias Output. | +| 3 | [ex3_deactivate_did](src/ex3_deactivate_did.ts) | Deactivate a DID in an Alias Output. | +| 4 | [ex4_delete_did](src/ex4_delete_did.ts) | Delete a DID in an Alias Output, reclaiming the storage deposit. | -### Browser Examples +## Browser -All the Node.js examples are also available for the browser. - -Before running the examples, make sure you have [built the bindings](../README.md#Build) for `web`. - -- To build the examples use: - ```bash - npm run build:examples - ``` - -- You can then run the browser examples with: - ```bash - npm run example:browser - ``` - -Note: the webpage will be served on port 8080 +While the examples should work in a browser environment, we do not provide browser examples yet. diff --git a/bindings/wasm/examples/src/config.js b/bindings/wasm/examples/src/config.js deleted file mode 100644 index 15854b9bc6..0000000000 --- a/bindings/wasm/examples/src/config.js +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { Network, ExplorerUrl } from '@iota/identity-wasm'; - -const MAINNET = Network.mainnet(); -const EXPLORER_MAINNET = ExplorerUrl.mainnet(); - -/* @type {{network: Network, explorer: ExplorerUrl}} */ -const CLIENT_CONFIG = { - network: MAINNET, - explorer: EXPLORER_MAINNET, -} - -export {CLIENT_CONFIG}; diff --git a/bindings/wasm/examples/src/create_did.js b/bindings/wasm/examples/src/create_did.js deleted file mode 100644 index a0a27e8e4d..0000000000 --- a/bindings/wasm/examples/src/create_did.js +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import {Client, Document, KeyPair, KeyType} from '@iota/identity-wasm'; - -/** - This example shows a basic introduction on how to create a basic DID Document and upload it to the Tangle. - A ED25519 Keypair is generated, from which the public key is hashed, becoming the DID. - The keypair becomes part of the DID Document in order to prove a link between the DID and the published DID Document. - That same keypair should be used to sign the original DID Document. - - @param {{network: Network, explorer: ExplorerUrl}} clientConfig - **/ -async function createIdentity(clientConfig) { - // Generate a new ed25519 public/private key pair. - const key = new KeyPair(KeyType.Ed25519); - - // Create a DID Document (an identity) from the generated key pair. - const doc = new Document(key, clientConfig.network.name()); - - // Sign the DID Document with the generated key. - doc.signSelf(key, doc.defaultSigningMethod().id()); - - // Create a client instance to publish messages to the configured Tangle network. - const client = await Client.fromConfig({ - network: clientConfig.network - }); - - // Publish the Identity to the IOTA Network, this may take a few seconds to complete Proof-of-Work. - const receipt = await client.publishDocument(doc); - - // Log the results. - console.log(`DID Document Transaction: ${clientConfig.explorer.messageUrl(receipt.messageId())}`); - console.log(`Explore the DID Document: ${clientConfig.explorer.resolverUrl(doc.id())}`); - - // Return the results. - return {key, doc, receipt}; -} - -export {createIdentity}; diff --git a/bindings/wasm/examples-stardust/src/ex0_create_did.ts b/bindings/wasm/examples/src/ex0_create_did.ts similarity index 85% rename from bindings/wasm/examples-stardust/src/ex0_create_did.ts rename to bindings/wasm/examples/src/ex0_create_did.ts index e1fe698b31..3b8417e9a7 100644 --- a/bindings/wasm/examples-stardust/src/ex0_create_did.ts +++ b/bindings/wasm/examples/src/ex0_create_did.ts @@ -5,31 +5,31 @@ import { KeyPair, KeyType, MethodScope, - StardustDID, - StardustDocument, - StardustIdentityClient, - StardustVerificationMethod + IotaDID, + IotaDocument, + IotaIdentityClient, + IotaVerificationMethod } from '../../node'; -import {Bech32Helper, IAliasOutput} from '@iota/iota.js'; -import {Bip39} from "@iota/crypto.js"; +import { Bech32Helper, IAliasOutput } from '@iota/iota.js'; +import { Bip39 } from "@iota/crypto.js"; import fetch from "node-fetch"; -import {Client, MnemonicSecretManager, SecretManager} from "@cycraig/iota-client-wasm/node"; +import { Client, MnemonicSecretManager, SecretManager } from "@cycraig/iota-client-wasm/node"; const API_ENDPOINT = "https://api.testnet.shimmer.network/"; const FAUCET = "https://faucet.testnet.shimmer.network/api/enqueue"; /** Demonstrate how to create a DID Document and publish it in a new Alias Output. */ export async function createIdentity(): Promise<{ - didClient: StardustIdentityClient, + didClient: IotaIdentityClient, secretManager: SecretManager, walletAddressBech32: string, - did: StardustDID + did: IotaDID }> { const client = new Client({ primaryNode: API_ENDPOINT, localPow: true, }); - const didClient = new StardustIdentityClient(client); + const didClient = new IotaIdentityClient(client); // Get the Bech32 human-readable part (HRP) of the network. const networkHrp: string = await didClient.getNetworkHrp(); @@ -52,11 +52,11 @@ export async function createIdentity(): Promise<{ // Create a new DID document with a placeholder DID. // The DID will be derived from the Alias Id of the Alias Output after publishing. - const document = new StardustDocument(networkHrp); + const document = new IotaDocument(networkHrp); // Insert a new Ed25519 verification method in the DID document. let keypair = new KeyPair(KeyType.Ed25519); - let method = new StardustVerificationMethod(document.id(), keypair.type(), keypair.public(), "#key-1"); + let method = new IotaVerificationMethod(document.id(), keypair.type(), keypair.public(), "#key-1"); document.insertMethod(method, MethodScope.VerificationMethod()); // Construct an Alias Output containing the DID document, with the wallet address @@ -100,10 +100,10 @@ async function ensureAddressHasFunds(client: Client, addressBech32: string) { async function getAddressBalance(client: Client, addressBech32: string): Promise { // TODO: use the `addresses/ed25519/` API to get the balance? const outputIds = await client.basicOutputIds([ - {address: addressBech32}, - {hasExpiration: false}, - {hasTimelock: false}, - {hasStorageDepositReturn: false} + { address: addressBech32 }, + { hasExpiration: false }, + { hasTimelock: false }, + { hasStorageDepositReturn: false } ]); const outputs = await client.getOutputs(outputIds); @@ -117,7 +117,7 @@ async function getAddressBalance(client: Client, addressBech32: string): Promise /** Request tokens from the testnet faucet API. */ async function requestFundsFromFaucet(addressBech32: string) { - const requestObj = JSON.stringify({address: addressBech32}); + const requestObj = JSON.stringify({ address: addressBech32 }); let errorMessage, data; try { const response = await fetch(FAUCET, { diff --git a/bindings/wasm/examples-stardust/src/ex1_update_did.ts b/bindings/wasm/examples/src/ex1_update_did.ts similarity index 72% rename from bindings/wasm/examples-stardust/src/ex1_update_did.ts rename to bindings/wasm/examples/src/ex1_update_did.ts index 8fea877b66..ff0a5cb3c3 100644 --- a/bindings/wasm/examples-stardust/src/ex1_update_did.ts +++ b/bindings/wasm/examples/src/ex1_update_did.ts @@ -1,25 +1,25 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import {MethodRelationship, StardustDocument, StardustService, Timestamp} from '../../node'; -import {IAliasOutput, IRent, TransactionHelper} from '@iota/iota.js'; +import { MethodRelationship, IotaDocument, IotaService, Timestamp } from '../../node'; +import { IAliasOutput, IRent, TransactionHelper } from '@iota/iota.js'; -import {createIdentity} from "./ex0_create_did"; +import { createIdentity } from "./ex0_create_did"; /** Demonstrates how to update a DID document in an existing Alias Output. */ export async function updateIdentity() { // Creates a new wallet and identity (see "ex0_create_did" example). - const {didClient, secretManager, did} = await createIdentity(); + const { didClient, secretManager, did } = await createIdentity(); // Resolve the latest state of the document. // Technically this is equivalent to the document above. - const document: StardustDocument = await didClient.resolveDid(did); + const document: IotaDocument = await didClient.resolveDid(did); // Attach a new method relationship to the existing method. document.attachMethodRelationship(did.join("#key-1"), MethodRelationship.Authentication); // Add a new Service. - const service: StardustService = new StardustService({ + const service: IotaService = new IotaService({ id: did.join("#linked-domain"), type: "LinkedDomains", serviceEndpoint: "https://iota.org/" @@ -36,6 +36,6 @@ export async function updateIdentity() { aliasOutput.amount = TransactionHelper.getStorageDeposit(aliasOutput, rentStructure).toString(); // Publish the output. - const updated: StardustDocument = await didClient.publishDidOutput(secretManager, aliasOutput); + const updated: IotaDocument = await didClient.publishDidOutput(secretManager, aliasOutput); console.log("Updated DID document:", JSON.stringify(updated, null, 2)); } diff --git a/bindings/wasm/examples-stardust/src/ex2_resolve_did.ts b/bindings/wasm/examples/src/ex2_resolve_did.ts similarity index 69% rename from bindings/wasm/examples-stardust/src/ex2_resolve_did.ts rename to bindings/wasm/examples/src/ex2_resolve_did.ts index 5f1dd5202c..b48757f4c7 100644 --- a/bindings/wasm/examples-stardust/src/ex2_resolve_did.ts +++ b/bindings/wasm/examples/src/ex2_resolve_did.ts @@ -1,18 +1,18 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import type {StardustDocument} from '../../node'; -import type {IAliasOutput} from '@iota/iota.js'; +import type { IotaDocument } from '../../node'; +import type { IAliasOutput } from '@iota/iota.js'; -import {createIdentity} from "./ex0_create_did"; +import { createIdentity } from "./ex0_create_did"; /** Demonstrates how to resolve an existing DID in an Alias Output. */ export async function resolveIdentity() { // Creates a new wallet and identity (see "ex0_create_did" example). - const {didClient, did} = await createIdentity(); + const { didClient, did } = await createIdentity(); // Resolve the associated Alias Output and extract the DID document from it. - const resolved: StardustDocument = await didClient.resolveDid(did); + const resolved: IotaDocument = await didClient.resolveDid(did); console.log("Resolved DID document:", JSON.stringify(resolved, null, 2)); // We can also resolve the Alias Output directly. diff --git a/bindings/wasm/examples-stardust/src/ex3_deactivate_did.ts b/bindings/wasm/examples/src/ex3_deactivate_did.ts similarity index 81% rename from bindings/wasm/examples-stardust/src/ex3_deactivate_did.ts rename to bindings/wasm/examples/src/ex3_deactivate_did.ts index 1d49a9f684..f9686cd926 100644 --- a/bindings/wasm/examples-stardust/src/ex3_deactivate_did.ts +++ b/bindings/wasm/examples/src/ex3_deactivate_did.ts @@ -1,18 +1,18 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import type {StardustDocument} from '../../node'; -import {IAliasOutput, IRent, TransactionHelper} from '@iota/iota.js'; +import type { IotaDocument } from '../../node'; +import { IAliasOutput, IRent, TransactionHelper } from '@iota/iota.js'; -import {createIdentity} from "./ex0_create_did"; +import { createIdentity } from "./ex0_create_did"; /** Demonstrates how to deactivate a DID in an Alias Output. */ export async function deactivateIdentity() { // Creates a new wallet and identity (see "ex0_create_did" example). - const {didClient, secretManager, did} = await createIdentity(); + const { didClient, secretManager, did } = await createIdentity(); // Resolve the latest state of the DID document, so we can reactivate it later. - let document: StardustDocument = await didClient.resolveDid(did); + let document: IotaDocument = await didClient.resolveDid(did); // Deactivate the DID by publishing an empty document. // This process can be reversed since the Alias Output is not destroyed. @@ -28,7 +28,7 @@ export async function deactivateIdentity() { // Resolving a deactivated DID returns an empty DID document // with its `deactivated` metadata field set to `true`. - let deactivated: StardustDocument = await didClient.resolveDid(did); + let deactivated: IotaDocument = await didClient.resolveDid(did); console.log("Deactivated DID document:", JSON.stringify(deactivated, null, 2)); if (deactivated.metadataDeactivated() !== true) { throw new Error("Failed to deactivate DID document"); @@ -42,7 +42,7 @@ export async function deactivateIdentity() { await didClient.publishDidOutput(secretManager, reactivatedOutput); // Resolve the reactivated DID document. - let reactivated: StardustDocument = await didClient.resolveDid(did); + let reactivated: IotaDocument = await didClient.resolveDid(did); if (reactivated.metadataDeactivated() === true) { throw new Error("Failed to reactivate DID document"); } diff --git a/bindings/wasm/examples-stardust/src/ex4_delete_did.ts b/bindings/wasm/examples/src/ex4_delete_did.ts similarity index 100% rename from bindings/wasm/examples-stardust/src/ex4_delete_did.ts rename to bindings/wasm/examples/src/ex4_delete_did.ts diff --git a/bindings/wasm/examples/src/index.html b/bindings/wasm/examples/src/index.html deleted file mode 100644 index 61cb0f89e2..0000000000 --- a/bindings/wasm/examples/src/index.html +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - - Wasm Examples Browser - - - - - - - - -
- - -
- - -
- - - -
- - - - - - -

Output:

-

-
-
-
\ No newline at end of file
diff --git a/bindings/wasm/examples/src/key_exchange.js b/bindings/wasm/examples/src/key_exchange.js
deleted file mode 100644
index d21c4aae33..0000000000
--- a/bindings/wasm/examples/src/key_exchange.js
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright 2020-2022 IOTA Stiftung
-// SPDX-License-Identifier: Apache-2.0
-
-import {
-    Client,
-    Document,
-    KeyPair,
-    KeyType,
-    MethodScope,
-    VerificationMethod,
-    X25519,
-} from '@iota/identity-wasm';
-
-/**
- Demonstrates Elliptic-curve Diffie-Hellman (ECDH) cryptographic key exchange with DID Documents.
-
- @param {{network: Network, explorer: ExplorerUrl}} clientConfig
- **/
-async function keyExchange(clientConfig) {
-    // Create a client instance to publish messages to the configured Tangle network.
-    const client = await Client.fromConfig({
-        network: clientConfig.network
-    });
-
-    // Alice creates and publishes their DID Document (see create_did and manipulate_did examples).
-    let aliceDID;
-    let aliceX25519;
-    {
-        // Create a DID Document.
-        const keypair = new KeyPair(KeyType.Ed25519);
-        const document = new Document(keypair, clientConfig.network.name());
-
-        // Insert a new X25519 KeyAgreement verification method.
-        let x25519 = new KeyPair(KeyType.X25519);
-        let method = new VerificationMethod(document.id(), KeyType.X25519, x25519.public(), "kex-0");
-        document.insertMethod(method, MethodScope.KeyAgreement());
-
-        // Publish the DID Document.
-        document.signSelf(keypair, document.defaultSigningMethod().id());
-        await client.publishDocument(document);
-
-        aliceDID = document.id();
-        aliceX25519 = x25519;
-    }
-
-    // Bob creates and publishes their DID Document (see create_did and manipulate_did examples).
-    let bobDID;
-    let bobX25519;
-    {
-        // Create a DID Document.
-        const keypair = new KeyPair(KeyType.Ed25519);
-        const document = new Document(keypair, clientConfig.network.name());
-
-        // Insert a new X25519 KeyAgreement verification method.
-        let x25519 = new KeyPair(KeyType.X25519);
-        let method = new VerificationMethod(document.id(), KeyType.X25519, x25519.public(), "kex-0");
-        document.insertMethod(method, MethodScope.KeyAgreement());
-
-        // Publish the DID Document.
-        document.signSelf(keypair, document.defaultSigningMethod().id());
-        await client.publishDocument(document);
-
-        bobDID = document.id();
-        bobX25519 = x25519;
-    }
-
-    // Alice and Bob tell each other their DIDs. They each resolve the DID Document of the other
-    // to obtain their X25519 public key. Note that in practice, they would run this code completely
-    // separately.
-
-    let aliceSharedSecretKey;
-    {
-        // Alice: resolves Bob's DID Document and extracts their public key.
-        const bobDocument = await client.resolve(bobDID);
-        const bobMethod = bobDocument.intoDocument().resolveMethod("kex-0", MethodScope.KeyAgreement());
-        const bobPublicKey = bobMethod.data().tryDecode();
-        // Compute the shared secret.
-        aliceSharedSecretKey = X25519.keyExchange(aliceX25519.private(), bobPublicKey);
-    }
-
-    let bobSharedSecretKey;
-    {
-        // Bob: resolves Alice's DID Document and extracts their public key.
-        const aliceDocument = await client.resolve(aliceDID);
-        const aliceMethod = aliceDocument.intoDocument().resolveMethod("kex-0", MethodScope.KeyAgreement());
-        const alicePublicKey = aliceMethod.data().tryDecode();
-        // Compute the shared secret.
-        bobSharedSecretKey = X25519.keyExchange(bobX25519.private(), alicePublicKey);
-    }
-
-    // Both shared secret keys computed separately by Alice and Bob will match
-    // and can then be used to establish encrypted communications.
-    if(!isArrayEqual(aliceSharedSecretKey, bobSharedSecretKey)) throw new Error("shared secret keys do not match!");
-    console.log(`Diffie-Hellman key exchange successful!`);
-}
-
-function isArrayEqual(a, b) {
-    if(a.length !== b.length) return false;
-    for(let i = 0; i < a.length; i++) {
-        if(a[i] !== b[i]) return false;
-    }
-    return true;
-}
-
-export {keyExchange};
diff --git a/bindings/wasm/examples-stardust/src/main.ts b/bindings/wasm/examples/src/main.ts
similarity index 100%
rename from bindings/wasm/examples-stardust/src/main.ts
rename to bindings/wasm/examples/src/main.ts
diff --git a/bindings/wasm/examples/src/manipulate_did.js b/bindings/wasm/examples/src/manipulate_did.js
deleted file mode 100644
index 4593c0792e..0000000000
--- a/bindings/wasm/examples/src/manipulate_did.js
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2020-2022 IOTA Stiftung
-// SPDX-License-Identifier: Apache-2.0
-
-import {
-    Client,
-    KeyPair,
-    KeyType,
-    MethodScope,
-    Service,
-    Timestamp,
-    VerificationMethod
-} from '@iota/identity-wasm';
-import {createIdentity} from "./create_did";
-
-/**
- This example shows how to add more to an existing DID Document.
- The two main things to add are Verification Methods and Services.
- A verification method adds public keys, which can be used to digitally sign things as an identity.
- The services provide metadata around the identity via URIs. These can be URLs, but can also emails or IOTA indices.
- An important detail to note is the previousMessageId:
- This is an important field as it links the new DID Document to the old DID Document, creating a chain.
- Without setting this value, the new DID Document won't get used during resolution of the DID!
-
- @param {{network: Network, explorer: ExplorerUrl}} clientConfig
- **/
-async function manipulateIdentity(clientConfig) {
-    // Create a client instance to publish messages to the configured Tangle network.
-    const client = await Client.fromConfig({
-        network: clientConfig.network
-    });
-
-    // Creates a new identity (see "create_did" example)
-    let {key, doc, receipt} = await createIdentity(clientConfig);
-
-    // Add a new VerificationMethod with a new KeyPair
-    const newKey = new KeyPair(KeyType.Ed25519);
-    const method = new VerificationMethod(doc.id(), newKey.type(), newKey.public(), "newKey");
-    doc.insertMethod(method, MethodScope.VerificationMethod());
-
-    // Add a new ServiceEndpoint
-    const service = new Service({
-        id: doc.id().toUrl().join("#linked-domain"),
-        type: "LinkedDomains",
-        serviceEndpoint: "https://iota.org",
-    });
-    doc.insertService(service);
-
-    /*
-        Add the messageId of the previous message in the chain.
-        This is REQUIRED in order for the messages to form a chain.
-        Skipping / forgetting this will render the publication useless.
-    */
-    doc.setMetadataPreviousMessageId(receipt.messageId());
-    doc.setMetadataUpdated(Timestamp.nowUTC());
-
-    // Sign the DID Document with the appropriate key.
-    doc.signSelf(key, doc.defaultSigningMethod().id());
-
-    // Publish the Identity to the IOTA Network, this may take a few seconds to complete Proof-of-Work.
-    const updateReceipt = await client.publishDocument(doc);
-
-    // Log the results.
-    console.log(`DID Document Update Transaction: ${clientConfig.explorer.messageUrl(updateReceipt.messageId())}`);
-    console.log(`Explore the DID Document: ${clientConfig.explorer.resolverUrl(doc.id())}`);
-
-    return {
-        key,
-        newKey,
-        doc,
-        originalMessageId: receipt.messageId(),
-        updatedMessageId: updateReceipt.messageId(),
-    };
-}
-
-export {manipulateIdentity};
diff --git a/bindings/wasm/examples/src/node.js b/bindings/wasm/examples/src/node.js
deleted file mode 100644
index 034c77dabf..0000000000
--- a/bindings/wasm/examples/src/node.js
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2020-2022 IOTA Stiftung
-// SPDX-License-Identifier: Apache-2.0
-
-import { createIdentity } from "./create_did";
-import { manipulateIdentity } from "./manipulate_did";
-import { privateTangle } from "./private_tangle";
-import { resolveIdentity } from "./resolve_did";
-import { CLIENT_CONFIG } from "./config";
-import { resolveHistory } from "./resolve_history";
-import { keyExchange } from "./key_exchange";
-
-async function main() {
-    //Check if an example is mentioned
-    if (process.argv.length != 3) {
-        throw "Please provide one command line argument with the example name.";
-    }
-
-    //Take out the argument
-    let argument = process.argv[2];
-    switch (argument) {
-        case "create_did":
-            return await createIdentity(CLIENT_CONFIG);
-        case "manipulate_did":
-            return await manipulateIdentity(CLIENT_CONFIG);
-        case "resolve_did":
-            return await resolveIdentity(CLIENT_CONFIG);
-        case "key_exchange":
-            return await keyExchange(CLIENT_CONFIG);
-        case "private_tangle":
-            return await privateTangle();
-        case "resolve_history":
-            return await resolveHistory(CLIENT_CONFIG);
-        case "all":
-            console.log(">>> Run All Examples");
-
-            await createIdentity(CLIENT_CONFIG);
-            await manipulateIdentity(CLIENT_CONFIG);
-            await resolveIdentity(CLIENT_CONFIG);
-            await keyExchange(CLIENT_CONFIG);
-            await resolveHistory(CLIENT_CONFIG);
-
-            console.log(">>> End All Examples");
-            return "all";
-        default:
-            throw "Unknown example name";
-    }
-}
-
-main()
-    .then((output) => {
-        console.log("Ok >", output);
-    })
-    .catch((error) => {
-        console.log("Err >", error);
-    });
diff --git a/bindings/wasm/examples/src/private_tangle.js b/bindings/wasm/examples/src/private_tangle.js
deleted file mode 100644
index 64bb297aaa..0000000000
--- a/bindings/wasm/examples/src/private_tangle.js
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2020-2022 IOTA Stiftung
-// SPDX-License-Identifier: Apache-2.0
-
-import {Client, DIDMessageEncoding, Document, ExplorerUrl, KeyPair, KeyType, Network} from '@iota/identity-wasm';
-
-/**
- This example shows how a DID document can be created on a private tangle.
- It can be run together with a local hornet node.
- Refer to https://github.com/iotaledger/one-click-tangle/tree/chrysalis/hornet-private-net
- for setup instructions.
- **/
-async function privateTangle(restURL, networkName) {
-    // This name needs to match the id of the network or part of it.
-    // Since the id of the one-click private tangle is `private-tangle`
-    // but we can only use 6 characters, we use just `tangle`.
-    const network = Network.tryFromName(networkName || "tangle");
-
-    // Optionally point to a locally-deployed Tangle explorer.
-    const explorer = ExplorerUrl.parse("http://127.0.0.1:8082/");
-
-    // Create a client instance with a custom configuration to publish messages to our private Tangle.
-    const client = await Client.fromConfig({
-        network: network,
-        // This URL points to the REST API of the locally running hornet node.
-        primaryNode: {url: restURL || "http://127.0.0.1:14265/"},
-        // Use DIDMessageEncoding.Json instead to publish plaintext messages to the Tangle for debugging.
-        encoding: DIDMessageEncoding.JsonBrotli,
-    });
-
-    // Generate a new ed25519 public/private key pair.
-    const key = new KeyPair(KeyType.Ed25519);
-
-    // Create a DID with the network set explicitly.
-    // This will result in a DID prefixed by `did:iota:tangle`.
-    const doc = new Document(key, network.name());
-
-    // Sign the DID Document with the generated key.
-    doc.signSelf(key, doc.defaultSigningMethod().id());
-
-    // Publish the Identity to the IOTA Network, this may take a few seconds to complete Proof-of-Work.
-    const receipt = await client.publishDocument(doc);
-
-    // Make sure the DID can be resolved on the private tangle
-    const resolved = await client.resolve(doc.id());
-
-    console.log(`Published the DID document to the private tangle:`);
-    console.log(resolved);
-    console.log(`Explore the DID Document: ${explorer.resolverUrl(doc.id())}`);
-
-    // Return the results.
-    return {key, resolved, receipt};
-}
-
-export {privateTangle};
diff --git a/bindings/wasm/examples/src/resolve_did.js b/bindings/wasm/examples/src/resolve_did.js
deleted file mode 100644
index 67db866643..0000000000
--- a/bindings/wasm/examples/src/resolve_did.js
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2020-2022 IOTA Stiftung
-// SPDX-License-Identifier: Apache-2.0
-
-import {Resolver} from '@iota/identity-wasm';
-import {createIdentity} from "./create_did";
-
-/**
- A short example to show how to resolve a DID. This returns the latest DID Document.
-
- @param {{network: Network, explorer: ExplorerUrl}} clientConfig
- **/
-async function resolveIdentity(clientConfig) {
-
-    // Creates a new identity (see "create_did" example)
-    const {key, doc, receipt} = await createIdentity(clientConfig);
-    const doc_did = doc.id();
-
-    // Retrieve the published DID Document from the Tangle.
-    const resolver = await Resolver
-        .builder()
-        .clientConfig({
-            network: clientConfig.network
-        })
-        .build();
-
-    return await resolver.resolve(doc_did);
-}
-
-export {resolveIdentity};
diff --git a/bindings/wasm/examples/src/resolve_history.js b/bindings/wasm/examples/src/resolve_history.js
deleted file mode 100644
index d6f7b289f4..0000000000
--- a/bindings/wasm/examples/src/resolve_history.js
+++ /dev/null
@@ -1,143 +0,0 @@
-// Copyright 2020-2022 IOTA Stiftung
-// SPDX-License-Identifier: Apache-2.0
-
-import {
-    Client,
-    Document,
-    KeyPair,
-    KeyType,
-    MethodScope,
-    Service,
-    Timestamp,
-    VerificationMethod
-} from '@iota/identity-wasm';
-import {createIdentity} from "./create_did";
-
-/**
- Advanced example that performs multiple updates and demonstrates how to resolve the DID Document history to view them.
-
- @param {{network: Network, explorer: ExplorerUrl}} clientConfig
- **/
-async function resolveHistory(clientConfig) {
-    // Create a client instance to publish messages to the configured Tangle network.
-    const client = await Client.fromConfig({
-        network: clientConfig.network
-    });
-
-    // ===========================================================================
-    // DID Creation
-    // ===========================================================================
-
-    // Create a new identity (see "create_did.js" example).
-    const {doc, key, receipt: originalReceipt} = await createIdentity(clientConfig);
-
-    // ===========================================================================
-    // Integration Chain Spam
-    // ===========================================================================
-
-    // Publish several spam messages to the same index as the integration chain on the Tangle.
-    // These are not valid DID documents and are simply to demonstrate that invalid messages can be
-    // included in the history, potentially for debugging invalid DID documents.
-    const intIndex = doc.integrationIndex();
-    await client.publishJSON(intIndex, {"intSpam:1": true});
-    await client.publishJSON(intIndex, {"intSpam:2": true});
-    await client.publishJSON(intIndex, {"intSpam:3": true});
-    await client.publishJSON(intIndex, {"intSpam:4": true});
-    await client.publishJSON(intIndex, {"intSpam:5": true});
-
-    // ===========================================================================
-    // Integration Chain Update 1
-    // ===========================================================================
-
-    // Prepare an integration chain update, which writes the full updated DID document to the Tangle.
-    const intDoc1 = doc.clone();
-
-    // Add a new Service with the tag "linked-domain-1"
-    const service1 = new Service({
-        id: intDoc1.id().toUrl().join("#linked-domain-1"),
-        type: "LinkedDomains",
-        serviceEndpoint: "https://iota.org",
-    });
-    intDoc1.insertService(service1);
-
-    // Add a second Service with the tag "linked-domain-2"
-    const service2 = new Service({
-        id: intDoc1.id().toUrl().join("#linked-domain-2"),
-        type: "LinkedDomains",
-        serviceEndpoint: {
-            "origins": ["https://iota.org/", "https://example.com/"]
-        },
-    });
-    intDoc1.insertService(service2);
-
-    // Add a new VerificationMethod with a new KeyPair, with the tag "keys-1"
-    const keys1 = new KeyPair(KeyType.Ed25519);
-    const method1 = new VerificationMethod(intDoc1.id(), keys1.type(), keys1.public(), "keys-1");
-    intDoc1.insertMethod(method1, MethodScope.VerificationMethod());
-
-    // Add the `messageId` of the previous message in the chain.
-    // This is REQUIRED in order for the messages to form a chain.
-    // Skipping / forgetting this will render the publication useless.
-    intDoc1.setMetadataPreviousMessageId(originalReceipt.messageId());
-    intDoc1.setMetadataUpdated(Timestamp.nowUTC());
-
-    // Sign the DID Document with the original private key.
-    intDoc1.signSelf(key, intDoc1.defaultSigningMethod().id());
-
-    // Publish the updated DID Document to the Tangle, updating the integration chain.
-    // This may take a few seconds to complete proof-of-work.
-    const intReceipt1 = await client.publishDocument(intDoc1);
-
-    // Log the results.
-    console.log(`Int. Chain Update (1): ${clientConfig.explorer.messageUrl(intReceipt1.messageId())}`);
-
-    // ===========================================================================
-    // DID History 1
-    // ===========================================================================
-
-    // Retrieve the message history of the DID.
-    const history1 = await client.resolveHistory(doc.id());
-
-    // The history shows two documents in the integration chain.
-    console.log(`History (1): ${JSON.stringify(history1, null, 2)}`);
-
-    // ===========================================================================
-    // Integration Chain Update 2
-    // ===========================================================================
-
-    // Publish a second integration chain update
-    let intDoc2 = Document.fromJSON(intDoc1.toJSON());
-
-    // Remove the #keys-1 VerificationMethod
-    intDoc2.removeMethod(intDoc2.id().toUrl().join("#keys-1"));
-
-    // Remove the #linked-domain-1 Service
-    intDoc2.removeService(intDoc2.id().toUrl().join("#linked-domain-1"));
-
-    // Add a VerificationMethod with a new KeyPair, called "keys-2"
-    const keys2 = new KeyPair(KeyType.Ed25519);
-    const method2 = new VerificationMethod(intDoc2.id(), keys2.type(), keys2.public(), "keys-2");
-    intDoc2.insertMethod(method2, MethodScope.VerificationMethod());
-
-    // Note: the `previous_message_id` points to the `message_id` of the last integration chain
-    //       update.
-    intDoc2.setMetadataPreviousMessageId(intReceipt1.messageId());
-    intDoc2.setMetadataUpdated(Timestamp.nowUTC());
-    intDoc2.signSelf(key, intDoc2.defaultSigningMethod().id());
-    const intReceipt2 = await client.publishDocument(intDoc2);
-
-    // Log the results.
-    console.log(`Int. Chain Update (2): ${clientConfig.explorer.messageUrl(intReceipt2.messageId())}`);
-
-    // ===========================================================================
-    // DID History 2
-    // ===========================================================================
-
-    // Retrieve the updated message history of the DID.
-    const history2 = await client.resolveHistory(doc.id());
-
-    // The history now shows three documents in the integration chain.
-    console.log(`History (2): ${JSON.stringify(history2, null, 2)}`);
-}
-
-export {resolveHistory};
diff --git a/bindings/wasm/examples-stardust/src/tests/ex0_create_did.ts b/bindings/wasm/examples/src/tests/ex0_create_did.ts
similarity index 100%
rename from bindings/wasm/examples-stardust/src/tests/ex0_create_did.ts
rename to bindings/wasm/examples/src/tests/ex0_create_did.ts
diff --git a/bindings/wasm/examples-stardust/src/tests/ex1_update_did.ts b/bindings/wasm/examples/src/tests/ex1_update_did.ts
similarity index 100%
rename from bindings/wasm/examples-stardust/src/tests/ex1_update_did.ts
rename to bindings/wasm/examples/src/tests/ex1_update_did.ts
diff --git a/bindings/wasm/examples-stardust/src/tests/ex2_resolve_did.ts b/bindings/wasm/examples/src/tests/ex2_resolve_did.ts
similarity index 100%
rename from bindings/wasm/examples-stardust/src/tests/ex2_resolve_did.ts
rename to bindings/wasm/examples/src/tests/ex2_resolve_did.ts
diff --git a/bindings/wasm/examples-stardust/src/tests/ex3_deactivate_did.ts b/bindings/wasm/examples/src/tests/ex3_deactivate_did.ts
similarity index 100%
rename from bindings/wasm/examples-stardust/src/tests/ex3_deactivate_did.ts
rename to bindings/wasm/examples/src/tests/ex3_deactivate_did.ts
diff --git a/bindings/wasm/examples-stardust/src/tests/ex4_delete_did.ts b/bindings/wasm/examples/src/tests/ex4_delete_did.ts
similarity index 100%
rename from bindings/wasm/examples-stardust/src/tests/ex4_delete_did.ts
rename to bindings/wasm/examples/src/tests/ex4_delete_did.ts
diff --git a/bindings/wasm/examples/src/tests/node/createIdentity.js b/bindings/wasm/examples/src/tests/node/createIdentity.js
deleted file mode 100644
index 068023725d..0000000000
--- a/bindings/wasm/examples/src/tests/node/createIdentity.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import {createIdentity} from "../../create_did";
-import {CLIENT_CONFIG} from "../../config";
-
-// Only verifies that no uncaught exceptions are thrown, including syntax errors etc.
-describe("Test node examples", function () {
-    it("Create Identity", async () => {
-        await createIdentity(CLIENT_CONFIG);
-    });
-})
diff --git a/bindings/wasm/examples/src/tests/node/keyExchange.js b/bindings/wasm/examples/src/tests/node/keyExchange.js
deleted file mode 100644
index a2a807fa5c..0000000000
--- a/bindings/wasm/examples/src/tests/node/keyExchange.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import {CLIENT_CONFIG} from "../../config";
-import {keyExchange} from "../../key_exchange";
-
-// Only verifies that no uncaught exceptions are thrown, including syntax errors etc.
-describe("Test node examples", function () {
-    it("Key Exchange", async () => {
-        await keyExchange(CLIENT_CONFIG);
-    });
-})
diff --git a/bindings/wasm/examples/src/tests/node/manipulateIdentity.js b/bindings/wasm/examples/src/tests/node/manipulateIdentity.js
deleted file mode 100644
index b17bceb899..0000000000
--- a/bindings/wasm/examples/src/tests/node/manipulateIdentity.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import {manipulateIdentity} from "../../manipulate_did";
-import {CLIENT_CONFIG} from "../../config";
-
-// Only verifies that no uncaught exceptions are thrown, including syntax errors etc.
-describe("Test node examples", function () {
-    it("Manipulate Identity", async () => {
-        await manipulateIdentity(CLIENT_CONFIG);
-    });
-})
diff --git a/bindings/wasm/examples/src/tests/node/privateTangle.js b/bindings/wasm/examples/src/tests/node/privateTangle.js
deleted file mode 100644
index 447ac98f53..0000000000
--- a/bindings/wasm/examples/src/tests/node/privateTangle.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import * as assert from "assert";
-import {privateTangle} from "../../private_tangle";
-
-// Only verifies that no uncaught exceptions are thrown, including syntax errors etc.
-describe("Test node examples", function () {
-    it("Private Tangle", async () => {
-        try {
-            await privateTangle()
-            throw new Error("Did not throw.")
-        } catch (err) {
-            // Example is expected to throw an error because no private Tangle is running
-            assert.strictEqual(err.name, "ClientError")
-            assert.strictEqual(err.message.includes("error sending request"), true)
-        }
-    });
-})
diff --git a/bindings/wasm/examples/src/tests/node/resolveHistory.js b/bindings/wasm/examples/src/tests/node/resolveHistory.js
deleted file mode 100644
index 403451790b..0000000000
--- a/bindings/wasm/examples/src/tests/node/resolveHistory.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import {resolveHistory} from "../../resolve_history";
-import {CLIENT_CONFIG} from "../../config";
-
-// Only verifies that no uncaught exceptions are thrown, including syntax errors etc.
-describe("Test node examples", function () {
-    it("Resolve History", async () => {
-        await resolveHistory(CLIENT_CONFIG);
-    });
-})
diff --git a/bindings/wasm/examples/src/tests/node/resolveIdentity.js b/bindings/wasm/examples/src/tests/node/resolveIdentity.js
deleted file mode 100644
index 69b1f2dcde..0000000000
--- a/bindings/wasm/examples/src/tests/node/resolveIdentity.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import {resolveIdentity} from "../../resolve_did";
-import {CLIENT_CONFIG} from "../../config";
-
-// Only verifies that no uncaught exceptions are thrown, including syntax errors etc.
-describe("Test node examples", function () {
-    it("Resolve Identity", async () => {
-        await resolveIdentity(CLIENT_CONFIG);
-    });
-})
diff --git a/bindings/wasm/examples/src/utils_web.js b/bindings/wasm/examples/src/utils_web.js
deleted file mode 100644
index 08ef401b38..0000000000
--- a/bindings/wasm/examples/src/utils_web.js
+++ /dev/null
@@ -1,78 +0,0 @@
-import * as identity from "@iota/identity-wasm";
-
-export const LINK_REGEX = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
-
-/**
- * Loads the identity wasm library.
- *
- * @returns {Promise}
- */
-export async function initIdentity(path = "./identity_wasm_bg.wasm") {
-    console.log("Initialization started...");
-    await identity.init(path);
-    console.log("Initialization success!");
-}
-
-/**
- * Returns the default client configuration to connect to the IOTA mainnet.
- *
- * N.B. initIdentity() must be called prior to this function.
- *
- * @returns {{network: Network, explorer: ExplorerUrl}}
- */
-export function defaultClientConfig() {
-    const mainnet = identity.Network.mainnet();
-    const explorer = identity.ExplorerUrl.mainnet();
-    return {
-        network: mainnet,
-        explorer: explorer,
-    }
-}
-
-/**
- * logs a string to the output window
- *
- * @param {string} message
- */
-export function logToScreen(message) {
-    document.querySelector("#content").innerHTML =
-        document.querySelector("#content").innerHTML +
-        message +
-        "
-------------------------------------
"; -} - -/** - * parses a string for urls and wraps them with link tags - * - * @param {string} inputText - * @returns {string} - */ -export function linkify(inputText) { - return inputText.replace(LINK_REGEX, '$1'); -} - -/** - * - * @param {object} obj - */ -export function logObjectToScreen(obj) { - logToScreen("
" + JSON.stringify(obj, null, 4) + "
"); -} - -export function setupDOMLog() { - var orig = console.log; - - console.log = function () { - Array.from(arguments).forEach(argument => { - if (typeof argument === 'object') { - return logObjectToScreen(argument); - } else if (typeof argument === 'string' && argument.match(LINK_REGEX)) { - return logToScreen(linkify(argument)); - } - logToScreen(argument); - }); - - orig.apply(console, arguments); - }; - -} diff --git a/bindings/wasm/examples/src/web.js b/bindings/wasm/examples/src/web.js deleted file mode 100644 index 90f0b6a276..0000000000 --- a/bindings/wasm/examples/src/web.js +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import {defaultClientConfig, initIdentity, setupDOMLog} from "./utils_web.js"; -import {createIdentity} from "./create_did.js"; -import {manipulateIdentity} from "./manipulate_did.js"; -import {keyExchange} from "./key_exchange.js"; -import {resolveIdentity} from "./resolve_did"; -import {privateTangle} from "./private_tangle.js"; -import {resolveHistory} from "./resolve_history.js"; - -export { - initIdentity, - defaultClientConfig, - createIdentity, - manipulateIdentity, - keyExchange, - resolveIdentity, - privateTangle, - resolveHistory, -}; - -window.onload = async () => { - - setupDOMLog(); - - await initIdentity(); - const CLIENT_CONFIG = defaultClientConfig(); - - //handle create identity on click event - document - .querySelector("#create-identity-btn") - .addEventListener("click", () => createIdentity(CLIENT_CONFIG)); - - //handle resolve DID on click event - document - .querySelector("#resolve-did-btn") - .addEventListener("click", async () => { - const inputDid = document.querySelector("#resolve-did-input").value; - const result = await resolveIdentity(CLIENT_CONFIG, inputDid); - console.log(result); - }); - - //handle manipulate DID on click event - document - .querySelector("#manipulate_did_btn") - .addEventListener("click", () => manipulateIdentity(CLIENT_CONFIG)); - - - //handle private tangle DID creation on click event - document - .querySelector("#private_tangle_btn") - .addEventListener("click", () => { - const restURL = document.querySelector("#create-private-rest-url").value; - const networkName = document.querySelector("#create-private-network-name").value; - privateTangle(restURL, networkName); - }); - - //handle key exchange example on click event - document - .querySelector("#key_exchange_btn") - .addEventListener("click", () => keyExchange(CLIENT_CONFIG)); - - //handle resolve history on click event - document - .querySelector("#did_history_btn") - .addEventListener("click", () => resolveHistory(CLIENT_CONFIG)); - -}; diff --git a/bindings/wasm/examples/webpack.config.js b/bindings/wasm/examples/webpack.config.js deleted file mode 100644 index 4afba93320..0000000000 --- a/bindings/wasm/examples/webpack.config.js +++ /dev/null @@ -1,87 +0,0 @@ -const path = require('path'); -const glob = require('glob'); -const CopyWebPlugin = require('copy-webpack-plugin'); -const serverConfig = { - target: 'node', - entry: './examples/src/node.js', - devtool: "source-map", - output: { - path: path.resolve(__dirname, 'dist'), - filename: 'node.js', - }, - externals: [ - function ({ context, request }, callback) { - if (/^@iota\/identity-wasm$/.test(request)) { - // Externalize to a commonjs module - return callback(null, 'commonjs ' + path.resolve(__dirname, '../node/identity_wasm.js')); - } - - // Continue without externalizing the import - callback(); - }, - ], -}; - -const serverTestConfig = { - target: 'node', - entry: glob.sync('./examples/src/tests/node/**.js').reduce(function(obj, el){ - obj[path.parse(el).name] = el; - return obj - },{}), - devtool: "source-map", - output: { - path: path.resolve(__dirname, 'dist'), - filename: 'tests/node/[name].js' - }, - externals: [ - function ({ context, request }, callback) { - if (/^@iota\/identity-wasm$/.test(request)) { - // Externalize to a commonjs module - return callback(null, 'commonjs ' + path.resolve(__dirname, '../node/identity_wasm.js')); - } - - // Continue without externalizing the import - callback(); - }, - ], -}; - -const clientConfig = { - target: 'web', - entry: './examples/src/web.js', - devtool: "source-map", - output: { - path: path.resolve(__dirname, 'dist'), - filename: 'web.js', - library: { - type: 'module', - }, - }, - experiments: { - topLevelAwait: true, - outputModule: true, - }, - resolve: { - alias: { - '@iota/identity-wasm': path.resolve(__dirname, '../web/identity_wasm.js'), - }, - }, - plugins: [ - new CopyWebPlugin({ - patterns: [ - { - from: path.resolve(__dirname, "./src/index.html"), - to: path.resolve(__dirname, "dist"), - }, - - { - from: path.resolve(__dirname, "../web/identity_wasm_bg.wasm"), - to: path.resolve(__dirname, "dist"), - } - ] - - }), - ], -}; - -module.exports = [serverConfig, serverTestConfig, clientConfig]; diff --git a/bindings/wasm/lib/index.ts b/bindings/wasm/lib/index.ts index beaf6af755..5d81fd9b2d 100644 --- a/bindings/wasm/lib/index.ts +++ b/bindings/wasm/lib/index.ts @@ -1,6 +1,6 @@ // Copyright 2021-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -export * from './stardust_identity_client'; +export * from './iota_identity_client'; export * from '~identity_wasm'; diff --git a/bindings/wasm/lib/stardust_identity_client.ts b/bindings/wasm/lib/iota_identity_client.ts similarity index 78% rename from bindings/wasm/lib/stardust_identity_client.ts rename to bindings/wasm/lib/iota_identity_client.ts index 9b2a046228..346f6e2efd 100644 --- a/bindings/wasm/lib/stardust_identity_client.ts +++ b/bindings/wasm/lib/iota_identity_client.ts @@ -1,9 +1,9 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import {IStardustIdentityClient, StardustDID, StardustDocument, StardustIdentityClientExt} from '~identity_wasm'; +import { IIotaIdentityClient, IotaDID, IotaDocument, IotaIdentityClientExt } from '~identity_wasm'; -import type {Client, INodeInfoWrapper, SecretManager} from '~iota-client-wasm'; +import type { Client, INodeInfoWrapper, SecretManager } from '~iota-client-wasm'; import { ADDRESS_UNLOCK_CONDITION_TYPE, AddressTypes, @@ -15,8 +15,8 @@ import { TransactionHelper } from '@iota/iota.js'; -/** Provides operations for IOTA UTXO DID Documents with Alias Outputs. */ -export class StardustIdentityClient implements IStardustIdentityClient { +/** Provides operations for IOTA DID Documents with Alias Outputs. */ +export class IotaIdentityClient implements IIotaIdentityClient { client: Client; constructor(client: Client) { @@ -56,8 +56,8 @@ export class StardustIdentityClient implements IStardustIdentityClient { * * NOTE: this does *not* publish the Alias Output. */ - async newDidOutput(address: AddressTypes, document: StardustDocument, rentStructure?: IRent): Promise { - return await StardustIdentityClientExt.newDidOutput(this, address, document, rentStructure); + async newDidOutput(address: AddressTypes, document: IotaDocument, rentStructure?: IRent): Promise { + return await IotaIdentityClientExt.newDidOutput(this, address, document, rentStructure); } /** Fetches the associated Alias Output and updates it with `document` in its state metadata. @@ -66,8 +66,8 @@ export class StardustIdentityClient implements IStardustIdentityClient { * * NOTE: this does *not* publish the updated Alias Output. */ - async updateDidOutput(document: StardustDocument): Promise { - return await StardustIdentityClientExt.updateDidOutput(this, document); + async updateDidOutput(document: IotaDocument): Promise { + return await IotaIdentityClientExt.updateDidOutput(this, document); } /** Removes the DID document from the state metadata of its Alias Output, @@ -79,20 +79,20 @@ export class StardustIdentityClient implements IStardustIdentityClient { * * NOTE: this does *not* publish the updated Alias Output. */ - async deactivateDidOutput(did: StardustDID): Promise { - return await StardustIdentityClientExt.deactivateDidOutput(this, did); + async deactivateDidOutput(did: IotaDID): Promise { + return await IotaIdentityClientExt.deactivateDidOutput(this, did); } - /** Resolve a {@link StardustDocument}. Returns an empty, deactivated document if the state + /** Resolve a {@link IotaDocument}. Returns an empty, deactivated document if the state * metadata of the Alias Output is empty. */ - async resolveDid(did: StardustDID): Promise { - return await StardustIdentityClientExt.resolveDid(this, did); + async resolveDid(did: IotaDID): Promise { + return await IotaIdentityClientExt.resolveDid(this, did); } /** Fetches the Alias Output associated with the given DID. */ - async resolveDidOutput(did: StardustDID): Promise { - return await StardustIdentityClientExt.resolveDidOutput(this, did); + async resolveDidOutput(did: IotaDID): Promise { + return await IotaIdentityClientExt.resolveDidOutput(this, did); } /** Publish the given `aliasOutput` with the provided ` `, and returns @@ -104,7 +104,7 @@ export class StardustIdentityClient implements IStardustIdentityClient { * * This method modifies the on-ledger state. */ - async publishDidOutput(secretManager: SecretManager, aliasOutput: IAliasOutput): Promise { + async publishDidOutput(secretManager: SecretManager, aliasOutput: IAliasOutput): Promise { const networkHrp = await this.getNetworkHrp(); // Publish block. @@ -114,7 +114,7 @@ export class StardustIdentityClient implements IStardustIdentityClient { await this.client.retryUntilIncluded(blockId); // Extract document with computed AliasId. - const documents = StardustDocument.unpackFromBlock(networkHrp, block); + const documents = IotaDocument.unpackFromBlock(networkHrp, block); if (documents.length < 1) { throw new Error("publishDidOutput: no DID document in transaction payload"); } @@ -130,7 +130,7 @@ export class StardustIdentityClient implements IStardustIdentityClient { * * This destroys the Alias Output and DID document, rendering them permanently unrecoverable. */ - async deleteDidOutput(secretManager: SecretManager, address: AddressTypes, did: StardustDID) { + async deleteDidOutput(secretManager: SecretManager, address: AddressTypes, did: IotaDID) { const networkHrp = await this.getNetworkHrp(); if (networkHrp !== did.networkStr()) { throw new Error("deleteDidOutput: DID network mismatch, client expected `" + networkHrp + "`, DID network is `" + did.networkStr() + "`"); diff --git a/bindings/wasm/package.json b/bindings/wasm/package.json index 1ee61ed043..48ce064f7f 100644 --- a/bindings/wasm/package.json +++ b/bindings/wasm/package.json @@ -16,17 +16,14 @@ "build:nodejs": "npm run build:src && npm run bundle:nodejs && wasm-opt -O node/identity_wasm_bg.wasm -o node/identity_wasm_bg.wasm", "build:web": "npm run build:src && npm run bundle:web && wasm-opt -O web/identity_wasm_bg.wasm -o web/identity_wasm_bg.wasm", "build:docs": "node ./build/docs", - "build:examples": "webpack --config ./examples/webpack.config.js --mode=production", "build": "npm run build:web && npm run build:nodejs && npm run build:docs", - "example:node": "node examples/dist/node.js", "example:browser": "http-server ./examples/dist/ -c-1 -o ", "example:account": "ts-node ./examples-account/src/node.ts", - "example:stardust": "ts-node ./examples-stardust/src/main.ts", + "example:node": "ts-node ./examples/src/main.ts", "test": "npm run test:unit && npm run test:unit:node && npm run test:examples", - "test:examples": "concurrently -g --timings \"npm:test:node\" \"npm:test:account:node\" \"npm:test:stardust\" \"npm:test:browser:parallel\" \"npm:test:readme\"", - "test:node": "mocha ./examples/dist/tests/node/*.js --parallel --jobs 4 --retries 3 --timeout 180000 --exit", + "test:examples": "npm run test:node && npm run test:readme", "test:account:node": "ts-mocha -p tsconfig.node.json ./examples-account/src/tests/*.ts --parallel --jobs 4 --retries 3 --timeout 180000 --exit", - "test:stardust": "ts-mocha -p tsconfig.node.json ./examples-stardust/src/tests/*.ts --parallel --jobs 4 --retries 3 --timeout 180000 --exit", + "test:node": "ts-mocha -p tsconfig.node.json ./examples/src/tests/*.ts --parallel --jobs 4 --retries 3 --timeout 180000 --exit", "test:browser:parallel": "cypress-parallel -s test:browser -t 4 -d cypress/e2e -a '\"--quiet\"'", "test:browser": "cypress run --headless", "test:readme": "mocha ./tests/txm_readme.js --retries 3 --timeout 180000 --exit", diff --git a/bindings/wasm/src/account/storage/traits.rs b/bindings/wasm/src/account/storage/traits.rs index f63fe1f75e..5a6d549007 100644 --- a/bindings/wasm/src/account/storage/traits.rs +++ b/bindings/wasm/src/account/storage/traits.rs @@ -16,7 +16,7 @@ use identity_iota::account_storage::Storage; use identity_iota::crypto::PrivateKey; use identity_iota::crypto::PublicKey; use identity_iota::did::CoreDID; -use identity_iota::iota_core::NetworkName; +use identity_iota::iota::NetworkName; use identity_iota::prelude::KeyType; use js_sys::Array; use js_sys::Promise; diff --git a/bindings/wasm/src/account/wasm_account/account.rs b/bindings/wasm/src/account/wasm_account/account.rs index 20a65fb4bc..322cc8e49e 100644 --- a/bindings/wasm/src/account/wasm_account/account.rs +++ b/bindings/wasm/src/account/wasm_account/account.rs @@ -19,8 +19,8 @@ use identity_iota::credential::Presentation; use identity_iota::crypto::ProofOptions; use identity_iota::crypto::PublicKey; use identity_iota::did::verifiable::VerifiableProperties; -use identity_iota::iota_core::IotaDID; -use identity_iota::iota_core::IotaDocument; +use identity_iota::iota::IotaDID; +use identity_iota::iota::IotaDocument; use js_sys::Promise; use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; diff --git a/bindings/wasm/src/account/wasm_account/account_builder.rs b/bindings/wasm/src/account/wasm_account/account_builder.rs index 97c6bf0c7f..9fb722cdf9 100644 --- a/bindings/wasm/src/account/wasm_account/account_builder.rs +++ b/bindings/wasm/src/account/wasm_account/account_builder.rs @@ -8,7 +8,7 @@ use identity_iota::account::AccountBuilder; use identity_iota::account::IdentitySetup; use identity_iota::client::Client; use identity_iota::client::ClientBuilder; -use identity_iota::iota_core::IotaDID; +use identity_iota::iota::IotaDID; use js_sys::Promise; use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; diff --git a/bindings/wasm/src/account/wasm_account/update/set_controller.rs b/bindings/wasm/src/account/wasm_account/update/set_controller.rs index 30da579b15..f7cd06069b 100644 --- a/bindings/wasm/src/account/wasm_account/update/set_controller.rs +++ b/bindings/wasm/src/account/wasm_account/update/set_controller.rs @@ -7,7 +7,7 @@ use std::rc::Rc; use identity_iota::core::OneOrMany; use identity_iota::core::OneOrSet; use identity_iota::core::OrderedSet; -use identity_iota::iota_core::IotaDID; +use identity_iota::iota::IotaDID; use js_sys::Promise; use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; diff --git a/bindings/wasm/src/chain/document_history.rs b/bindings/wasm/src/chain/document_history.rs deleted file mode 100644 index 87a97dc653..0000000000 --- a/bindings/wasm/src/chain/document_history.rs +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use crate::common::ArrayString; -use identity_iota::client::ChainHistory; -use identity_iota::client::DocumentHistory; -use identity_iota::client::ResolvedIotaDocument; -use identity_iota::iota_core::DiffMessage; -use wasm_bindgen::prelude::*; -use wasm_bindgen::JsCast; - -use crate::did::ArrayResolvedDocument; -use crate::did::WasmDiffMessage; -use crate::did::WasmResolvedDocument; -use crate::error::Result; -use crate::error::WasmResult; - -/// A DID Document's history and current state. -#[wasm_bindgen(js_name = DocumentHistory, inspectable)] -pub struct WasmDocumentHistory(DocumentHistory); - -// Workaround for Typescript type annotations on async function returns and arrays. -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(typescript_type = "Promise")] - pub type PromiseDocumentHistory; - - #[wasm_bindgen(typescript_type = "Promise")] - pub type PromiseIntegrationChainHistory; - - #[wasm_bindgen(typescript_type = "Promise")] - pub type PromiseDiffChainHistory; - - #[wasm_bindgen(typescript_type = "Array")] - pub type ArrayDiffMessage; -} - -#[wasm_bindgen(js_class = DocumentHistory)] -impl WasmDocumentHistory { - /// Returns an `Array` of integration chain `Documents`. - /// - /// NOTE: clones the data. - #[wasm_bindgen(js_name = integrationChainData)] - pub fn integration_chain_data(&self) -> ArrayResolvedDocument { - self - .0 - .integration_chain_data - .iter() - .cloned() - .map(WasmResolvedDocument::from) - .map(JsValue::from) - .collect::() - .unchecked_into::() - } - - /// Returns an `Array` of message id strings for "spam" messages on the same index - /// as the integration chain. - /// - /// NOTE: clones the data. - #[wasm_bindgen(js_name = integrationChainSpam)] - pub fn integration_chain_spam(&self) -> ArrayString { - self - .0 - .integration_chain_spam - .iter() - .cloned() - .map(|message_id| message_id.to_string()) - .map(JsValue::from) - .collect::() - .unchecked_into::() - } - - /// Returns an `Array` of diff chain `DiffMessages`. - /// - /// NOTE: clones the data. - /// - /// @deprecated since 0.5.0, diff chain features are slated for removal. - #[wasm_bindgen(js_name = diffChainData)] - pub fn diff_chain_data(&self) -> ArrayDiffMessage { - self - .0 - .diff_chain_data - .iter() - .cloned() - .map(WasmDiffMessage::from) - .map(JsValue::from) - .collect::() - .unchecked_into::() - } - - /// Returns an `Array` of message id strings for "spam" messages on the same index - /// as the diff chain. - /// - /// NOTE: clones the data. - /// - /// @deprecated since 0.5.0, diff chain features are slated for removal. - #[wasm_bindgen(js_name = diffChainSpam)] - pub fn diff_chain_spam(&self) -> ArrayString { - self - .0 - .diff_chain_spam - .iter() - .cloned() - .map(|message_id| message_id.to_string()) - .map(JsValue::from) - .collect::() - .unchecked_into::() - } -} - -impl_wasm_json!(WasmDocumentHistory, DocumentHistory); -impl_wasm_clone!(WasmDocumentHistory, DocumentHistory); - -impl From for WasmDocumentHistory { - fn from(document_history: DocumentHistory) -> Self { - Self(document_history) - } -} - -#[wasm_bindgen(inspectable)] -pub struct IntegrationChainHistory(ChainHistory); - -/// @deprecated since 0.5.0, diff chain features are slated for removal. -#[wasm_bindgen(inspectable)] -pub struct DiffChainHistory(ChainHistory); - -#[wasm_bindgen] -impl IntegrationChainHistory { - /// Returns an `Array` of the integration chain `Documents`. - /// - /// NOTE: this clones the field. - #[wasm_bindgen(js_name = chainData)] - pub fn chain_data(&self) -> ArrayResolvedDocument { - self - .0 - .chain_data - .iter() - .cloned() - .map(WasmResolvedDocument::from) - .map(JsValue::from) - .collect::() - .unchecked_into::() - } -} - -#[wasm_bindgen] -impl DiffChainHistory { - /// Returns an `Array` of the diff chain `DiffMessages`. - /// - /// NOTE: this clones the field. - #[wasm_bindgen(js_name = chainData)] - pub fn chain_data(&self) -> ArrayDiffMessage { - self - .0 - .chain_data - .iter() - .cloned() - .map(WasmDiffMessage::from) - .map(JsValue::from) - .collect::() - .unchecked_into::() - } -} - -macro_rules! impl_wasm_chain_history { - ($ident:ident, $ty:ty, $wasm_ty:ty) => { - #[wasm_bindgen] - impl $ident { - /// Returns an `Array` of `MessageIds` as strings. - /// - /// NOTE: this clones the field. - #[wasm_bindgen] - pub fn spam(&self) -> ArrayString { - self - .0 - .spam - .iter() - .cloned() - .map(|message_id| message_id.to_string()) - .map(JsValue::from) - .collect::() - .unchecked_into::() - } - - /// Serializes as a JSON object. - #[wasm_bindgen(js_name = toJSON)] - pub fn to_json(&self) -> Result { - JsValue::from_serde(&self.0).wasm_result() - } - - /// Deserializes from a JSON object. - #[wasm_bindgen(js_name = fromJSON)] - pub fn from_json(json: &JsValue) -> Result<$ident> { - json.into_serde().map(Self).wasm_result() - } - } - - impl From> for $ident { - fn from(chain_history: ChainHistory<$ty>) -> Self { - $ident(chain_history) - } - } - }; -} - -impl_wasm_chain_history!(IntegrationChainHistory, ResolvedIotaDocument, WasmResolvedDocument); -impl_wasm_chain_history!(DiffChainHistory, DiffMessage, WasmDiffMessage); diff --git a/bindings/wasm/src/chain/mod.rs b/bindings/wasm/src/chain/mod.rs deleted file mode 100644 index c566b05bb0..0000000000 --- a/bindings/wasm/src/chain/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -pub use document_history::*; - -mod document_history; diff --git a/bindings/wasm/src/credential/credential_builder.rs b/bindings/wasm/src/credential/credential_builder.rs index 898c698c04..1aa8c0ecac 100644 --- a/bindings/wasm/src/credential/credential_builder.rs +++ b/bindings/wasm/src/credential/credential_builder.rs @@ -125,7 +125,7 @@ struct ICredentialHelper { #[typescript(optional = false, name = "credentialSubject", type = "Subject | Array")] credential_subject: Option>, /// A reference to the issuer of the `Credential`. - #[typescript(optional = false, type = "string | CoreDID | IotaDID | StardustDID | Issuer")] + #[typescript(optional = false, type = "string | CoreDID | IotaDID | Issuer")] issuer: Option, /// A timestamp of when the `Credential` becomes valid. Defaults to the current datetime. #[typescript(name = "issuanceDate", type = "Timestamp")] diff --git a/bindings/wasm/src/credential/presentation_builder.rs b/bindings/wasm/src/credential/presentation_builder.rs index ea5a7f331a..f93cccb6bf 100644 --- a/bindings/wasm/src/credential/presentation_builder.rs +++ b/bindings/wasm/src/credential/presentation_builder.rs @@ -95,7 +95,7 @@ struct IPresentationHelper { )] verifiable_credential: Option>, /// The entity that generated the `Presentation`. - #[typescript(type = "string | CoreDID | IotaDID | StardustDID")] + #[typescript(type = "string | CoreDID | IotaDID ")] holder: Option, /// Service(s) used to refresh an expired {@link Credential} in the `Presentation`. #[typescript(name = "refreshService", type = "RefreshService | Array")] diff --git a/bindings/wasm/src/credential/types.rs b/bindings/wasm/src/credential/types.rs index 719ce1b38d..3c37378c0b 100644 --- a/bindings/wasm/src/credential/types.rs +++ b/bindings/wasm/src/credential/types.rs @@ -119,7 +119,7 @@ const I_SUBJECT: &'static str = r#" [More Info](https://www.w3.org/TR/vc-data-model/#credential-subject) */ interface Subject { /** A URI identifying the credential subject. */ - readonly id?: string | CoreDID | StardustDID; + readonly id?: string | CoreDID | IotaDID; /** Additional properties of the credential subject. */ readonly [properties: string]: unknown; }"#; diff --git a/bindings/wasm/src/did/mod.rs b/bindings/wasm/src/did/mod.rs index be0323a99d..f90a69d336 100644 --- a/bindings/wasm/src/did/mod.rs +++ b/bindings/wasm/src/did/mod.rs @@ -1,47 +1,26 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +pub use self::service_common::IService; +pub use self::service_common::UServiceEndpoint; pub use self::wasm_core_did::WasmCoreDID; pub use self::wasm_core_document::WasmCoreDocument; -pub use self::wasm_did_url::WasmDIDUrl; -pub use self::wasm_diff_message::WasmDiffMessage; -pub use self::wasm_document::WasmDocument; -pub use self::wasm_document_metadata::WasmDocumentMetadata; -pub use self::wasm_iota_did::UWasmIotaDID; -pub use self::wasm_iota_did::WasmIotaDID; pub use self::wasm_method_data::WasmMethodData; pub use self::wasm_method_relationship::WasmMethodRelationship; pub use self::wasm_method_scope::OptionMethodScope; pub use self::wasm_method_scope::RefMethodScope; pub use self::wasm_method_scope::WasmMethodScope; pub use self::wasm_method_type::WasmMethodType; -pub use self::wasm_resolved_document::ArrayDocumentOrResolvedDocument; -pub use self::wasm_resolved_document::ArrayResolvedDocument; -pub use self::wasm_resolved_document::DocumentOrResolvedDocument; -pub use self::wasm_resolved_document::PromiseArrayResolvedDocument; -pub use self::wasm_resolved_document::PromiseResolvedDocument; -pub use self::wasm_resolved_document::WasmResolvedDocument; -pub use self::wasm_service::IService; -pub use self::wasm_service::UServiceEndpoint; -pub use self::wasm_service::WasmService; -pub use self::wasm_verification_method::WasmVerificationMethod; pub use self::wasm_verifier_options::WasmVerifierOptions; +mod service_common; mod wasm_core_did; mod wasm_core_document; mod wasm_core_service; mod wasm_core_url; mod wasm_core_verification_method; -mod wasm_did_url; -mod wasm_diff_message; -mod wasm_document; -mod wasm_document_metadata; -mod wasm_iota_did; mod wasm_method_data; mod wasm_method_relationship; mod wasm_method_scope; mod wasm_method_type; -mod wasm_resolved_document; -mod wasm_service; -mod wasm_verification_method; mod wasm_verifier_options; diff --git a/bindings/wasm/src/did/service_common.rs b/bindings/wasm/src/did/service_common.rs new file mode 100644 index 0000000000..517d2cb906 --- /dev/null +++ b/bindings/wasm/src/did/service_common.rs @@ -0,0 +1,90 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use identity_iota::did::ServiceEndpoint; +use wasm_bindgen::prelude::*; +use wasm_bindgen::JsCast; + +impl From for UServiceEndpoint { + fn from(endpoint: ServiceEndpoint) -> Self { + UServiceEndpoint::from(&endpoint) + } +} + +impl From<&ServiceEndpoint> for UServiceEndpoint { + fn from(endpoint: &ServiceEndpoint) -> Self { + match endpoint { + // string + ServiceEndpoint::One(url) => JsValue::from_str(url.as_str()).unchecked_into::(), + // string[] + ServiceEndpoint::Set(set) => set + .iter() + .map(|url| JsValue::from_str(url.as_str())) + .collect::() + .unchecked_into::(), + // Map + ServiceEndpoint::Map(map) => { + let js_map: js_sys::Map = js_sys::Map::new(); + for (key, urls) in map.into_iter() { + js_map.set( + &JsValue::from_str(key.as_str()), + &urls + .iter() + .map(|url| JsValue::from_str(url.as_str())) + .collect::(), + ); + } + js_map.unchecked_into::() + } + } + } +} + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(typescript_type = "string | string[] | Map")] + pub type UServiceEndpoint; +} + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(typescript_type = "IService")] + pub type IService; + + #[wasm_bindgen(method, getter, js_name = type)] + pub fn type_(this: &IService) -> JsValue; + + #[wasm_bindgen(method, getter, js_name = serviceEndpoint)] + pub fn service_endpoint(this: &IService) -> JsValue; + + #[wasm_bindgen(method, getter)] + pub fn properties(this: &IService) -> JsValue; +} + +#[wasm_bindgen(typescript_custom_section)] +const I_SERVICE: &'static str = r#" +/** + * Base `Service` properties. + */ +interface IService { + /** + * Type of service. + * + * E.g. "LinkedDomains" or "DIDCommMessaging". + */ + readonly type: string | string[]; + + /** + * A URL, set of URLs, or map of URL sets. + * + * NOTE: throws an error if any entry is not a valid URL string. List entries must be unique. + */ + readonly serviceEndpoint: string | string[] | Map | Record; + + /** + * Additional custom properties to embed in the service. + * + * WARNING: entries may overwrite existing fields and result in invalid documents. + */ + readonly properties?: Map | Record; +}"#; diff --git a/bindings/wasm/src/did/wasm_core_did.rs b/bindings/wasm/src/did/wasm_core_did.rs index a505b8f2d9..46c45a2de3 100644 --- a/bindings/wasm/src/did/wasm_core_did.rs +++ b/bindings/wasm/src/did/wasm_core_did.rs @@ -6,7 +6,6 @@ use identity_iota::did::DID; use wasm_bindgen::prelude::*; use crate::did::wasm_core_url::WasmCoreDIDUrl; -use crate::did::WasmIotaDID; use crate::error::Result; use crate::error::WasmResult; @@ -128,10 +127,3 @@ impl From for WasmCoreDID { WasmCoreDID(did) } } - -impl From for WasmCoreDID { - fn from(wasm_did: WasmIotaDID) -> Self { - let core_did: CoreDID = wasm_did.0.into(); - core_did.into() - } -} diff --git a/bindings/wasm/src/did/wasm_core_document.rs b/bindings/wasm/src/did/wasm_core_document.rs index 7f0c9a6d62..eb30fb13b6 100644 --- a/bindings/wasm/src/did/wasm_core_document.rs +++ b/bindings/wasm/src/did/wasm_core_document.rs @@ -477,34 +477,34 @@ extern "C" { #[typescript(name = "ICoreDocument", readonly, optional)] #[allow(non_snake_case, dead_code)] struct ICoreDocumentHelper { - #[typescript(optional = false, type = "string | CoreDID | StardustDID")] + #[typescript(optional = false, type = "string | CoreDID | IotaDID")] id: Option, - #[typescript(type = "(string | CoreDID | StardustDID)[]")] + #[typescript(type = "(string | CoreDID | IotaDID)[]")] controller: Option>, #[typescript(type = "string[]")] alsoKnownAs: Option>, - #[typescript(type = "(CoreVerificationMethod | StardustVerificationMethod)[]")] + #[typescript(type = "(CoreVerificationMethod | IotaVerificationMethod)[]")] verificationMethod: Option>>, - #[typescript(type = "(CoreVerificationMethod | CoreDIDUrl | StardustVerificationMethod | StardustDIDUrl)[]")] + #[typescript(type = "(CoreVerificationMethod | CoreDIDUrl | IotaVerificationMethod | IotaDIDUrl)[]")] authentication: Option>>, - #[typescript(type = "(CoreVerificationMethod | CoreDIDUrl | StardustVerificationMethod | StardustDIDUrl)[]")] + #[typescript(type = "(CoreVerificationMethod | CoreDIDUrl | IotaVerificationMethod | IotaDIDUrl)[]")] assertionMethod: Option>>, - #[typescript(type = "(CoreVerificationMethod | CoreDIDUrl | StardustVerificationMethod | StardustDIDUrl)[]")] + #[typescript(type = "(CoreVerificationMethod | CoreDIDUrl | IotaVerificationMethod | IotaDIDUrl)[]")] keyAgreement: Option>>, - #[typescript(type = "(CoreVerificationMethod | CoreDIDUrl | StardustVerificationMethod | StardustDIDUrl)[]")] + #[typescript(type = "(CoreVerificationMethod | CoreDIDUrl | IotaVerificationMethod | IotaDIDUrl)[]")] capabilityDelegation: Option>>, - #[typescript(type = "(CoreVerificationMethod | CoreDIDUrl | StardustVerificationMethod | StardustDIDUrl)[]")] + #[typescript(type = "(CoreVerificationMethod | CoreDIDUrl | IotaVerificationMethod | IotaDIDUrl)[]")] capabilityInvocation: Option>>, - #[typescript(type = "(CoreService | StardustService)[]")] + #[typescript(type = "(CoreService | IotaService)[]")] service: Option>>, #[serde(flatten)] diff --git a/bindings/wasm/src/did/wasm_diff_message.rs b/bindings/wasm/src/did/wasm_diff_message.rs deleted file mode 100644 index 033922d4a5..0000000000 --- a/bindings/wasm/src/did/wasm_diff_message.rs +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::str::FromStr; - -use identity_iota::core::ToJson; -use identity_iota::iota_core::DiffMessage; -use identity_iota::iota_core::MessageId; -use wasm_bindgen::prelude::*; - -use crate::crypto::WasmProof; -use crate::did::WasmDocument; -use crate::did::WasmIotaDID; -use crate::error::Result; -use crate::error::WasmResult; - -/// Defines the difference between two DID `Document`s' JSON representations. -/// -/// @deprecated since 0.5.0, diff chain features are slated for removal. -#[wasm_bindgen(js_name = DiffMessage, inspectable)] -pub struct WasmDiffMessage(pub(crate) DiffMessage); - -#[wasm_bindgen(js_class = DiffMessage)] -impl WasmDiffMessage { - /// Returns the DID of the associated DID Document. - /// - /// NOTE: clones the data. - /// - /// @deprecated since 0.5.0, diff chain features are slated for removal. - #[wasm_bindgen] - pub fn id(&self) -> WasmIotaDID { - WasmIotaDID::from(self.0.id().clone()) - } - - /// Returns a copy of the DID of the associated DID Document. - /// - /// @deprecated since 0.5.0, diff chain features are slated for removal. - #[wasm_bindgen] - pub fn did(&self) -> WasmIotaDID { - self.id() - } - - /// Returns a copy of the raw contents of the DID Document diff as a JSON string. - /// - /// @deprecated since 0.5.0, diff chain features are slated for removal. - #[wasm_bindgen] - pub fn diff(&self) -> Result { - self.0.diff().to_json().wasm_result() - } - - /// Returns a copy of the message_id of the DID Document diff. - /// - /// @deprecated since 0.5.0, diff chain features are slated for removal. - #[wasm_bindgen(js_name = messageId)] - pub fn message_id(&self) -> String { - self.0.message_id().to_string() - } - - /// Sets the message_id of the DID Document diff. - /// - /// @deprecated since 0.5.0, diff chain features are slated for removal. - #[wasm_bindgen(js_name = setMessageId)] - pub fn set_message_id(&mut self, message_id: &str) -> Result<()> { - let message_id: MessageId = MessageId::from_str(message_id) - .map_err(identity_iota::iota_core::Error::InvalidMessage) - .wasm_result()?; - self.0.set_message_id(message_id); - Ok(()) - } - - /// Returns a copy of the Tangle message id of the previous DID Document diff. - /// - /// @deprecated since 0.5.0, diff chain features are slated for removal. - #[wasm_bindgen(js_name = previousMessageId)] - pub fn previous_message_id(&self) -> String { - self.0.previous_message_id().to_string() - } - - /// Sets the Tangle message id of the previous DID Document diff. - /// - /// @deprecated since 0.5.0, diff chain features are slated for removal. - #[wasm_bindgen(js_name = setPreviousMessageId)] - pub fn set_previous_message_id(&mut self, message_id: &str) -> Result<()> { - let previous_message_id: MessageId = MessageId::from_str(message_id) - .map_err(identity_iota::iota_core::Error::InvalidMessage) - .wasm_result()?; - self.0.set_previous_message_id(previous_message_id); - Ok(()) - } - - /// Returns a copy of the proof. - /// - /// @deprecated since 0.5.0, diff chain features are slated for removal. - #[wasm_bindgen] - pub fn proof(&self) -> Option { - self.0.proof().cloned().map(WasmProof::from) - } - - /// Returns a new DID Document which is the result of merging `self` - /// with the given Document. - /// - /// @deprecated since 0.5.0, diff chain features are slated for removal. - pub fn merge(&self, document: &WasmDocument) -> Result { - self.0.merge(&document.0).map(WasmDocument).wasm_result() - } -} - -impl_wasm_json!(WasmDiffMessage, DiffMessage); -impl_wasm_clone!(WasmDiffMessage, DiffMessage); - -impl From for WasmDiffMessage { - fn from(document_diff: DiffMessage) -> Self { - Self(document_diff) - } -} diff --git a/bindings/wasm/src/did/wasm_document.rs b/bindings/wasm/src/did/wasm_document.rs deleted file mode 100644 index 1b272bf55f..0000000000 --- a/bindings/wasm/src/did/wasm_document.rs +++ /dev/null @@ -1,703 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::str::FromStr; - -use identity_iota::core::OneOrMany; -use identity_iota::core::OneOrSet; -use identity_iota::core::OrderedSet; -use identity_iota::core::Timestamp; -use identity_iota::core::Url; -use identity_iota::credential::Credential; -use identity_iota::credential::Presentation; -use identity_iota::crypto::PrivateKey; -use identity_iota::crypto::ProofOptions; -use identity_iota::did::verifiable::VerifiableProperties; -use identity_iota::did::Document; -use identity_iota::did::MethodScope; -use identity_iota::iota_core::IotaDID; -use identity_iota::iota_core::IotaDocument; -use identity_iota::iota_core::IotaVerificationMethod; -use identity_iota::iota_core::MessageId; -use identity_iota::iota_core::NetworkName; -use wasm_bindgen::prelude::*; -use wasm_bindgen::JsCast; - -use crate::common::ArrayString; -use crate::common::MapStringAny; -use crate::common::OptionOneOrManyString; -use crate::common::OptionTimestamp; -use crate::common::UOneOrManyNumber; -use crate::common::WasmTimestamp; -use crate::credential::WasmCredential; -use crate::credential::WasmPresentation; -use crate::crypto::WasmKeyPair; -use crate::crypto::WasmProof; -use crate::crypto::WasmProofOptions; -use crate::did::RefMethodScope; -use crate::did::WasmDIDUrl; -use crate::did::WasmDiffMessage; -use crate::did::WasmDocumentMetadata; -use crate::did::WasmIotaDID; -use crate::did::WasmMethodRelationship; -use crate::did::WasmMethodScope; -use crate::did::WasmMethodType; -use crate::did::WasmService; -use crate::did::WasmVerificationMethod; -use crate::did::WasmVerifierOptions; -use crate::error::Result; -use crate::error::WasmResult; - -// ============================================================================= -// ============================================================================= - -#[wasm_bindgen(js_name = Document, inspectable)] -pub struct WasmDocument(pub(crate) IotaDocument); - -#[wasm_bindgen(js_class = Document)] -impl WasmDocument { - /// Creates a new DID Document from the given `KeyPair`, network, and verification method - /// fragment name. - /// - /// The DID Document will be pre-populated with a single verification method - /// derived from the provided `KeyPair` embedded as a capability invocation - /// verification relationship. This method will have the DID URL fragment - /// `#sign-0` by default and can be easily retrieved with `Document::defaultSigningMethod`. - /// - /// NOTE: the generated document is unsigned, see `Document::signSelf`. - /// - /// Arguments: - /// - /// * keypair: the initial verification method is derived from the public key with this keypair. - /// * network: Tangle network to use for the DID, default `Network::mainnet`. - /// * fragment: name of the initial verification method, default "sign-0". - #[wasm_bindgen(constructor)] - pub fn new(keypair: &WasmKeyPair, network: Option, fragment: Option) -> Result { - let network_name = network.map(NetworkName::try_from).transpose().wasm_result()?; - IotaDocument::new_with_options(&keypair.0, network_name, fragment.as_deref()) - .map(Self) - .wasm_result() - } - - /// Creates a new DID Document from the given `VerificationMethod`. - /// - /// NOTE: the generated document is unsigned, see `Document::signSelf`. - #[wasm_bindgen(js_name = fromVerificationMethod)] - pub fn from_verification_method(method: &WasmVerificationMethod) -> Result { - IotaDocument::from_verification_method(method.0.clone()) - .map(Self) - .wasm_result() - } - - /// Returns whether the given {@link MethodType} can be used to sign document updates. - #[wasm_bindgen(js_name = isSigningMethodType)] - pub fn is_signing_method_type(method_type: &WasmMethodType) -> bool { - IotaDocument::is_signing_method_type(method_type.0) - } - - // =========================================================================== - // Properties - // =========================================================================== - - /// Returns a copy of the DID Document `id`. - #[wasm_bindgen] - pub fn id(&self) -> WasmIotaDID { - WasmIotaDID(self.0.id().clone()) - } - - /// Sets the controllers of the DID Document. - /// - /// Note: Duplicates will be ignored. - /// Use `null` to remove all controllers. - #[wasm_bindgen(js_name = setController)] - pub fn set_controller(&mut self, controllers: &OptionOneOrManyIotaDID) -> Result<()> { - let controllers: Option> = controllers.into_serde().wasm_result()?; - let controller_set: Option> = if let Some(controllers) = controllers.map(OneOrMany::into_vec) { - if controllers.is_empty() { - None - } else { - Some(OneOrSet::try_from(OrderedSet::from_iter(controllers)).wasm_result()?) - } - } else { - None - }; - *self.0.controller_mut() = controller_set; - Ok(()) - } - - /// Returns a copy of the list of document controllers. - #[wasm_bindgen] - pub fn controller(&self) -> ArrayIotaDID { - match self.0.controller() { - Some(controllers) => controllers - .iter() - .cloned() - .map(WasmIotaDID::from) - .map(JsValue::from) - .collect::() - .unchecked_into::(), - None => js_sys::Array::new().unchecked_into::(), - } - } - - /// Sets the `alsoKnownAs` property in the DID document. - #[wasm_bindgen(js_name = setAlsoKnownAs)] - pub fn set_also_known_as(&mut self, urls: &OptionOneOrManyString) -> Result<()> { - let urls: Option> = urls.into_serde().wasm_result()?; - let mut urls_set: OrderedSet = OrderedSet::new(); - if let Some(urls) = urls { - for url in urls.into_vec() { - urls_set.append(Url::parse(url).wasm_result()?); - } - } - *self.0.also_known_as_mut() = urls_set; - Ok(()) - } - - /// Returns a copy of the document's `alsoKnownAs` set. - #[wasm_bindgen(js_name = alsoKnownAs)] - pub fn also_known_as(&self) -> ArrayString { - self - .0 - .also_known_as() - .iter() - .map(|url| url.to_string()) - .map(JsValue::from) - .collect::() - .unchecked_into::() - } - - /// Adds a custom property to the DID Document. - /// If the value is set to `null`, the custom property will be removed. - /// - /// ### WARNING - /// This method can overwrite existing properties like `id` and result in an invalid document. - #[wasm_bindgen(js_name = setPropertyUnchecked)] - pub fn set_property_unchecked(&mut self, key: String, value: &JsValue) -> Result<()> { - let value: Option = value.into_serde().wasm_result()?; - match value { - Some(value) => { - self.0.properties_mut().insert(key, value); - } - None => { - self.0.properties_mut().remove(&key); - } - } - Ok(()) - } - - /// Returns a copy of the custom DID Document properties. - #[wasm_bindgen] - pub fn properties(&self) -> Result { - MapStringAny::try_from(self.0.properties()) - } - - // =========================================================================== - // Services - // =========================================================================== - - /// Return a set of all {@link Service Services} in the document. - #[wasm_bindgen] - pub fn service(&self) -> ArrayService { - self - .0 - .service() - .iter() - .cloned() - .map(WasmService) - .map(JsValue::from) - .collect::() - .unchecked_into::() - } - - /// Add a new {@link Service} to the document. - /// - /// Returns `true` if the service was added. - #[wasm_bindgen(js_name = insertService)] - pub fn insert_service(&mut self, service: &WasmService) -> bool { - self.0.insert_service(service.0.clone()) - } - - /// Remove a {@link Service} identified by the given {@link DIDUrl} from the document. - /// - /// Returns `true` if a service was removed. - #[wasm_bindgen(js_name = removeService)] - pub fn remove_service(&mut self, did: &WasmDIDUrl) -> bool { - self.0.remove_service(&did.0) - } - - /// Returns the first {@link Service} with an `id` property matching the provided `query`, - /// if present. - #[wasm_bindgen(js_name = resolveService)] - pub fn resolve_service(&self, query: &UDIDUrlQuery) -> Option { - let service_query: String = query.into_serde().ok()?; - self.0.resolve_service(&service_query).cloned().map(WasmService::from) - } - - // =========================================================================== - // Verification Methods - // =========================================================================== - - /// Returns a list of all {@link VerificationMethod} in the DID Document. - #[wasm_bindgen] - pub fn methods(&self) -> ArrayVerificationMethods { - self - .0 - .methods() - .cloned() - .map(WasmVerificationMethod::from) - .map(JsValue::from) - .collect::() - .unchecked_into::() - } - /// Adds a new `method` to the document in the given `scope`. - #[wasm_bindgen(js_name = insertMethod)] - pub fn insert_method(&mut self, method: &WasmVerificationMethod, scope: &WasmMethodScope) -> Result<()> { - self.0.insert_method(method.0.clone(), scope.0).wasm_result()?; - Ok(()) - } - - /// Removes all references to the specified Verification Method. - #[wasm_bindgen(js_name = removeMethod)] - pub fn remove_method(&mut self, did: &WasmDIDUrl) -> Result<()> { - self.0.remove_method(&did.0).wasm_result() - } - - /// Returns a copy of the first `VerificationMethod` with a capability invocation relationship - /// capable of signing this DID document. - /// - /// Throws an error if no signing method is present. - #[wasm_bindgen(js_name = defaultSigningMethod)] - pub fn default_signing_method(&self) -> Result { - self - .0 - .default_signing_method() - .map(Clone::clone) - .map(WasmVerificationMethod::from) - .wasm_result() - } - - /// Returns a copy of the first verification method with an `id` property - /// matching the provided `query` and the verification relationship - /// specified by `scope`, if present. - #[wasm_bindgen(js_name = resolveMethod)] - pub fn resolve_method( - &self, - query: &UDIDUrlQuery, - scope: Option, - ) -> Result> { - let method_query: String = query.into_serde().wasm_result()?; - let method_scope: Option = scope.map(|js| js.into_serde().wasm_result()).transpose()?; - - let method: Option<&IotaVerificationMethod> = self.0.resolve_method(&method_query, method_scope); - Ok(method.cloned().map(WasmVerificationMethod)) - } - - /// Attempts to resolve the given method query into a method capable of signing a document update. - #[wasm_bindgen(js_name = resolveSigningMethod)] - pub fn resolve_signing_method(&mut self, query: &UDIDUrlQuery) -> Result { - let method_query: String = query.into_serde().wasm_result()?; - Ok(WasmVerificationMethod( - self.0.resolve_signing_method(&method_query).wasm_result()?.clone(), - )) - } - - /// Attaches the relationship to the given method, if the method exists. - /// - /// Note: The method needs to be in the set of verification methods, - /// so it cannot be an embedded one. - #[allow(non_snake_case)] - #[wasm_bindgen(js_name = attachMethodRelationship)] - pub fn attach_method_relationship( - &mut self, - didUrl: &WasmDIDUrl, - relationship: WasmMethodRelationship, - ) -> Result { - self - .0 - .attach_method_relationship(&didUrl.0, relationship.into()) - .wasm_result() - } - - /// Detaches the given relationship from the given method, if the method exists. - #[allow(non_snake_case)] - #[wasm_bindgen(js_name = detachMethodRelationship)] - pub fn detach_method_relationship( - &mut self, - didUrl: &WasmDIDUrl, - relationship: WasmMethodRelationship, - ) -> Result { - self - .0 - .detach_method_relationship(&didUrl.0, relationship.into()) - .wasm_result() - } - - // =========================================================================== - // Signatures - // =========================================================================== - - /// Signs the DID document with the verification method specified by `method_query`. - /// The `method_query` may be the full `DIDUrl` of the method or just its fragment, - /// e.g. "#sign-0". - /// - /// NOTE: does not validate whether the private key of the given `key_pair` corresponds to the - /// verification method. See `Document::verifySelfSigned`. - #[wasm_bindgen(js_name = signSelf)] - pub fn sign_self(&mut self, key_pair: &WasmKeyPair, method_query: &UDIDUrlQuery) -> Result<()> { - let method_query: String = method_query.into_serde().wasm_result()?; - self.0.sign_self(key_pair.0.private(), &method_query).wasm_result() - } - - /// Signs another DID document using the verification method specified by `method_query`. - /// The `method_query` may be the full `DIDUrl` of the method or just its fragment, - /// e.g. "#sign-0". - /// - /// `Document.signSelf` should be used in general, this throws an error if trying to operate - /// on the same document. This is intended for signing updates to a document where a sole - /// capability invocation method is rotated or replaced entirely. - /// - /// NOTE: does not validate whether the private key of the given `key_pair` corresponds to the - /// verification method. See [Document.verifyDocument](#Document+verifyDocument). - #[wasm_bindgen(js_name = signDocument)] - pub fn sign_document( - &self, - document: &mut WasmDocument, - key_pair: &WasmKeyPair, - method_query: &UDIDUrlQuery, - ) -> Result<()> { - let method_query: String = method_query.into_serde().wasm_result()?; - self - .0 - .sign_data( - &mut document.0, - key_pair.0.private(), - &method_query, - ProofOptions::default(), - ) - .wasm_result() - } - - /// Creates a signature for the given `Credential` with the specified DID Document - /// Verification Method. - #[allow(non_snake_case)] - #[wasm_bindgen(js_name = signCredential)] - pub fn sign_credential( - &self, - credential: &WasmCredential, - privateKey: Vec, - methodQuery: &UDIDUrlQuery, - options: &WasmProofOptions, - ) -> Result { - let mut data: Credential = credential.0.clone(); - let private_key: PrivateKey = privateKey.into(); - let method_query: String = methodQuery.into_serde().wasm_result()?; - let options: ProofOptions = options.0.clone(); - - self - .0 - .sign_data(&mut data, &private_key, &method_query, options) - .wasm_result()?; - Ok(WasmCredential::from(data)) - } - - /// Creates a signature for the given `Presentation` with the specified DID Document - /// Verification Method. - #[allow(non_snake_case)] - #[wasm_bindgen(js_name = signPresentation)] - pub fn sign_presentation( - &self, - presentation: &WasmPresentation, - privateKey: Vec, - methodQuery: &UDIDUrlQuery, - options: &WasmProofOptions, - ) -> Result { - let mut data: Presentation = presentation.0.clone(); - let private_key: PrivateKey = privateKey.into(); - let method_query: String = methodQuery.into_serde().wasm_result()?; - let options: ProofOptions = options.0.clone(); - - self - .0 - .sign_data(&mut data, &private_key, &method_query, options) - .wasm_result()?; - Ok(WasmPresentation::from(data)) - } - - /// Creates a signature for the given `data` with the specified DID Document - /// Verification Method. - /// - /// NOTE: use `signSelf` or `signDocument` for DID Documents. - #[allow(non_snake_case)] - #[wasm_bindgen(js_name = signData)] - pub fn sign_data( - &self, - data: &JsValue, - privateKey: Vec, - methodQuery: &UDIDUrlQuery, - options: &WasmProofOptions, - ) -> Result { - let mut data: VerifiableProperties = data.into_serde().wasm_result()?; - let private_key: PrivateKey = privateKey.into(); - let method_query: String = methodQuery.into_serde().wasm_result()?; - let options: ProofOptions = options.0.clone(); - - self - .0 - .sign_data(&mut data, &private_key, &method_query, options) - .wasm_result()?; - - JsValue::from_serde(&data).wasm_result() - } - - // =========================================================================== - // Verification - // =========================================================================== - - /// Verifies the authenticity of `data` using the target verification method. - #[wasm_bindgen(js_name = verifyData)] - pub fn verify_data(&self, data: &JsValue, options: &WasmVerifierOptions) -> Result { - let data: VerifiableProperties = data.into_serde().wasm_result()?; - Ok(self.0.verify_data(&data, &options.0).is_ok()) - } - - /// Verifies that the signature on the DID document `signed` was generated by a valid method from - /// this DID document. - /// - /// # Errors - /// - /// Fails if: - /// - The signature proof section is missing in the `signed` document. - /// - The method is not found in this document. - /// - An unsupported verification method is used. - /// - The signature verification operation fails. - #[wasm_bindgen(js_name = verifyDocument)] - pub fn verify_document(&self, signed: &WasmDocument) -> Result<()> { - self.0.verify_document(&signed.0).wasm_result() - } - - /// Verifies whether `document` is a valid root DID document according to the IOTA DID method - /// specification. - /// - /// It must be signed using a verification method with a public key whose BLAKE2b-256 hash matches - /// the DID tag. - #[wasm_bindgen(js_name = verifyRootDocument)] - pub fn verify_root_document(document: &WasmDocument) -> Result<()> { - IotaDocument::verify_root_document(&document.0).wasm_result() - } - - // =========================================================================== - // Diffs - // =========================================================================== - - /// Generate a `DiffMessage` between two DID Documents and sign it using the specified - /// `key` and `method`. - /// - /// @deprecated since 0.5.0, diff chain features are slated for removal. - #[wasm_bindgen] - pub fn diff( - &self, - other: &WasmDocument, - message_id: &str, - key: &WasmKeyPair, - method_query: &UDIDUrlQuery, - ) -> Result { - let method_query: String = method_query.into_serde().wasm_result()?; - self - .0 - .diff( - &other.0, - MessageId::from_str(message_id) - .map_err(identity_iota::iota_core::Error::InvalidMessage) - .wasm_result()?, - key.0.private(), - &method_query, - ) - .map(WasmDiffMessage::from) - .wasm_result() - } - - /// Verifies the signature of the `diff` was created using a capability invocation method - /// in this DID Document. - /// - /// # Errors - /// - /// Fails if an unsupported verification method is used or the verification operation fails. - /// - /// @deprecated since 0.5.0, diff chain features are slated for removal. - #[wasm_bindgen(js_name = verifyDiff)] - pub fn verify_diff(&self, diff: &WasmDiffMessage) -> Result<()> { - self.0.verify_diff(&diff.0).wasm_result() - } - - /// Verifies a `DiffMessage` signature and attempts to merge the changes into `self`. - /// - /// @deprecated since 0.5.0, diff chain features are slated for removal. - #[wasm_bindgen(js_name = mergeDiff)] - pub fn merge_diff(&mut self, diff: &WasmDiffMessage) -> Result<()> { - self.0.merge_diff(&diff.0).wasm_result() - } - - // =========================================================================== - // Publishing - // =========================================================================== - - /// Returns the Tangle index of the integration chain for this DID. - /// - /// This is simply the tag segment of the `DID`. - /// E.g. - /// For a document with DID: did:iota:1234567890abcdefghijklmnopqrstuvxyzABCDEFGHI, - /// `doc.integration_index()` == "1234567890abcdefghijklmnopqrstuvxyzABCDEFGHI" - #[wasm_bindgen(js_name = integrationIndex)] - pub fn integration_index(&self) -> String { - self.0.integration_index().to_owned() - } - - /// Returns the Tangle index of the DID diff chain. This should only be called on documents - /// published on the integration chain. - /// - /// This is the Base58-btc encoded SHA-256 digest of the hex-encoded message id. - /// - /// @deprecated since 0.5.0, diff chain features are slated for removal. - #[wasm_bindgen(js_name = diffIndex)] - pub fn diff_index(message_id: &str) -> Result { - let message_id = MessageId::from_str(message_id) - .map_err(identity_iota::iota_core::Error::InvalidMessage) - .wasm_result()?; - IotaDocument::diff_index(&message_id).wasm_result() - } - - // =========================================================================== - // Metadata - // =========================================================================== - - /// Returns a copy of the metadata associated with this document. - /// - /// NOTE: Copies all the metadata. See also `metadataCreated`, `metadataUpdated`, - /// `metadataPreviousMessageId`, `metadataProof` if only a subset of the metadata required. - #[wasm_bindgen] - pub fn metadata(&self) -> WasmDocumentMetadata { - WasmDocumentMetadata::from(self.0.metadata.clone()) - } - - /// Returns a copy of the timestamp of when the DID document was created. - #[wasm_bindgen(js_name = metadataCreated)] - pub fn metadata_created(&self) -> Option { - self.0.metadata.created.map(WasmTimestamp::from) - } - - /// Sets the timestamp of when the DID document was created. - #[wasm_bindgen(js_name = setMetadataCreated)] - pub fn set_metadata_created(&mut self, timestamp: OptionTimestamp) -> Result<()> { - let timestamp: Option = timestamp.into_serde().wasm_result()?; - self.0.metadata.created = timestamp; - Ok(()) - } - - /// Returns a copy of the timestamp of the last DID document update. - #[wasm_bindgen(js_name = metadataUpdated)] - pub fn metadata_updated(&self) -> Option { - self.0.metadata.updated.map(WasmTimestamp::from) - } - - /// Sets the timestamp of the last DID document update. - #[wasm_bindgen(js_name = setMetadataUpdated)] - pub fn set_metadata_updated(&mut self, timestamp: OptionTimestamp) -> Result<()> { - let timestamp: Option = timestamp.into_serde().wasm_result()?; - self.0.metadata.updated = timestamp; - Ok(()) - } - - /// Returns a copy of the previous integration chain message id. - #[wasm_bindgen(js_name = metadataPreviousMessageId)] - pub fn metadata_previous_message_id(&self) -> String { - self.0.metadata.previous_message_id.to_string() - } - - /// Sets the previous integration chain message id. - #[wasm_bindgen(js_name = setMetadataPreviousMessageId)] - pub fn set_metadata_previous_message_id(&mut self, value: &str) -> Result<()> { - let message_id: MessageId = MessageId::from_str(value) - .map_err(identity_iota::iota_core::Error::InvalidMessage) - .wasm_result()?; - self.0.metadata.previous_message_id = message_id; - Ok(()) - } - - /// Sets a custom property in the document metadata. - /// If the value is set to `null`, the custom property will be removed. - #[wasm_bindgen(js_name = setMetadataPropertyUnchecked)] - pub fn set_metadata_property_unchecked(&mut self, key: String, value: &JsValue) -> Result<()> { - let value: Option = value.into_serde().wasm_result()?; - match value { - Some(value) => { - self.0.metadata.properties.insert(key, value); - } - None => { - self.0.metadata.properties.remove(&key); - } - } - Ok(()) - } - - /// Returns a copy of the proof. - #[wasm_bindgen] - pub fn proof(&self) -> Option { - self.0.proof.clone().map(WasmProof::from) - } - - /// If the document has a `RevocationBitmap` service identified by `serviceQuery`, - /// revoke all specified `indices`. - #[wasm_bindgen(js_name = revokeCredentials)] - #[allow(non_snake_case)] - pub fn revoke_credentials(&mut self, serviceQuery: &UDIDUrlQuery, indices: UOneOrManyNumber) -> Result<()> { - let query: String = serviceQuery.into_serde().wasm_result()?; - let indices: OneOrMany = indices.into_serde().wasm_result()?; - - self.0.revoke_credentials(&query, indices.as_slice()).wasm_result() - } - - /// If the document has a `RevocationBitmap` service identified by `serviceQuery`, - /// unrevoke all specified `indices`. - #[wasm_bindgen(js_name = unrevokeCredentials)] - #[allow(non_snake_case)] - pub fn unrevoke_credentials(&mut self, serviceQuery: &UDIDUrlQuery, indices: UOneOrManyNumber) -> Result<()> { - let query: String = serviceQuery.into_serde().wasm_result()?; - let indices: OneOrMany = indices.into_serde().wasm_result()?; - - self.0.unrevoke_credentials(&query, indices.as_slice()).wasm_result() - } -} - -impl_wasm_json!(WasmDocument, Document); -impl_wasm_clone!(WasmDocument, Document); - -impl From for WasmDocument { - fn from(document: IotaDocument) -> Self { - Self(document) - } -} - -impl From for IotaDocument { - fn from(wasm_document: WasmDocument) -> Self { - wasm_document.0 - } -} - -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(typescript_type = "DIDUrl | string")] - pub type UDIDUrlQuery; - - #[wasm_bindgen(typescript_type = "IotaDID | IotaDID[] | null")] - pub type OptionOneOrManyIotaDID; - - #[wasm_bindgen(typescript_type = "IotaDID[]")] - pub type ArrayIotaDID; - - #[wasm_bindgen(typescript_type = "Service[]")] - pub type ArrayService; - - #[wasm_bindgen(typescript_type = "VerificationMethod[]")] - pub type ArrayVerificationMethods; -} diff --git a/bindings/wasm/src/did/wasm_document_metadata.rs b/bindings/wasm/src/did/wasm_document_metadata.rs deleted file mode 100644 index bea7838efb..0000000000 --- a/bindings/wasm/src/did/wasm_document_metadata.rs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use identity_iota::iota_core::IotaDocumentMetadata; -use wasm_bindgen::prelude::*; - -use crate::common::MapStringAny; -use crate::common::WasmTimestamp; -use crate::error::Result; - -// ============================================================================= -// ============================================================================= - -/// Additional attributes related to an IOTA DID Document. -#[wasm_bindgen(js_name = DocumentMetadata, inspectable)] -pub struct WasmDocumentMetadata(pub(crate) IotaDocumentMetadata); - -// NOTE: these properties are read-only (no setters) to prevent bugs where a clone of the metadata -// is updated instead of the actual instance in the document. -#[wasm_bindgen(js_class = DocumentMetadata)] -impl WasmDocumentMetadata { - /// Returns a copy of the timestamp of when the DID document was created. - #[wasm_bindgen] - pub fn created(&self) -> Option { - self.0.created.map(WasmTimestamp::from) - } - - /// Returns a copy of the timestamp of the last DID document update. - #[wasm_bindgen] - pub fn updated(&self) -> Option { - self.0.updated.map(WasmTimestamp::from) - } - - /// Returns a copy of the previous message identifier. - #[wasm_bindgen(js_name = previousMessageId)] - pub fn previous_message_id(&self) -> String { - self.0.previous_message_id.to_string() - } - - /// Returns a copy of the custom metadata properties. - #[wasm_bindgen] - pub fn properties(&self) -> Result { - MapStringAny::try_from(&self.0.properties) - } -} - -impl_wasm_json!(WasmDocumentMetadata, DocumentMetadata); -impl_wasm_clone!(WasmDocumentMetadata, DocumentMetadata); - -impl From for WasmDocumentMetadata { - fn from(metadata: IotaDocumentMetadata) -> Self { - Self(metadata) - } -} diff --git a/bindings/wasm/src/did/wasm_resolved_document.rs b/bindings/wasm/src/did/wasm_resolved_document.rs deleted file mode 100644 index e8fefccb5b..0000000000 --- a/bindings/wasm/src/did/wasm_resolved_document.rs +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::str::FromStr; - -use identity_iota::client::ResolvedIotaDocument; -use identity_iota::iota_core::MessageId; -use wasm_bindgen::prelude::*; - -use crate::did::WasmDiffMessage; -use crate::did::WasmDocument; -use crate::error::Result; -use crate::error::WasmResult; - -/// An IOTA DID document resolved from the Tangle. Represents an integration chain message possibly -/// merged with one or more `DiffMessages`. -#[wasm_bindgen(js_name = ResolvedDocument, inspectable)] -pub struct WasmResolvedDocument(pub(crate) ResolvedIotaDocument); - -#[wasm_bindgen] -extern "C" { - // Workaround for Typescript type annotations on async function returns. - #[wasm_bindgen(typescript_type = "Promise")] - pub type PromiseResolvedDocument; - - #[wasm_bindgen(typescript_type = "Promise>")] - pub type PromiseArrayResolvedDocument; - - // Workaround for (current) lack of array support in wasm-bindgen - #[wasm_bindgen(typescript_type = "Array")] - pub type ArrayResolvedDocument; - - // Workaround for (current) lack of generics in wasm-bindgen - #[wasm_bindgen(typescript_type = "Document | ResolvedDocument")] - pub type DocumentOrResolvedDocument; - - #[wasm_bindgen(typescript_type = "Array")] - pub type ArrayDocumentOrResolvedDocument; -} - -#[wasm_bindgen(js_class = ResolvedDocument)] -impl WasmResolvedDocument { - /// Attempts to merge changes from a `DiffMessage` into this document and - /// updates the `ResolvedDocument::diffMessageId`. - /// - /// If merging fails the document remains unmodified, otherwise this represents - /// the merged document state. - /// - /// See `Document::mergeDiff`. - /// - /// # Errors - /// - /// Fails if the merge operation or signature verification on the diff fails. - /// - /// @deprecated since 0.5.0, diff chain features are slated for removal. - #[wasm_bindgen(js_name = "mergeDiffMessage")] - pub fn merge_diff_message(&mut self, diff_message: &WasmDiffMessage) -> Result<()> { - self.0.merge_diff_message(&diff_message.0).wasm_result()?; - Ok(()) - } - - // =========================================================================== - // Properties - // =========================================================================== - - /// Returns a copy of the inner DID document. - /// - /// NOTE: If the `ResolvedDocument` is no longer needed after calling this method - /// then consider using `intoDocument()` for efficiency. - #[wasm_bindgen] - pub fn document(&self) -> WasmDocument { - WasmDocument::from(self.0.document.clone()) - } - - /// Consumes this object and returns the inner DID document. - /// - /// NOTE: trying to use the `ResolvedDocument` after calling this will throw an error. - #[wasm_bindgen(js_name = intoDocument)] - pub fn into_document(self) -> WasmDocument { - WasmDocument::from(self.0.document) - } - - /// Returns a copy of the diff chain message id. - /// - /// @deprecated since 0.5.0, diff chain features are slated for removal. - #[wasm_bindgen(js_name = diffMessageId)] - pub fn diff_message_id(&self) -> String { - self.0.diff_message_id.to_string() - } - - /// Sets the diff chain message id. - /// - /// @deprecated since 0.5.0, diff chain features are slated for removal. - #[wasm_bindgen(js_name = setDiffMessageId)] - pub fn set_diff_message_id(&mut self, value: &str) -> Result<()> { - let message_id: MessageId = MessageId::from_str(value) - .map_err(identity_iota::iota_core::Error::InvalidMessage) - .wasm_result()?; - self.0.diff_message_id = message_id; - Ok(()) - } - - /// Returns a copy of the integration chain message id. - #[wasm_bindgen(js_name = integrationMessageId)] - pub fn integration_message_id(&self) -> String { - self.0.integration_message_id.to_string() - } - - /// Sets the integration chain message id. - #[wasm_bindgen(js_name = setIntegrationMessageId)] - pub fn set_integration_message_id(&mut self, value: &str) -> Result<()> { - let message_id: MessageId = MessageId::from_str(value) - .map_err(identity_iota::iota_core::Error::InvalidMessage) - .wasm_result()?; - self.0.integration_message_id = message_id; - Ok(()) - } -} - -impl_wasm_json!(WasmResolvedDocument, ResolvedDocument); -impl_wasm_clone!(WasmResolvedDocument, ResolvedDocument); - -impl From for WasmResolvedDocument { - fn from(document: ResolvedIotaDocument) -> Self { - Self(document) - } -} - -impl From for ResolvedIotaDocument { - fn from(wasm_document: WasmResolvedDocument) -> Self { - wasm_document.0 - } -} diff --git a/bindings/wasm/src/did/wasm_service.rs b/bindings/wasm/src/did/wasm_service.rs deleted file mode 100644 index 5de7cd44c7..0000000000 --- a/bindings/wasm/src/did/wasm_service.rs +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use identity_iota::core::OneOrMany; -use identity_iota::did::ServiceEndpoint; -use identity_iota::iota_core::IotaDIDUrl; -use identity_iota::iota_core::IotaService; -use wasm_bindgen::prelude::*; -use wasm_bindgen::JsCast; - -use crate::common::deserialize_map_or_any; -use crate::common::ArrayString; -use crate::common::MapStringAny; -use crate::did::WasmDIDUrl; -use crate::error::Result; -use crate::error::WasmResult; - -/// A DID Document Service used to enable trusted interactions associated -/// with a DID subject. -/// -/// See: https://www.w3.org/TR/did-core/#services -#[wasm_bindgen(js_name = Service, inspectable)] -pub struct WasmService(pub(crate) IotaService); - -#[wasm_bindgen(js_class = Service)] -impl WasmService { - #[wasm_bindgen(constructor)] - pub fn new(service: IIotaService) -> Result { - let id: IotaDIDUrl = service.id().into_serde().wasm_result()?; - - let base_service: &IService = service.as_ref(); - let types: OneOrMany = service.type_().into_serde().wasm_result()?; - let service_endpoint: ServiceEndpoint = deserialize_map_or_any(&base_service.service_endpoint())?; - let properties: Option = deserialize_map_or_any(&base_service.properties())?; - - IotaService::builder(properties.unwrap_or_default()) - .id(id) - .types(types) - .service_endpoint(service_endpoint) - .build() - .map(WasmService::from) - .wasm_result() - } - - /// Returns a copy of the `Service` id. - #[wasm_bindgen] - pub fn id(&self) -> WasmDIDUrl { - WasmDIDUrl::from(self.0.id().clone()) - } - - /// Returns a copy of the `Service` type. - #[wasm_bindgen(js_name = type)] - pub fn type_(&self) -> ArrayString { - self - .0 - .type_() - .iter() - .cloned() - .map(JsValue::from) - .collect::() - .unchecked_into::() - } - - /// Returns a copy of the `Service` endpoint. - #[wasm_bindgen(js_name = serviceEndpoint)] - pub fn service_endpoint(&self) -> UServiceEndpoint { - UServiceEndpoint::from(self.0.service_endpoint()) - } - - /// Returns a copy of the custom properties on the `Service`. - #[wasm_bindgen] - pub fn properties(&self) -> Result { - MapStringAny::try_from(self.0.properties()) - } -} - -impl_wasm_json!(WasmService, Service); -impl_wasm_clone!(WasmService, Service); - -impl From for WasmService { - fn from(service: IotaService) -> Self { - Self(service) - } -} - -impl From for UServiceEndpoint { - fn from(endpoint: ServiceEndpoint) -> Self { - UServiceEndpoint::from(&endpoint) - } -} - -impl From<&ServiceEndpoint> for UServiceEndpoint { - fn from(endpoint: &ServiceEndpoint) -> Self { - match endpoint { - // string - ServiceEndpoint::One(url) => JsValue::from_str(url.as_str()).unchecked_into::(), - // string[] - ServiceEndpoint::Set(set) => set - .iter() - .map(|url| JsValue::from_str(url.as_str())) - .collect::() - .unchecked_into::(), - // Map - ServiceEndpoint::Map(map) => { - let js_map: js_sys::Map = js_sys::Map::new(); - for (key, urls) in map.into_iter() { - js_map.set( - &JsValue::from_str(key.as_str()), - &urls - .iter() - .map(|url| JsValue::from_str(url.as_str())) - .collect::(), - ); - } - js_map.unchecked_into::() - } - } - } -} - -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(typescript_type = "string | string[] | Map")] - pub type UServiceEndpoint; -} - -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(typescript_type = "IIotaService", extends = IService)] - pub type IIotaService; - - #[wasm_bindgen(method, getter)] - pub fn id(this: &IIotaService) -> JsValue; -} - -#[wasm_bindgen(typescript_custom_section)] -const I_IOTA_SERVICE: &'static str = r#" -/** - * Holds options to create a new `IotaService`. - */ -interface IIotaService extends IService { - /** - * Identifier of the service. - * - * Must be a valid DIDUrl with a fragment. - */ - readonly id: DIDUrl | string; -}"#; - -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(typescript_type = "IService")] - pub type IService; - - #[wasm_bindgen(method, getter, js_name = type)] - pub fn type_(this: &IService) -> JsValue; - - #[wasm_bindgen(method, getter, js_name = serviceEndpoint)] - pub fn service_endpoint(this: &IService) -> JsValue; - - #[wasm_bindgen(method, getter)] - pub fn properties(this: &IService) -> JsValue; -} - -#[wasm_bindgen(typescript_custom_section)] -const I_SERVICE: &'static str = r#" -/** - * Base `Service` properties. - */ -interface IService { - /** - * Type of service. - * - * E.g. "LinkedDomains" or "DIDCommMessaging". - */ - readonly type: string | string[]; - - /** - * A URL, set of URLs, or map of URL sets. - * - * NOTE: throws an error if any entry is not a valid URL string. List entries must be unique. - */ - readonly serviceEndpoint: string | string[] | Map | Record; - - /** - * Additional custom properties to embed in the service. - * - * WARNING: entries may overwrite existing fields and result in invalid documents. - */ - readonly properties?: Map | Record; -}"#; diff --git a/bindings/wasm/src/did/wasm_verification_method.rs b/bindings/wasm/src/did/wasm_verification_method.rs deleted file mode 100644 index 7079c631ba..0000000000 --- a/bindings/wasm/src/did/wasm_verification_method.rs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use identity_iota::crypto::PublicKey; - -use identity_iota::iota_core::IotaVerificationMethod; -use wasm_bindgen::prelude::*; - -use crate::crypto::WasmKeyType; -use crate::did::WasmDIDUrl; -use crate::did::WasmIotaDID; -use crate::did::WasmMethodData; -use crate::did::WasmMethodType; -use crate::error::Result; -use crate::error::WasmResult; - -#[wasm_bindgen(js_name = VerificationMethod, inspectable)] -pub struct WasmVerificationMethod(pub(crate) IotaVerificationMethod); - -#[wasm_bindgen(js_class = VerificationMethod)] -impl WasmVerificationMethod { - /// Creates a new `VerificationMethod` from the given `did` and public key. - #[allow(non_snake_case)] - #[wasm_bindgen(constructor)] - pub fn new( - did: &WasmIotaDID, - keyType: WasmKeyType, - publicKey: Vec, - fragment: String, - ) -> Result { - let public_key: PublicKey = PublicKey::from(publicKey); - IotaVerificationMethod::new(did.0.clone(), keyType.into(), &public_key, &fragment) - .map(Self) - .wasm_result() - } - - /// Returns a copy of the `id` `DIDUrl` of the `VerificationMethod`. - #[wasm_bindgen] - pub fn id(&self) -> WasmDIDUrl { - WasmDIDUrl::from(self.0.id().clone()) - } - - /// Returns a copy of the `controller` `DID` of the `VerificationMethod`. - #[wasm_bindgen] - pub fn controller(&self) -> WasmIotaDID { - WasmIotaDID::from(self.0.controller().clone()) - } - - /// Sets the `controller` `DID` of the `VerificationMethod` object. - #[wasm_bindgen(js_name = setController)] - pub fn set_controller(&mut self, did: &WasmIotaDID) { - *self.0.controller_mut() = did.0.clone(); - } - - /// Returns a copy of the `VerificationMethod` type. - #[wasm_bindgen(js_name = type)] - pub fn type_(&self) -> WasmMethodType { - WasmMethodType::from(self.0.type_()) - } - - /// Returns a copy of the `VerificationMethod` public key data. - #[wasm_bindgen] - pub fn data(&self) -> WasmMethodData { - WasmMethodData::from(self.0.data().clone()) - } -} - -impl_wasm_json!(WasmVerificationMethod, VerificationMethod); -impl_wasm_clone!(WasmVerificationMethod, VerificationMethod); - -impl From for WasmVerificationMethod { - fn from(method: IotaVerificationMethod) -> Self { - Self(method) - } -} diff --git a/bindings/wasm/src/error.rs b/bindings/wasm/src/error.rs index 8fc5ed17d8..2c1d760419 100644 --- a/bindings/wasm/src/error.rs +++ b/bindings/wasm/src/error.rs @@ -5,9 +5,6 @@ use std::borrow::Cow; use std::fmt::Display; use std::result::Result as StdResult; -use identity_iota::account::UpdateError; -use identity_iota::account_storage::Error as AccountStorageError; -use identity_iota::account_storage::Result as AccountStorageResult; use wasm_bindgen::JsValue; /// Convenience wrapper for `Result`. @@ -90,15 +87,12 @@ macro_rules! impl_wasm_error_from { } impl_wasm_error_from!( - identity_iota::account::Error, - identity_iota::account_storage::Error, identity_iota::core::Error, identity_iota::credential::Error, identity_iota::did::Error, identity_iota::did::DIDError, - identity_iota::iota_core::Error, - identity_iota::credential::ValidationError, - identity_stardust::Error + identity_iota::iota::Error, + identity_iota::credential::ValidationError ); // Similar to `impl_wasm_error_from`, but uses the types name instead of requiring/calling Into &'static str @@ -139,27 +133,6 @@ fn error_chain_fmt(e: &impl std::error::Error, f: &mut std::fmt::Formatter<'_>) struct ErrorMessage<'a, E: std::error::Error>(&'a E); -impl<'a> Display for ErrorMessage<'a, identity_iota::client::Error> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match &self.0 { - identity_iota::client::Error::CredentialValidationError(e) => { - write!(f, "{}. ", self.0)?; - error_chain_fmt(&e, f) - } - identity_iota::client::Error::PresentationValidationError(e) => { - write!(f, "{}. ", self.0)?; - error_chain_fmt(&e, f) - } - identity_iota::client::Error::IsolatedValidationError(e) => { - write!(f, "{}. ", self.0)?; - error_chain_fmt(&e, f) - } - // the rest include the source error's message in their own - _ => self.0.fmt(f), - } - } -} - impl<'a> Display for ErrorMessage<'a, identity_resolver::Error> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { error_chain_fmt(self.0, f) @@ -175,15 +148,6 @@ impl From for WasmError<'_> { } } -impl From for WasmError<'_> { - fn from(error: identity_iota::client::Error) -> Self { - Self { - message: Cow::Owned(ErrorMessage(&error).to_string()), - name: Cow::Borrowed(error.into()), - } - } -} - impl From for WasmError<'_> { fn from(error: serde_json::Error) -> Self { Self { @@ -193,8 +157,8 @@ impl From for WasmError<'_> { } } -impl From for WasmError<'_> { - fn from(error: identity_stardust::block::Error) -> Self { +impl From for WasmError<'_> { + fn from(error: identity_iota::iota::block::Error) -> Self { Self { name: Cow::Borrowed("bee_block::Error"), message: Cow::Owned(error.to_string()), @@ -220,23 +184,25 @@ impl From for Wa } } -impl From for WasmError<'_> { - fn from(error: UpdateError) -> Self { - Self { - name: Cow::Borrowed("Update::Error"), - message: Cow::Owned(error.to_string()), - } - } -} +// TODO: Remove or reuse depending on what we do with the account. +// impl From for WasmError<'_> { +// fn from(error: UpdateError) -> Self { +// Self { +// name: Cow::Borrowed("Update::Error"), +// message: Cow::Owned(error.to_string()), +// } +// } +// } /// Convenience struct to convert Result to errors in the Rust library. pub struct JsValueResult(pub(crate) Result); impl JsValueResult { + // TODO: Remove or reuse depending on what we do with the account. /// Consumes the struct and returns a Result<_, AccountStorageError>, leaving an `Ok` value untouched. - pub fn to_account_error(self) -> StdResult { - self.stringify_error().map_err(AccountStorageError::JsError) - } + // pub fn to_account_error(self) -> StdResult { + // self.stringify_error().map_err(AccountStorageError::JsError) + // } // Consumes the struct and returns a Result<_, String>, leaving an `Ok` value untouched. pub(crate) fn stringify_error(self) -> StdResult { @@ -252,9 +218,9 @@ impl JsValueResult { }) } - /// Consumes the struct and returns a Result<_, identity_stardust::Error>, leaving an `Ok` value untouched. - pub fn to_stardust_error(self) -> StdResult { - self.stringify_error().map_err(identity_stardust::Error::JsError) + /// Consumes the struct and returns a Result<_, identity_iota::iota::Error>, leaving an `Ok` value untouched. + pub fn to_iota_core_error(self) -> StdResult { + self.stringify_error().map_err(identity_iota::iota::Error::JsError) } } @@ -264,22 +230,24 @@ impl From> for JsValueResult { } } -impl From for AccountStorageResult<()> { - fn from(result: JsValueResult) -> Self { - result.to_account_error().and_then(|js_value| { - js_value - .into_serde() - .map_err(|e| AccountStorageError::SerializationError(e.to_string())) - }) - } -} - -impl From for AccountStorageResult { - fn from(result: JsValueResult) -> Self { - result.to_account_error().and_then(|js_value| { - js_value - .into_serde() - .map_err(|e| AccountStorageError::SerializationError(e.to_string())) - }) - } -} +// TODO: Remove or reuse depending on what we do with the account.. +// impl From for AccountStorageResult<()> { +// fn from(result: JsValueResult) -> Self { +// result.to_account_error().and_then(|js_value| { +// js_value +// .into_serde() +// .map_err(|e| AccountStorageError::SerializationError(e.to_string())) +// }) +// } +// } + +// TODO: Remove or reuse depending on what we do with the account.. +// impl From for AccountStorageResult { +// fn from(result: JsValueResult) -> Self { +// result.to_account_error().and_then(|js_value| { +// js_value +// .into_serde() +// .map_err(|e| AccountStorageError::SerializationError(e.to_string())) +// }) +// } +// } diff --git a/bindings/wasm/src/iota/identity_client.rs b/bindings/wasm/src/iota/identity_client.rs new file mode 100644 index 0000000000..393c627acd --- /dev/null +++ b/bindings/wasm/src/iota/identity_client.rs @@ -0,0 +1,113 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use core::fmt::Debug; +use core::fmt::Formatter; + +use identity_iota::iota::block::output::dto::AliasOutputDto; +use identity_iota::iota::block::output::AliasId; +use identity_iota::iota::block::output::AliasOutput; +use identity_iota::iota::block::output::OutputId; +use identity_iota::iota::block::output::RentStructure; +use identity_iota::iota::block::output::RentStructureBuilder; +use identity_iota::iota::IotaIdentityClient; +use js_sys::Promise; +use wasm_bindgen::prelude::*; +use wasm_bindgen_futures::JsFuture; + +use crate::error::JsValueResult; + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(typescript_type = "IIotaIdentityClient")] + pub type WasmIotaIdentityClient; + + #[wasm_bindgen(method, js_name = getNetworkHrp)] + pub fn get_network_hrp(this: &WasmIotaIdentityClient) -> JsValue; + + #[allow(non_snake_case)] + #[wasm_bindgen(method, js_name = getAliasOutput)] + pub fn get_alias_output(this: &WasmIotaIdentityClient, aliasId: String) -> JsValue; + + #[wasm_bindgen(method, js_name = getRentStructure)] + pub fn get_rent_structure(this: &WasmIotaIdentityClient) -> JsValue; +} + +impl Debug for WasmIotaIdentityClient { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + f.write_str("WasmIotaIdentityClient") + } +} + +#[async_trait::async_trait(?Send)] +impl IotaIdentityClient for WasmIotaIdentityClient { + async fn get_network_hrp(&self) -> Result { + let promise: Promise = Promise::resolve(&WasmIotaIdentityClient::get_network_hrp(self)); + let result: JsValueResult = JsFuture::from(promise).await.into(); + let js: JsValue = result.to_iota_core_error()?; + let network_hrp = match js.as_string() { + Some(hrp) => hrp, + None => js.into_serde().map_err(|err| { + identity_iota::iota::Error::JsError(format!("get_network_hrp failed to deserialize String: {}", err)) + })?, + }; + Ok(network_hrp) + } + + async fn get_alias_output(&self, id: AliasId) -> Result<(OutputId, AliasOutput), identity_iota::iota::Error> { + let promise: Promise = Promise::resolve(&WasmIotaIdentityClient::get_alias_output(self, id.to_string())); + let result: JsValueResult = JsFuture::from(promise).await.into(); + let tuple: js_sys::Array = js_sys::Array::from(&result.to_iota_core_error()?); + let mut iter: js_sys::ArrayIter = tuple.iter(); + + let output_id: OutputId = iter + .next() + .ok_or_else(|| identity_iota::iota::Error::JsError("get_alias_output expected a tuple of size 2".to_owned()))? + .into_serde() + .map_err(|err| { + identity_iota::iota::Error::JsError(format!("get_alias_output failed to deserialize OutputId: {}", err)) + })?; + let alias_dto: AliasOutputDto = iter + .next() + .ok_or_else(|| identity_iota::iota::Error::JsError("get_alias_output expected a tuple of size 2".to_owned()))? + .into_serde() + .map_err(|err| { + identity_iota::iota::Error::JsError(format!( + "get_alias_output failed to deserialize AliasOutputDto: {}", + err + )) + })?; + let alias_output = AliasOutput::try_from(&alias_dto).map_err(|err| { + identity_iota::iota::Error::JsError(format!("get_alias_output failed to convert AliasOutputDto: {}", err)) + })?; + Ok((output_id, alias_output)) + } + + async fn get_rent_structure(&self) -> Result { + let promise: Promise = Promise::resolve(&WasmIotaIdentityClient::get_rent_structure(self)); + let result: JsValueResult = JsFuture::from(promise).await.into(); + let rent_structure: RentStructureBuilder = result.to_iota_core_error()?.into_serde().map_err(|err| { + identity_iota::iota::Error::JsError(format!("get_rent_structure failed to deserialize: {}", err)) + })?; + Ok(rent_structure.finish()) + } +} + +#[wasm_bindgen(typescript_custom_section)] +const I_IOTA_IDENTITY_CLIENT: &'static str = r#" +import type { IAliasOutput, IRent } from '@iota/types'; +/** Helper interface necessary for `IotaIdentityClientExt`. */ +interface IIotaIdentityClient { + /** + * Return the Bech32 human-readable part (HRP) of the network. + * + * E.g. "iota", "atoi", "smr", "rms". + */ + getNetworkHrp(): Promise; + + /** Resolve an Alias identifier, returning its latest `OutputId` and `AliasOutput`. */ + getAliasOutput(aliasId: string): Promise<[string, IAliasOutput]>; + + /** Return the rent structure of the network, indicating the byte costs for outputs. */ + getRentStructure(): Promise; +}"#; diff --git a/bindings/wasm/src/stardust/identity_client_ext.rs b/bindings/wasm/src/iota/identity_client_ext.rs similarity index 66% rename from bindings/wasm/src/stardust/identity_client_ext.rs rename to bindings/wasm/src/iota/identity_client_ext.rs index e6549c8175..7ee818955f 100644 --- a/bindings/wasm/src/stardust/identity_client_ext.rs +++ b/bindings/wasm/src/iota/identity_client_ext.rs @@ -1,14 +1,14 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use identity_stardust::block::address::dto::AddressDto; -use identity_stardust::block::address::Address; -use identity_stardust::block::output::dto::AliasOutputDto; -use identity_stardust::block::output::AliasOutput; -use identity_stardust::block::output::RentStructure; -use identity_stardust::StardustDID; -use identity_stardust::StardustDocument; -use identity_stardust::StardustIdentityClientExt; +use identity_iota::iota::block::address::dto::AddressDto; +use identity_iota::iota::block::address::Address; +use identity_iota::iota::block::output::dto::AliasOutputDto; +use identity_iota::iota::block::output::AliasOutput; +use identity_iota::iota::block::output::RentStructure; +use identity_iota::iota::IotaDID; +use identity_iota::iota::IotaDocument; +use identity_iota::iota::IotaIdentityClientExt; use js_sys::Promise; use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; @@ -16,9 +16,9 @@ use wasm_bindgen_futures::future_to_promise; use crate::error::Result; use crate::error::WasmResult; -use crate::stardust::identity_client::WasmStardustIdentityClient; -use crate::stardust::WasmStardustDID; -use crate::stardust::WasmStardustDocument; +use crate::iota::identity_client::WasmIotaIdentityClient; +use crate::iota::WasmIotaDID; +use crate::iota::WasmIotaDocument; // `IAliasOutput`, `AddressTypes`, and `IRent` are external interfaces. // See the custom TypeScript section in `identity_client.rs` for the first import statement. @@ -29,8 +29,8 @@ extern "C" { #[wasm_bindgen(typescript_type = "Promise")] pub type PromiseAliasOutput; - #[wasm_bindgen(typescript_type = "Promise")] - pub type PromiseStardustDocument; + #[wasm_bindgen(typescript_type = "Promise")] + pub type PromiseIotaDocument; #[wasm_bindgen(typescript_type = "AddressTypes")] pub type AddressTypes; @@ -41,11 +41,11 @@ extern "C" { /// An extension interface that provides helper functions for publication /// and resolution of DID documents in Alias Outputs. -#[wasm_bindgen(js_name = StardustIdentityClientExt)] -pub struct WasmStardustIdentityClientExt; +#[wasm_bindgen(js_name = IotaIdentityClientExt)] +pub struct WasmIotaIdentityClientExt; -#[wasm_bindgen(js_class = StardustIdentityClientExt)] -impl WasmStardustIdentityClientExt { +#[wasm_bindgen(js_class = IotaIdentityClientExt)] +impl WasmIotaIdentityClientExt { /// Create a DID with a new Alias Output containing the given `document`. /// /// The `address` will be set as the state controller and governor unlock conditions. @@ -57,24 +57,24 @@ impl WasmStardustIdentityClientExt { #[allow(non_snake_case)] #[wasm_bindgen(js_name = newDidOutput)] pub fn new_did_output( - client: WasmStardustIdentityClient, + client: WasmIotaIdentityClient, address: AddressTypes, - document: &WasmStardustDocument, + document: &WasmIotaDocument, rentStructure: Option, ) -> Result { let address_dto: AddressDto = address.into_serde().wasm_result()?; let address: Address = Address::try_from(&address_dto) .map_err(|err| { - identity_stardust::Error::JsError(format!("newDidOutput failed to decode Address: {err}: {address_dto:?}")) + identity_iota::iota::Error::JsError(format!("newDidOutput failed to decode Address: {err}: {address_dto:?}")) }) .wasm_result()?; - let doc: StardustDocument = document.0.clone(); + let doc: IotaDocument = document.0.clone(); let promise: Promise = future_to_promise(async move { let rent_structure: Option = rentStructure.map(|rent| rent.into_serde()).transpose().wasm_result()?; - let output: AliasOutput = StardustIdentityClientExt::new_did_output(&client, address, doc, rent_structure) + let output: AliasOutput = IotaIdentityClientExt::new_did_output(&client, address, doc, rent_structure) .await .wasm_result()?; // Use DTO for correct serialization. @@ -92,13 +92,10 @@ impl WasmStardustIdentityClientExt { /// /// NOTE: this does *not* publish the updated Alias Output. #[wasm_bindgen(js_name = updateDidOutput)] - pub fn update_did_output( - client: WasmStardustIdentityClient, - document: &WasmStardustDocument, - ) -> Result { - let document: StardustDocument = document.0.clone(); + pub fn update_did_output(client: WasmIotaIdentityClient, document: &WasmIotaDocument) -> Result { + let document: IotaDocument = document.0.clone(); let promise: Promise = future_to_promise(async move { - let output: AliasOutput = StardustIdentityClientExt::update_did_output(&client, document) + let output: AliasOutput = IotaIdentityClientExt::update_did_output(&client, document) .await .wasm_result()?; // Use DTO for correct serialization. @@ -119,13 +116,10 @@ impl WasmStardustIdentityClientExt { /// /// NOTE: this does *not* publish the updated Alias Output. #[wasm_bindgen(js_name = deactivateDidOutput)] - pub fn deactivate_did_output( - client: WasmStardustIdentityClient, - did: &WasmStardustDID, - ) -> Result { - let did: StardustDID = did.0.clone(); + pub fn deactivate_did_output(client: WasmIotaIdentityClient, did: &WasmIotaDID) -> Result { + let did: IotaDID = did.0.clone(); let promise: Promise = future_to_promise(async move { - let output: AliasOutput = StardustIdentityClientExt::deactivate_did_output(&client, &did) + let output: AliasOutput = IotaIdentityClientExt::deactivate_did_output(&client, &did) .await .wasm_result()?; // Use DTO for correct serialization. @@ -137,29 +131,29 @@ impl WasmStardustIdentityClientExt { Ok(promise.unchecked_into::()) } - /// Resolve a {@link StardustDocument}. Returns an empty, deactivated document if the state metadata + /// Resolve a {@link IotaDocument}. Returns an empty, deactivated document if the state metadata /// of the Alias Output is empty. #[wasm_bindgen(js_name = resolveDid)] - pub fn resolve_did(client: WasmStardustIdentityClient, did: &WasmStardustDID) -> Result { - let did: StardustDID = did.0.clone(); + pub fn resolve_did(client: WasmIotaIdentityClient, did: &WasmIotaDID) -> Result { + let did: IotaDID = did.0.clone(); let promise: Promise = future_to_promise(async move { - StardustIdentityClientExt::resolve_did(&client, &did) + IotaIdentityClientExt::resolve_did(&client, &did) .await - .map(WasmStardustDocument) + .map(WasmIotaDocument) .map(Into::into) .wasm_result() }); // WARNING: this does not validate the return type. Check carefully. - Ok(promise.unchecked_into::()) + Ok(promise.unchecked_into::()) } /// Fetches the `IAliasOutput` associated with the given DID. #[wasm_bindgen(js_name = resolveDidOutput)] - pub fn resolve_did_output(client: WasmStardustIdentityClient, did: &WasmStardustDID) -> Result { - let did: StardustDID = did.0.clone(); + pub fn resolve_did_output(client: WasmIotaIdentityClient, did: &WasmIotaDID) -> Result { + let did: IotaDID = did.0.clone(); let promise: Promise = future_to_promise(async move { - let output: AliasOutput = StardustIdentityClientExt::resolve_did_output(&client, &did) + let output: AliasOutput = IotaIdentityClientExt::resolve_did_output(&client, &did) .await .wasm_result()?; // Use DTO for correct serialization. diff --git a/bindings/wasm/src/did/wasm_iota_did.rs b/bindings/wasm/src/iota/iota_did.rs similarity index 64% rename from bindings/wasm/src/did/wasm_iota_did.rs rename to bindings/wasm/src/iota/iota_did.rs index 15a08986c4..7b916aa0ab 100644 --- a/bindings/wasm/src/did/wasm_iota_did.rs +++ b/bindings/wasm/src/iota/iota_did.rs @@ -1,16 +1,19 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use identity_iota::did::DIDError; use identity_iota::did::DID; -use identity_iota::iota_core::IotaDID; +use identity_iota::iota::IotaDID; +use identity_iota::iota::NetworkName; use wasm_bindgen::prelude::*; -use crate::did::wasm_did_url::WasmDIDUrl; use crate::error::Result; use crate::error::WasmResult; -use crate::tangle::WasmNetwork; +use crate::iota::WasmIotaDIDUrl; /// A DID conforming to the IOTA DID method specification. +/// +/// @typicalname did #[wasm_bindgen(js_name = IotaDID, inspectable)] pub struct WasmIotaDID(pub(crate) IotaDID); @@ -22,7 +25,7 @@ impl WasmIotaDID { IotaDID::METHOD.to_owned() } - /// The default Tangle network (`"main"`). + /// The default Tangle network (`"iota"`). #[wasm_bindgen(getter = DEFAULT_NETWORK)] pub fn static_default_network() -> String { IotaDID::DEFAULT_NETWORK.to_owned() @@ -32,38 +35,39 @@ impl WasmIotaDID { // Constructors // =========================================================================== - /// Creates a new `DID` from a public key. + /// Constructs a new `IotaDID` from a byte representation of the tag and the given + /// network name. + /// + /// See also {@link IotaDID.placeholder}. #[wasm_bindgen(constructor)] - pub fn new(public_key: &[u8], network: Option) -> Result { - Self::from_public_key(public_key, network) + pub fn new(bytes: &[u8], network: String) -> Result { + let network_name: NetworkName = NetworkName::try_from(network).wasm_result()?; + let tag_bytes: &[u8; 32] = bytes + .try_into() + .map_err(|_| DIDError::Other("invalid bytes length for IotaDID tag, expected 32")) + .wasm_result()?; + Ok(Self::from(IotaDID::new(tag_bytes, &network_name))) } - /// Creates a new `IotaDID` from an arbitrary public key. - fn from_public_key(public_key: &[u8], network: Option) -> Result { - let did = if let Some(network) = network { - IotaDID::new_with_network(public_key, network) - } else { - IotaDID::new(public_key) - }; - did.wasm_result().map(Self) + /// Creates a new placeholder [`IotaDID`] with the given network name. + /// + /// E.g. `did:iota:smr:0x0000000000000000000000000000000000000000000000000000000000000000`. + #[wasm_bindgen] + pub fn placeholder(network: String) -> Result { + let network_name: NetworkName = NetworkName::try_from(network).wasm_result()?; + Ok(Self::from(IotaDID::placeholder(&network_name))) } /// Parses a `IotaDID` from the input string. #[wasm_bindgen] pub fn parse(input: &str) -> Result { - IotaDID::parse(input).wasm_result().map(Self) + IotaDID::parse(input).map(Self).wasm_result() } // =========================================================================== // Properties // =========================================================================== - /// Returns the Tangle network of the `IotaDID`. - #[wasm_bindgen] - pub fn network(&self) -> Result { - self.0.network().map(Into::into).wasm_result() - } - /// Returns the Tangle network name of the `IotaDID`. #[wasm_bindgen(js_name = networkStr)] pub fn network_str(&self) -> String { @@ -122,23 +126,23 @@ impl WasmIotaDID { /// Construct a new `DIDUrl` by joining with a relative DID Url string. #[wasm_bindgen] - pub fn join(&self, segment: &str) -> Result { - self.0.clone().join(segment).wasm_result().map(WasmDIDUrl) + pub fn join(&self, segment: &str) -> Result { + self.0.clone().join(segment).wasm_result().map(WasmIotaDIDUrl) } - /// Clones the `IotaDID` into a `DIDUrl`. + /// Clones the `DID` into a `DIDUrl`. #[wasm_bindgen(js_name = toUrl)] - pub fn to_url(&self) -> WasmDIDUrl { - WasmDIDUrl::from(self.0.to_url()) + pub fn to_url(&self) -> WasmIotaDIDUrl { + WasmIotaDIDUrl::from(self.0.to_url()) } - /// Converts the `IotaDID` into a `DIDUrl`, consuming it. + /// Converts the `DID` into a `DIDUrl`, consuming it. #[wasm_bindgen(js_name = intoUrl)] - pub fn into_url(self) -> WasmDIDUrl { - WasmDIDUrl::from(self.0.into_url()) + pub fn into_url(self) -> WasmIotaDIDUrl { + WasmIotaDIDUrl::from(self.0.into_url()) } - /// Returns the `IotaDID` as a string. + /// Returns the `DID` as a string. #[allow(clippy::inherent_to_string)] #[wasm_bindgen(js_name = toString)] pub fn to_string(&self) -> String { @@ -154,20 +158,3 @@ impl From for WasmIotaDID { Self(did) } } - -/// Duck-typed union to pass either a string or WasmDID as a parameter. -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(typescript_type = "IotaDID | string")] - pub type UWasmIotaDID; -} - -impl TryFrom for IotaDID { - type Error = JsValue; - - fn try_from(did: UWasmIotaDID) -> std::result::Result { - // Parse rather than going through serde directly to return proper error types. - let json: String = did.into_serde().wasm_result()?; - IotaDID::parse(&json).wasm_result() - } -} diff --git a/bindings/wasm/src/did/wasm_did_url.rs b/bindings/wasm/src/iota/iota_did_url.rs similarity index 61% rename from bindings/wasm/src/did/wasm_did_url.rs rename to bindings/wasm/src/iota/iota_did_url.rs index f9a03a7daf..02a9ffb7fa 100644 --- a/bindings/wasm/src/did/wasm_did_url.rs +++ b/bindings/wasm/src/iota/iota_did_url.rs @@ -1,28 +1,26 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use identity_iota::iota_core::IotaDIDUrl; +use identity_iota::iota::IotaDIDUrl; use wasm_bindgen::prelude::*; -use crate::did::WasmIotaDID; use crate::error::Result; use crate::error::WasmResult; +use crate::iota::WasmIotaDID; /// A DID URL conforming to the IOTA DID method specification. -/// -/// @typicalname didUrl -#[wasm_bindgen(js_name = DIDUrl, inspectable)] -pub struct WasmDIDUrl(pub(crate) IotaDIDUrl); - -#[wasm_bindgen(js_class = DIDUrl)] -impl WasmDIDUrl { - /// Parses a `DIDUrl` from the input string. +#[wasm_bindgen(js_name = IotaDIDUrl, inspectable)] +pub struct WasmIotaDIDUrl(pub(crate) IotaDIDUrl); + +#[wasm_bindgen(js_class = IotaDIDUrl)] +impl WasmIotaDIDUrl { + /// Parses a `IotaDIDUrl` from the input string. #[wasm_bindgen] - pub fn parse(input: &str) -> Result { - IotaDIDUrl::parse(input).map(WasmDIDUrl).wasm_result() + pub fn parse(input: &str) -> Result { + IotaDIDUrl::parse(input).map(WasmIotaDIDUrl).wasm_result() } - /// Return a copy of the `DID` section of the `DIDUrl`. + /// Return a copy of the `IotaDID` section of the `IotaDIDUrl`. #[wasm_bindgen] pub fn did(&self) -> WasmIotaDID { WasmIotaDID::from(self.0.did().clone()) @@ -34,43 +32,43 @@ impl WasmDIDUrl { self.0.url().to_string() } - /// Returns a copy of the `DIDUrl` method fragment, if any. Excludes the leading '#'. + /// Returns a copy of the `IotaDIDUrl` method fragment, if any. Excludes the leading '#'. #[wasm_bindgen] pub fn fragment(&self) -> Option { self.0.fragment().map(str::to_owned) } - /// Sets the `fragment` component of the `DIDUrl`. + /// Sets the `fragment` component of the `IotaDIDUrl`. #[wasm_bindgen(js_name = setFragment)] pub fn set_fragment(&mut self, value: Option) -> Result<()> { self.0.set_fragment(value.as_deref()).wasm_result() } - /// Returns a copy of the `DIDUrl` path. + /// Returns a copy of the `IotaDIDUrl` path. #[wasm_bindgen] pub fn path(&self) -> Option { self.0.path().map(str::to_owned) } - /// Sets the `path` component of the `DIDUrl`. + /// Sets the `path` component of the `IotaDIDUrl`. #[wasm_bindgen(js_name = setPath)] pub fn set_path(&mut self, value: Option) -> Result<()> { self.0.set_path(value.as_deref()).wasm_result() } - /// Returns a copy of the `DIDUrl` method query, if any. Excludes the leading '?'. + /// Returns a copy of the `IotaDIDUrl` method query, if any. Excludes the leading '?'. #[wasm_bindgen] pub fn query(&self) -> Option { self.0.query().map(str::to_owned) } - /// Sets the `query` component of the `DIDUrl`. + /// Sets the `query` component of the `IotaDIDUrl`. #[wasm_bindgen(js_name = setQuery)] pub fn set_query(&mut self, value: Option) -> Result<()> { self.0.set_query(value.as_deref()).wasm_result() } - /// Append a string representing a path, query, and/or fragment, returning a new `DIDUrl`. + /// Append a string representing a path, query, and/or fragment, returning a new `IotaDIDUrl`. /// /// Must begin with a valid delimiter character: '/', '?', '#'. Overwrites the existing URL /// segment and any following segments in order of path, query, then fragment. @@ -80,11 +78,11 @@ impl WasmDIDUrl { /// - joining a query will clear the fragment. /// - joining a fragment will only overwrite the fragment. #[wasm_bindgen] - pub fn join(&self, segment: &str) -> Result { - self.0.join(segment).map(WasmDIDUrl::from).wasm_result() + pub fn join(&self, segment: &str) -> Result { + self.0.join(segment).map(WasmIotaDIDUrl::from).wasm_result() } - /// Returns the `DIDUrl` as a string. + /// Returns the `IotaDIDUrl` as a string. #[allow(clippy::inherent_to_string)] #[wasm_bindgen(js_name = toString)] pub fn to_string(&self) -> String { @@ -92,17 +90,17 @@ impl WasmDIDUrl { } } -impl_wasm_json!(WasmDIDUrl, DIDUrl); -impl_wasm_clone!(WasmDIDUrl, DIDUrl); +impl_wasm_json!(WasmIotaDIDUrl, IotaDIDUrl); +impl_wasm_clone!(WasmIotaDIDUrl, IotaDIDUrl); -impl From for WasmDIDUrl { +impl From for WasmIotaDIDUrl { fn from(did_url: IotaDIDUrl) -> Self { Self(did_url) } } -impl From for IotaDIDUrl { - fn from(wasm_did_url: WasmDIDUrl) -> Self { +impl From for IotaDIDUrl { + fn from(wasm_did_url: WasmIotaDIDUrl) -> Self { wasm_did_url.0 } } diff --git a/bindings/wasm/src/stardust/stardust_document.rs b/bindings/wasm/src/iota/iota_document.rs similarity index 76% rename from bindings/wasm/src/stardust/stardust_document.rs rename to bindings/wasm/src/iota/iota_document.rs index 9d5cf625a5..422f9c529b 100644 --- a/bindings/wasm/src/stardust/stardust_document.rs +++ b/bindings/wasm/src/iota/iota_document.rs @@ -12,11 +12,11 @@ use identity_iota::crypto::ProofOptions; use identity_iota::did::verifiable::VerifiableProperties; use identity_iota::did::Document; use identity_iota::did::MethodScope; -use identity_stardust::NetworkName; -use identity_stardust::StardustDID; -use identity_stardust::StardustDocument; -use identity_stardust::StardustVerificationMethod; -use identity_stardust::StateMetadataEncoding; +use identity_iota::iota::IotaDID; +use identity_iota::iota::IotaDocument; +use identity_iota::iota::IotaVerificationMethod; +use identity_iota::iota::NetworkName; +use identity_iota::iota::StateMetadataEncoding; use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; @@ -35,38 +35,38 @@ use crate::did::WasmMethodScope; use crate::did::WasmVerifierOptions; use crate::error::Result; use crate::error::WasmResult; -use crate::stardust::WasmStardustDID; -use crate::stardust::WasmStardustDIDUrl; -use crate::stardust::WasmStardustDocumentMetadata; -use crate::stardust::WasmStardustService; -use crate::stardust::WasmStardustVerificationMethod; -use crate::stardust::WasmStateMetadataEncoding; +use crate::iota::WasmIotaDID; +use crate::iota::WasmIotaDIDUrl; +use crate::iota::WasmIotaDocumentMetadata; +use crate::iota::WasmIotaService; +use crate::iota::WasmIotaVerificationMethod; +use crate::iota::WasmStateMetadataEncoding; // ============================================================================= // ============================================================================= -#[wasm_bindgen(js_name = StardustDocument, inspectable)] -pub struct WasmStardustDocument(pub(crate) StardustDocument); +#[wasm_bindgen(js_name = IotaDocument, inspectable)] +pub struct WasmIotaDocument(pub(crate) IotaDocument); -#[wasm_bindgen(js_class = StardustDocument)] -impl WasmStardustDocument { +#[wasm_bindgen(js_class = IotaDocument)] +impl WasmIotaDocument { // =========================================================================== // Constructors // =========================================================================== - /// Constructs an empty DID Document with a {@link StardustDID.placeholder} identifier + /// Constructs an empty DID Document with a {@link IotaDID.placeholder} identifier /// for the given `network`. #[wasm_bindgen(constructor)] - pub fn new(network: String) -> Result { + pub fn new(network: String) -> Result { let network_name: NetworkName = NetworkName::try_from(network).wasm_result()?; - Ok(WasmStardustDocument::from(StardustDocument::new(&network_name))) + Ok(WasmIotaDocument::from(IotaDocument::new(&network_name))) } /// Constructs an empty DID Document with the given identifier. #[wasm_bindgen(js_name = newWithId)] - pub fn new_with_id(id: &WasmStardustDID) -> WasmStardustDocument { - let did: StardustDID = id.0.clone(); - WasmStardustDocument::from(StardustDocument::new_with_id(did)) + pub fn new_with_id(id: &WasmIotaDID) -> WasmIotaDocument { + let did: IotaDID = id.0.clone(); + WasmIotaDocument::from(IotaDocument::new_with_id(did)) } // =========================================================================== @@ -75,8 +75,8 @@ impl WasmStardustDocument { /// Returns a copy of the DID Document `id`. #[wasm_bindgen] - pub fn id(&self) -> WasmStardustDID { - WasmStardustDID::from(self.0.id().clone()) + pub fn id(&self) -> WasmIotaDID { + WasmIotaDID::from(self.0.id().clone()) } /// Returns a copy of the list of document controllers. @@ -84,16 +84,16 @@ impl WasmStardustDocument { /// NOTE: controllers are determined by the `state_controller` unlock condition of the output /// during resolution and are omitted when publishing. #[wasm_bindgen] - pub fn controller(&self) -> ArrayStardustDID { + pub fn controller(&self) -> ArrayIotaDID { match self.0.controller() { Some(controllers) => controllers .iter() .cloned() - .map(WasmStardustDID::from) + .map(WasmIotaDID::from) .map(JsValue::from) .collect::() - .unchecked_into::(), - None => js_sys::Array::new().unchecked_into::(), + .unchecked_into::(), + None => js_sys::Array::new().unchecked_into::(), } } @@ -153,75 +153,75 @@ impl WasmStardustDocument { // Services // =========================================================================== - /// Return a set of all {@link StardustService} in the document. + /// Return a set of all {@link IotaService} in the document. #[wasm_bindgen] - pub fn service(&self) -> ArrayStardustService { + pub fn service(&self) -> ArrayIotaService { self .0 .service() .iter() .cloned() - .map(WasmStardustService) + .map(WasmIotaService) .map(JsValue::from) .collect::() - .unchecked_into::() + .unchecked_into::() } - /// Add a new {@link StardustService} to the document. + /// Add a new {@link IotaService} to the document. /// /// Returns `true` if the service was added. #[wasm_bindgen(js_name = insertService)] - pub fn insert_service(&mut self, service: &WasmStardustService) -> bool { + pub fn insert_service(&mut self, service: &WasmIotaService) -> bool { self.0.insert_service(service.0.clone()) } - /// Remove a {@link StardustService} identified by the given {@link StardustDIDUrl} from the document. + /// Remove a {@link IotaService} identified by the given {@link IotaDIDUrl} from the document. /// /// Returns `true` if a service was removed. #[wasm_bindgen(js_name = removeService)] - pub fn remove_service(&mut self, did: &WasmStardustDIDUrl) -> bool { + pub fn remove_service(&mut self, did: &WasmIotaDIDUrl) -> bool { self.0.remove_service(&did.0) } - /// Returns the first {@link StardustService} with an `id` property matching the provided `query`, + /// Returns the first {@link IotaService} with an `id` property matching the provided `query`, /// if present. #[wasm_bindgen(js_name = resolveService)] - pub fn resolve_service(&self, query: &UStardustDIDUrlQuery) -> Option { + pub fn resolve_service(&self, query: &UIotaDIDUrlQuery) -> Option { let service_query: String = query.into_serde().ok()?; self .0 .resolve_service(&service_query) .cloned() - .map(WasmStardustService::from) + .map(WasmIotaService::from) } // =========================================================================== // Verification Methods // =========================================================================== - /// Returns a list of all {@link StardustVerificationMethod} in the DID Document. + /// Returns a list of all {@link IotaVerificationMethod} in the DID Document. #[wasm_bindgen] - pub fn methods(&self) -> ArrayStardustVerificationMethods { + pub fn methods(&self) -> ArrayIotaVerificationMethods { self .0 .methods() .cloned() - .map(WasmStardustVerificationMethod::from) + .map(WasmIotaVerificationMethod::from) .map(JsValue::from) .collect::() - .unchecked_into::() + .unchecked_into::() } /// Adds a new `method` to the document in the given `scope`. #[wasm_bindgen(js_name = insertMethod)] - pub fn insert_method(&mut self, method: &WasmStardustVerificationMethod, scope: &WasmMethodScope) -> Result<()> { + pub fn insert_method(&mut self, method: &WasmIotaVerificationMethod, scope: &WasmMethodScope) -> Result<()> { self.0.insert_method(method.0.clone(), scope.0).wasm_result()?; Ok(()) } /// Removes all references to the specified Verification Method. #[wasm_bindgen(js_name = removeMethod)] - pub fn remove_method(&mut self, did: &WasmStardustDIDUrl) -> Result<()> { + pub fn remove_method(&mut self, did: &WasmIotaDIDUrl) -> Result<()> { self.0.remove_method(&did.0).wasm_result() } @@ -231,14 +231,14 @@ impl WasmStardustDocument { #[wasm_bindgen(js_name = resolveMethod)] pub fn resolve_method( &self, - query: &UStardustDIDUrlQuery, + query: &UIotaDIDUrlQuery, scope: Option, - ) -> Result> { + ) -> Result> { let method_query: String = query.into_serde().wasm_result()?; let method_scope: Option = scope.map(|js| js.into_serde().wasm_result()).transpose()?; - let method: Option<&StardustVerificationMethod> = self.0.resolve_method(&method_query, method_scope); - Ok(method.cloned().map(WasmStardustVerificationMethod)) + let method: Option<&IotaVerificationMethod> = self.0.resolve_method(&method_query, method_scope); + Ok(method.cloned().map(WasmIotaVerificationMethod)) } /// Attaches the relationship to the given method, if the method exists. @@ -249,7 +249,7 @@ impl WasmStardustDocument { #[wasm_bindgen(js_name = attachMethodRelationship)] pub fn attach_method_relationship( &mut self, - didUrl: &WasmStardustDIDUrl, + didUrl: &WasmIotaDIDUrl, relationship: WasmMethodRelationship, ) -> Result { self @@ -263,7 +263,7 @@ impl WasmStardustDocument { #[wasm_bindgen(js_name = detachMethodRelationship)] pub fn detach_method_relationship( &mut self, - didUrl: &WasmStardustDIDUrl, + didUrl: &WasmIotaDIDUrl, relationship: WasmMethodRelationship, ) -> Result { self @@ -284,7 +284,7 @@ impl WasmStardustDocument { &self, credential: &WasmCredential, privateKey: Vec, - methodQuery: &UStardustDIDUrlQuery, + methodQuery: &UIotaDIDUrlQuery, options: &WasmProofOptions, ) -> Result { let mut data: Credential = credential.0.clone(); @@ -307,7 +307,7 @@ impl WasmStardustDocument { &self, presentation: &WasmPresentation, privateKey: Vec, - methodQuery: &UStardustDIDUrlQuery, + methodQuery: &UIotaDIDUrlQuery, options: &WasmProofOptions, ) -> Result { let mut data: Presentation = presentation.0.clone(); @@ -332,7 +332,7 @@ impl WasmStardustDocument { &self, data: &JsValue, privateKey: Vec, - methodQuery: &UStardustDIDUrlQuery, + methodQuery: &UIotaDIDUrlQuery, options: &WasmProofOptions, ) -> Result { let mut data: VerifiableProperties = data.into_serde().wasm_result()?; @@ -390,9 +390,9 @@ impl WasmStardustDocument { /// encoded in the `AliasId` alone. #[allow(non_snake_case)] #[wasm_bindgen] - pub fn unpack(did: &WasmStardustDID, stateMetadata: &[u8], allowEmpty: bool) -> Result { - StardustDocument::unpack(&did.0, stateMetadata, allowEmpty) - .map(WasmStardustDocument) + pub fn unpack(did: &WasmIotaDID, stateMetadata: &[u8], allowEmpty: bool) -> Result { + IotaDocument::unpack(&did.0, stateMetadata, allowEmpty) + .map(WasmIotaDocument) .wasm_result() } @@ -401,26 +401,28 @@ impl WasmStardustDocument { /// /// Errors if any Alias Output does not contain a valid or empty DID Document. #[wasm_bindgen(js_name = unpackFromBlock)] - pub fn unpack_from_block(network: String, block: &IBlock) -> Result { + pub fn unpack_from_block(network: String, block: &IBlock) -> Result { let network_name: NetworkName = NetworkName::try_from(network).wasm_result()?; let block_dto: bee_block::BlockDto = block .into_serde() .map_err(|err| { - identity_stardust::Error::JsError(format!("unpackFromBlock failed to deserialize BlockDto: {}", err)) + identity_iota::iota::Error::JsError(format!("unpackFromBlock failed to deserialize BlockDto: {}", err)) }) .wasm_result()?; let block: bee_block::Block = bee_block::Block::try_from(&block_dto) - .map_err(|err| identity_stardust::Error::JsError(format!("unpackFromBlock failed to convert BlockDto: {}", err))) + .map_err(|err| { + identity_iota::iota::Error::JsError(format!("unpackFromBlock failed to convert BlockDto: {}", err)) + }) .wasm_result()?; Ok( - StardustDocument::unpack_from_block(&network_name, &block) + IotaDocument::unpack_from_block(&network_name, &block) .wasm_result()? .into_iter() - .map(WasmStardustDocument::from) + .map(WasmIotaDocument::from) .map(JsValue::from) .collect::() - .unchecked_into::(), + .unchecked_into::(), ) } @@ -433,8 +435,8 @@ impl WasmStardustDocument { /// NOTE: Copies all the metadata. See also `metadataCreated`, `metadataUpdated`, /// `metadataPreviousMessageId`, `metadataProof` if only a subset of the metadata required. #[wasm_bindgen] - pub fn metadata(&self) -> WasmStardustDocumentMetadata { - WasmStardustDocumentMetadata::from(self.0.metadata.clone()) + pub fn metadata(&self) -> WasmIotaDocumentMetadata { + WasmIotaDocumentMetadata::from(self.0.metadata.clone()) } /// Returns a copy of the timestamp of when the DID document was created. @@ -501,7 +503,7 @@ impl WasmStardustDocument { /// revoke all specified `indices`. #[wasm_bindgen(js_name = revokeCredentials)] #[allow(non_snake_case)] - pub fn revoke_credentials(&mut self, serviceQuery: &UStardustDIDUrlQuery, indices: UOneOrManyNumber) -> Result<()> { + pub fn revoke_credentials(&mut self, serviceQuery: &UIotaDIDUrlQuery, indices: UOneOrManyNumber) -> Result<()> { let query: String = serviceQuery.into_serde().wasm_result()?; let indices: OneOrMany = indices.into_serde().wasm_result()?; @@ -512,7 +514,7 @@ impl WasmStardustDocument { /// unrevoke all specified `indices`. #[wasm_bindgen(js_name = unrevokeCredentials)] #[allow(non_snake_case)] - pub fn unrevoke_credentials(&mut self, serviceQuery: &UStardustDIDUrlQuery, indices: UOneOrManyNumber) -> Result<()> { + pub fn unrevoke_credentials(&mut self, serviceQuery: &UIotaDIDUrlQuery, indices: UOneOrManyNumber) -> Result<()> { let query: String = serviceQuery.into_serde().wasm_result()?; let indices: OneOrMany = indices.into_serde().wasm_result()?; @@ -520,37 +522,37 @@ impl WasmStardustDocument { } } -impl_wasm_json!(WasmStardustDocument, StardustDocument); -impl_wasm_clone!(WasmStardustDocument, StardustDocument); +impl_wasm_json!(WasmIotaDocument, IotaDocument); +impl_wasm_clone!(WasmIotaDocument, IotaDocument); -impl From for WasmStardustDocument { - fn from(document: StardustDocument) -> Self { +impl From for WasmIotaDocument { + fn from(document: IotaDocument) -> Self { Self(document) } } -impl From for StardustDocument { - fn from(wasm_document: WasmStardustDocument) -> Self { +impl From for IotaDocument { + fn from(wasm_document: WasmIotaDocument) -> Self { wasm_document.0 } } #[wasm_bindgen] extern "C" { - #[wasm_bindgen(typescript_type = "StardustDIDUrl | string")] - pub type UStardustDIDUrlQuery; + #[wasm_bindgen(typescript_type = "IotaDIDUrl | string")] + pub type UIotaDIDUrlQuery; - #[wasm_bindgen(typescript_type = "StardustDID[]")] - pub type ArrayStardustDID; + #[wasm_bindgen(typescript_type = "IotaDID[]")] + pub type ArrayIotaDID; - #[wasm_bindgen(typescript_type = "StardustDocument[]")] - pub type ArrayStardustDocument; + #[wasm_bindgen(typescript_type = "IotaDocument[]")] + pub type ArrayIotaDocument; - #[wasm_bindgen(typescript_type = "StardustService[]")] - pub type ArrayStardustService; + #[wasm_bindgen(typescript_type = "IotaService[]")] + pub type ArrayIotaService; - #[wasm_bindgen(typescript_type = "StardustVerificationMethod[]")] - pub type ArrayStardustVerificationMethods; + #[wasm_bindgen(typescript_type = "IotaVerificationMethod[]")] + pub type ArrayIotaVerificationMethods; // External interface from `@iota/types`, must be deserialized via BlockDto. #[wasm_bindgen(typescript_type = "IBlock")] diff --git a/bindings/wasm/src/stardust/stardust_document_metadata.rs b/bindings/wasm/src/iota/iota_document_metadata.rs similarity index 68% rename from bindings/wasm/src/stardust/stardust_document_metadata.rs rename to bindings/wasm/src/iota/iota_document_metadata.rs index 7e40ce4714..7d7f6a1578 100644 --- a/bindings/wasm/src/stardust/stardust_document_metadata.rs +++ b/bindings/wasm/src/iota/iota_document_metadata.rs @@ -1,7 +1,7 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use identity_stardust::StardustDocumentMetadata; +use identity_iota::iota::IotaDocumentMetadata; use wasm_bindgen::prelude::*; use crate::common::MapStringAny; @@ -9,13 +9,13 @@ use crate::common::WasmTimestamp; use crate::error::Result; /// Additional attributes related to an IOTA DID Document. -#[wasm_bindgen(js_name = StardustDocumentMetadata, inspectable)] -pub struct WasmStardustDocumentMetadata(pub(crate) StardustDocumentMetadata); +#[wasm_bindgen(js_name = IotaDocumentMetadata, inspectable)] +pub struct WasmIotaDocumentMetadata(pub(crate) IotaDocumentMetadata); // NOTE: these properties are read-only (no setters) to prevent bugs where a clone of the metadata // is updated instead of the actual instance in the document. -#[wasm_bindgen(js_class = StardustDocumentMetadata)] -impl WasmStardustDocumentMetadata { +#[wasm_bindgen(js_class = IotaDocumentMetadata)] +impl WasmIotaDocumentMetadata { /// Returns a copy of the timestamp of when the DID document was created. #[wasm_bindgen] pub fn created(&self) -> Option { @@ -41,11 +41,11 @@ impl WasmStardustDocumentMetadata { } } -impl_wasm_json!(WasmStardustDocumentMetadata, StardustDocumentMetadata); -impl_wasm_clone!(WasmStardustDocumentMetadata, StardustDocumentMetadata); +impl_wasm_json!(WasmIotaDocumentMetadata, IotaDocumentMetadata); +impl_wasm_clone!(WasmIotaDocumentMetadata, IotaDocumentMetadata); -impl From for WasmStardustDocumentMetadata { - fn from(metadata: StardustDocumentMetadata) -> Self { +impl From for WasmIotaDocumentMetadata { + fn from(metadata: IotaDocumentMetadata) -> Self { Self(metadata) } } diff --git a/bindings/wasm/src/stardust/state_metadata_encoding.rs b/bindings/wasm/src/iota/iota_metadata_encoding.rs similarity index 93% rename from bindings/wasm/src/stardust/state_metadata_encoding.rs rename to bindings/wasm/src/iota/iota_metadata_encoding.rs index eb019851da..98e629dfb0 100644 --- a/bindings/wasm/src/stardust/state_metadata_encoding.rs +++ b/bindings/wasm/src/iota/iota_metadata_encoding.rs @@ -1,7 +1,7 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use identity_stardust::StateMetadataEncoding; +use identity_iota::iota::StateMetadataEncoding; use serde_repr::Deserialize_repr; use serde_repr::Serialize_repr; use wasm_bindgen::prelude::*; diff --git a/bindings/wasm/src/stardust/stardust_service.rs b/bindings/wasm/src/iota/iota_service.rs similarity index 60% rename from bindings/wasm/src/stardust/stardust_service.rs rename to bindings/wasm/src/iota/iota_service.rs index 68978766eb..0f930c60dc 100644 --- a/bindings/wasm/src/stardust/stardust_service.rs +++ b/bindings/wasm/src/iota/iota_service.rs @@ -3,8 +3,8 @@ use identity_iota::core::OneOrMany; use identity_iota::did::ServiceEndpoint; -use identity_stardust::StardustDIDUrl; -use identity_stardust::StardustService; +use identity_iota::iota::IotaDIDUrl; +use identity_iota::iota::IotaService; use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; @@ -15,36 +15,36 @@ use crate::did::IService; use crate::did::UServiceEndpoint; use crate::error::Result; use crate::error::WasmResult; -use crate::stardust::WasmStardustDIDUrl; +use crate::iota::WasmIotaDIDUrl; -/// A `Service` adhering to the IOTA UTXO DID method specification. -#[wasm_bindgen(js_name = StardustService, inspectable)] -pub struct WasmStardustService(pub(crate) StardustService); +/// A `Service` adhering to the IOTA DID method specification. +#[wasm_bindgen(js_name = IotaService, inspectable)] +pub struct WasmIotaService(pub(crate) IotaService); -#[wasm_bindgen(js_class = StardustService)] -impl WasmStardustService { +#[wasm_bindgen(js_class = IotaService)] +impl WasmIotaService { #[wasm_bindgen(constructor)] - pub fn new(service: IStardustService) -> Result { - let id: StardustDIDUrl = service.id().into_serde().wasm_result()?; + pub fn new(service: IIotaService) -> Result { + let id: IotaDIDUrl = service.id().into_serde().wasm_result()?; let base_service: &IService = service.as_ref(); let types: OneOrMany = service.type_().into_serde().wasm_result()?; let service_endpoint: ServiceEndpoint = deserialize_map_or_any(&base_service.service_endpoint())?; let properties: Option = deserialize_map_or_any(&base_service.properties())?; - StardustService::builder(properties.unwrap_or_default()) + IotaService::builder(properties.unwrap_or_default()) .id(id) .types(types) .service_endpoint(service_endpoint) .build() - .map(WasmStardustService) + .map(WasmIotaService) .wasm_result() } /// Returns a copy of the `Service` id. #[wasm_bindgen] - pub fn id(&self) -> WasmStardustDIDUrl { - WasmStardustDIDUrl::from(self.0.id().clone()) + pub fn id(&self) -> WasmIotaDIDUrl { + WasmIotaDIDUrl::from(self.0.id().clone()) } /// Returns a copy of the `Service` type. @@ -73,34 +73,34 @@ impl WasmStardustService { } } -impl_wasm_json!(WasmStardustService, StardustService); -impl_wasm_clone!(WasmStardustService, StardustService); +impl_wasm_json!(WasmIotaService, IotaService); +impl_wasm_clone!(WasmIotaService, IotaService); -impl From for WasmStardustService { - fn from(service: StardustService) -> Self { +impl From for WasmIotaService { + fn from(service: IotaService) -> Self { Self(service) } } #[wasm_bindgen] extern "C" { - #[wasm_bindgen(typescript_type = "IStardustService", extends = IService)] - pub type IStardustService; + #[wasm_bindgen(typescript_type = "IIotaService", extends = IService)] + pub type IIotaService; #[wasm_bindgen(method, getter)] - pub fn id(this: &IStardustService) -> JsValue; + pub fn id(this: &IIotaService) -> JsValue; } #[wasm_bindgen(typescript_custom_section)] -const I_STARDUST_SERVICE: &'static str = r#" +const I_IOTA_SERVICE: &'static str = r#" /** - * Holds options to create a new `StardustService`. + * Holds options to create a new `IotaService`. */ -interface IStardustService extends IService { +interface IIotaService extends IService { /** * Identifier of the service. * * Must be a valid DIDUrl with a fragment. */ - readonly id: StardustDIDUrl | string; + readonly id: IotaDIDUrl | string; }"#; diff --git a/bindings/wasm/src/stardust/stardust_verification_method.rs b/bindings/wasm/src/iota/iota_verification_method.rs similarity index 55% rename from bindings/wasm/src/stardust/stardust_verification_method.rs rename to bindings/wasm/src/iota/iota_verification_method.rs index 0bff7e189f..554b2b4c44 100644 --- a/bindings/wasm/src/stardust/stardust_verification_method.rs +++ b/bindings/wasm/src/iota/iota_verification_method.rs @@ -1,83 +1,84 @@ // Copyright 2020-2022 Stiftung // SPDX-License-Identifier: Apache-2.0 -use crate::common::MapStringAny; use identity_iota::crypto::PublicKey; -use identity_stardust::StardustVerificationMethod; + +use identity_iota::iota::IotaVerificationMethod; use wasm_bindgen::prelude::*; +use crate::common::MapStringAny; use crate::crypto::WasmKeyType; use crate::did::WasmMethodData; use crate::did::WasmMethodType; use crate::error::Result; use crate::error::WasmResult; -use crate::stardust::WasmStardustDID; -use crate::stardust::WasmStardustDIDUrl; +use crate::iota::WasmIotaDID; +use crate::iota::WasmIotaDIDUrl; -#[wasm_bindgen(js_name = StardustVerificationMethod, inspectable)] -pub struct WasmStardustVerificationMethod(pub(crate) StardustVerificationMethod); +#[wasm_bindgen(js_name = IotaVerificationMethod, inspectable)] +pub struct WasmIotaVerificationMethod(pub(crate) IotaVerificationMethod); -#[wasm_bindgen(js_class = StardustVerificationMethod)] -impl WasmStardustVerificationMethod { - /// Creates a new `StardustVerificationMethod` from the given `did` and public key. +#[wasm_bindgen(js_class = IotaVerificationMethod)] +impl WasmIotaVerificationMethod { + /// Creates a new `IotaVerificationMethod` from the given `did` and public key. #[allow(non_snake_case)] #[wasm_bindgen(constructor)] pub fn new( - did: &WasmStardustDID, + did: &WasmIotaDID, keyType: WasmKeyType, publicKey: Vec, fragment: String, - ) -> Result { + ) -> Result { let public_key: PublicKey = PublicKey::from(publicKey); - StardustVerificationMethod::new(did.0.clone(), keyType.into(), &public_key, &fragment) + IotaVerificationMethod::new(did.0.clone(), keyType.into(), &public_key, &fragment) .map(Self) .wasm_result() } - /// Returns a copy of the `StardustVerificationMethod` id. + /// Returns a reference to the `IotaVerificationMethod` id. #[wasm_bindgen] - pub fn id(&self) -> WasmStardustDIDUrl { - WasmStardustDIDUrl::from(self.0.id().clone()) + pub fn id(&self) -> WasmIotaDIDUrl { + WasmIotaDIDUrl::from(self.0.id().clone()) } - /// Sets the id of the `StardustVerificationMethod`. + /// Sets the id of the `IotaVerificationMethod`. #[wasm_bindgen(js_name = setId)] - pub fn set_id(&mut self, id: &WasmStardustDIDUrl) -> Result<()> { + pub fn set_id(&mut self, id: &WasmIotaDIDUrl) -> Result<()> { self.0.set_id(id.0.clone()).wasm_result()?; Ok(()) } - /// Returns a copy of the `controller` `DID` of the `StardustVerificationMethod`. + /// Returns a copy of the `controller` `DID` of the `IotaVerificationMethod`. #[wasm_bindgen] - pub fn controller(&self) -> WasmStardustDID { - WasmStardustDID::from(self.0.controller().clone()) + pub fn controller(&self) -> WasmIotaDID { + WasmIotaDID::from(self.0.controller().clone()) } - /// Sets the `controller` `DID` of the `StardustVerificationMethod`. + /// Sets the `controller` `DID` of the `IotaVerificationMethod`. #[wasm_bindgen(js_name = setController)] - pub fn set_controller(&mut self, did: &WasmStardustDID) { + pub fn set_controller(&mut self, did: &WasmIotaDID) { *self.0.controller_mut() = did.0.clone(); } - /// Returns a copy of the `StardustVerificationMethod` type. - #[wasm_bindgen(js_name = type)] - pub fn type_(&self) -> WasmMethodType { - WasmMethodType::from(self.0.type_()) - } - - /// Sets the `StardustVerificationMethod` type. + /// Sets the `IotaVerificationMethod` type. #[wasm_bindgen(js_name = setType)] pub fn set_type(&mut self, type_: &WasmMethodType) { *self.0.type_mut() = type_.0; } - /// Returns a copy of the `StardustVerificationMethod` public key data. + /// Returns a copy of the `IotaVerificationMethod` type. + #[wasm_bindgen(js_name = type)] + pub fn type_(&self) -> WasmMethodType { + WasmMethodType::from(self.0.type_()) + } + + /// Returns a copy of the `IotaVerificationMethod` public key data. #[wasm_bindgen] pub fn data(&self) -> WasmMethodData { WasmMethodData::from(self.0.data().clone()) } - /// Sets `StardustVerificationMethod` public key data. + /// Sets `IotaVerificationMethod` public key data. #[wasm_bindgen(js_name = setData)] pub fn set_data(&mut self, data: &WasmMethodData) { *self.0.data_mut() = data.0.clone(); @@ -110,11 +111,11 @@ impl WasmStardustVerificationMethod { } } -impl_wasm_json!(WasmStardustVerificationMethod, StardustVerificationMethod); -impl_wasm_clone!(WasmStardustVerificationMethod, StardustVerificationMethod); +impl_wasm_json!(WasmIotaVerificationMethod, IotaVerificationMethod); +impl_wasm_clone!(WasmIotaVerificationMethod, IotaVerificationMethod); -impl From for WasmStardustVerificationMethod { - fn from(method: StardustVerificationMethod) -> Self { +impl From for WasmIotaVerificationMethod { + fn from(method: IotaVerificationMethod) -> Self { Self(method) } } diff --git a/bindings/wasm/src/iota/mod.rs b/bindings/wasm/src/iota/mod.rs new file mode 100644 index 0000000000..0db9d5228e --- /dev/null +++ b/bindings/wasm/src/iota/mod.rs @@ -0,0 +1,22 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +pub(crate) use identity_client::WasmIotaIdentityClient; +pub use identity_client_ext::PromiseIotaDocument; +pub use iota_did::WasmIotaDID; +pub use iota_did_url::WasmIotaDIDUrl; +pub use iota_document::WasmIotaDocument; +pub use iota_document_metadata::WasmIotaDocumentMetadata; +pub use iota_metadata_encoding::WasmStateMetadataEncoding; +pub use iota_service::WasmIotaService; +pub use iota_verification_method::WasmIotaVerificationMethod; + +mod identity_client; +mod identity_client_ext; +mod iota_did; +mod iota_did_url; +mod iota_document; +mod iota_document_metadata; +mod iota_metadata_encoding; +mod iota_service; +mod iota_verification_method; diff --git a/bindings/wasm/src/lib.rs b/bindings/wasm/src/lib.rs index 4feb55610d..9abcf69fb1 100644 --- a/bindings/wasm/src/lib.rs +++ b/bindings/wasm/src/lib.rs @@ -17,16 +17,16 @@ use wasm_bindgen::prelude::*; #[macro_use] mod macros; -pub mod account; -pub mod chain; +// Deactivated legacy packages. +// pub mod account; + pub mod common; pub mod credential; pub mod crypto; pub mod did; pub mod error; +pub mod iota; pub mod revocation; -pub mod stardust; -pub mod tangle; pub mod resolver; diff --git a/bindings/wasm/src/resolver/constructor_input.rs b/bindings/wasm/src/resolver/constructor_input.rs index 7eb33b1022..54c3c1fb52 100644 --- a/bindings/wasm/src/resolver/constructor_input.rs +++ b/bindings/wasm/src/resolver/constructor_input.rs @@ -3,7 +3,7 @@ use wasm_bindgen::prelude::*; -use crate::stardust::WasmStardustIdentityClient; +use crate::iota::WasmIotaIdentityClient; #[wasm_bindgen] extern "C" { @@ -14,7 +14,7 @@ extern "C" { pub type ResolverConfig; #[wasm_bindgen(method, getter)] - pub(crate) fn client(this: &ResolverConfig) -> Option; + pub(crate) fn client(this: &ResolverConfig) -> Option; #[wasm_bindgen(method, getter)] pub(crate) fn handlers(this: &ResolverConfig) -> Option; @@ -25,7 +25,7 @@ extern "C" { // definitions (which would be accepted by JSDocs). #[wasm_bindgen(typescript_custom_section)] const HANDLERS: &'static str = - "export type ResolutionHandlers = Map Promise>;"; + "export type ResolutionHandlers = Map Promise>;"; #[wasm_bindgen(typescript_custom_section)] const TS_RESOLVER_CONFIG: &'static str = r#" @@ -37,7 +37,7 @@ export type ResolverConfig = { /** * Client for resolving DIDs of the iota method. */ - client?: IStardustIdentityClient, + client?: IIotaIdentityClient, /** * Handlers for resolving DIDs from arbitrary DID methods. @@ -46,6 +46,6 @@ export type ResolverConfig = { * * Note that if a `client` is given the key "iota" may NOT be present in this map. */ - handlers?: Map Promise> + handlers?: Map Promise> }; "#; diff --git a/bindings/wasm/src/resolver/mixed_resolver.rs b/bindings/wasm/src/resolver/mixed_resolver.rs index ae35954b3c..507d60ede1 100644 --- a/bindings/wasm/src/resolver/mixed_resolver.rs +++ b/bindings/wasm/src/resolver/mixed_resolver.rs @@ -9,10 +9,10 @@ use identity_iota::credential::Presentation; use identity_iota::credential::PresentationValidationOptions; use identity_iota::did::CoreDID; use identity_iota::did::DID; +use identity_iota::iota::IotaDID; +use identity_iota::iota::IotaDocument; +use identity_iota::iota::IotaIdentityClientExt; use identity_resolver::SingleThreadedResolver; -use identity_stardust::StardustDID; -use identity_stardust::StardustDocument; -use identity_stardust::StardustIdentityClientExt; use js_sys::Function; use js_sys::Map; use js_sys::Promise; @@ -24,13 +24,13 @@ use crate::credential::WasmPresentation; use crate::credential::WasmPresentationValidationOptions; use crate::error::JsValueResult; use crate::error::WasmError; +use crate::iota::WasmIotaDID; +use crate::iota::WasmIotaIdentityClient; use crate::resolver::constructor_input::MapResolutionHandler; use crate::resolver::constructor_input::ResolverConfig; use crate::resolver::supported_document_types::OptionArraySupportedDocument; use crate::resolver::supported_document_types::OptionSupportedDocument; use crate::resolver::supported_document_types::RustSupportedDocument; -use crate::stardust::WasmStardustDID; -use crate::stardust::WasmStardustIdentityClient; use super::supported_document_types::PromiseArraySupportedDocument; use super::supported_document_types::PromiseSupportedDocument; @@ -63,7 +63,7 @@ impl MixedResolver { let mut attached_iota_method = false; let resolution_handlers: Option = config.handlers(); - let client: Option = config.client(); + let client: Option = config.client(); if let Some(handlers) = resolution_handlers { let map: &Map = handlers.dyn_ref::().ok_or_else(|| { @@ -86,26 +86,26 @@ impl MixedResolver { ))?; } - let rc_client: Rc = Rc::new(wasm_client); - // Take CoreDID (instead of StardustDID) to avoid inconsistent error messages between the + let rc_client: Rc = Rc::new(wasm_client); + // Take CoreDID (instead of IotaDID) to avoid inconsistent error messages between the // cases when the iota handler is attached by passing a client or directly as a handler. let handler = move |did: CoreDID| { - let rc_client_clone: Rc = rc_client.clone(); + let rc_client_clone: Rc = rc_client.clone(); async move { - let stardust_did: StardustDID = StardustDID::parse(did)?; - Self::client_as_handler(rc_client_clone.as_ref(), stardust_did.into()).await + let iota_did: IotaDID = IotaDID::parse(did)?; + Self::client_as_handler(rc_client_clone.as_ref(), iota_did.into()).await } }; - resolver.attach_handler(StardustDID::METHOD.to_owned(), handler); + resolver.attach_handler(IotaDID::METHOD.to_owned(), handler); } Ok(Self(Rc::new(resolver))) } pub(crate) async fn client_as_handler( - client: &WasmStardustIdentityClient, - did: WasmStardustDID, - ) -> std::result::Result { + client: &WasmIotaIdentityClient, + did: WasmIotaDID, + ) -> std::result::Result { client.resolve_did(&did.0).await } @@ -124,7 +124,7 @@ impl MixedResolver { .dyn_into::() .map_err(|_| "could not construct resolver: the handler map contains a value which is not a function")?; - if did_method == StardustDID::METHOD { + if did_method == IotaDID::METHOD { *attached_iota_method = true; } diff --git a/bindings/wasm/src/resolver/supported_document_types.rs b/bindings/wasm/src/resolver/supported_document_types.rs index 909078321e..63d98c586d 100644 --- a/bindings/wasm/src/resolver/supported_document_types.rs +++ b/bindings/wasm/src/resolver/supported_document_types.rs @@ -5,14 +5,14 @@ use crate::did::WasmCoreDID; use crate::did::WasmCoreDocument; use crate::error::WasmError; use crate::error::WasmResult; -use crate::stardust::WasmStardustDID; -use crate::stardust::WasmStardustDocument; +use crate::iota::WasmIotaDID; +use crate::iota::WasmIotaDocument; use identity_iota::credential::AbstractValidatorDocument; use identity_iota::did::CoreDID; use identity_iota::did::CoreDocument; use identity_iota::did::DID; -use identity_stardust::StardustDID; -use identity_stardust::StardustDocument; +use identity_iota::iota::IotaDID; +use identity_iota::iota::IotaDocument; use serde::Deserialize; use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; @@ -22,7 +22,7 @@ use wasm_bindgen::JsCast; /// Temporary type used to convert to and from Box until /// we port the Document trait to these bindings. pub enum RustSupportedDocument { - Stardust(StardustDocument), + Iota(IotaDocument), Core(CoreDocument), } @@ -30,7 +30,7 @@ impl From for JsValue { fn from(document: RustSupportedDocument) -> Self { match document { RustSupportedDocument::Core(doc) => JsValue::from(WasmCoreDocument::from(doc)), - RustSupportedDocument::Stardust(doc) => JsValue::from(WasmStardustDocument(doc)), + RustSupportedDocument::Iota(doc) => JsValue::from(WasmIotaDocument(doc)), } } } @@ -39,7 +39,7 @@ impl From for AbstractValidatorDocument { fn from(document: RustSupportedDocument) -> Self { match document { RustSupportedDocument::Core(core_doc) => AbstractValidatorDocument::from(core_doc), - RustSupportedDocument::Stardust(stardust_doc) => AbstractValidatorDocument::from(stardust_doc), + RustSupportedDocument::Iota(iota_doc) => AbstractValidatorDocument::from(iota_doc), } } } @@ -51,8 +51,8 @@ impl TryFrom for RustSupportedDocument { let supported_document = match upcast.downcast::() { Ok(doc) => RustSupportedDocument::Core(*doc), Err(retry) => { - if let Ok(doc) = retry.downcast::() { - RustSupportedDocument::Stardust(*doc) + if let Ok(doc) = retry.downcast::() { + RustSupportedDocument::Iota(*doc) } else { Err(WasmError::new( "CastingError".into(), @@ -69,9 +69,9 @@ impl TryFrom for SupportedDID { type Error = JsValue; fn try_from(did: CoreDID) -> Result { - let js: JsValue = if did.method() == StardustDID::METHOD { - let ret: StardustDID = StardustDID::try_from_core(did).wasm_result()?; - JsValue::from(WasmStardustDID::from(ret)) + let js: JsValue = if did.method() == IotaDID::METHOD { + let ret: IotaDID = IotaDID::try_from_core(did).wasm_result()?; + JsValue::from(WasmIotaDID::from(ret)) } else { JsValue::from(WasmCoreDID::from(did)) }; @@ -82,24 +82,24 @@ impl TryFrom for SupportedDID { #[wasm_bindgen] extern "C" { - #[wasm_bindgen(typescript_type = "Promise>")] + #[wasm_bindgen(typescript_type = "Promise>")] pub type PromiseArraySupportedDocument; - #[wasm_bindgen(typescript_type = "Promise")] + #[wasm_bindgen(typescript_type = "Promise")] pub type PromiseSupportedDocument; - #[wasm_bindgen(typescript_type = "StardustDocument | CoreDocument")] + #[wasm_bindgen(typescript_type = "IotaDocument | CoreDocument")] pub type SupportedDocument; - #[wasm_bindgen(typescript_type = "StardustDocument | CoreDocument | undefined")] + #[wasm_bindgen(typescript_type = "IotaDocument | CoreDocument | undefined")] pub type OptionSupportedDocument; - #[wasm_bindgen(typescript_type = "Array")] + #[wasm_bindgen(typescript_type = "Array")] pub type ArraySupportedDocument; - #[wasm_bindgen(typescript_type = "Array | undefined")] + #[wasm_bindgen(typescript_type = "Array | undefined")] pub type OptionArraySupportedDocument; - #[wasm_bindgen(typescript_type = "CoreDID | StardustDID")] + #[wasm_bindgen(typescript_type = "CoreDID | IotaDID")] pub type SupportedDID; } diff --git a/bindings/wasm/src/stardust/identity_client.rs b/bindings/wasm/src/stardust/identity_client.rs deleted file mode 100644 index 1620c20ed0..0000000000 --- a/bindings/wasm/src/stardust/identity_client.rs +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use core::fmt::Debug; -use core::fmt::Formatter; - -use identity_stardust::block::output::dto::AliasOutputDto; -use identity_stardust::block::output::AliasId; -use identity_stardust::block::output::AliasOutput; -use identity_stardust::block::output::OutputId; -use identity_stardust::block::output::RentStructure; -use identity_stardust::block::output::RentStructureBuilder; -use identity_stardust::StardustIdentityClient; -use js_sys::Promise; -use wasm_bindgen::prelude::*; -use wasm_bindgen_futures::JsFuture; - -use crate::error::JsValueResult; - -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(typescript_type = "IStardustIdentityClient")] - pub type WasmStardustIdentityClient; - - #[wasm_bindgen(method, js_name = getNetworkHrp)] - pub fn get_network_hrp(this: &WasmStardustIdentityClient) -> JsValue; - - #[allow(non_snake_case)] - #[wasm_bindgen(method, js_name = getAliasOutput)] - pub fn get_alias_output(this: &WasmStardustIdentityClient, aliasId: String) -> JsValue; - - #[wasm_bindgen(method, js_name = getRentStructure)] - pub fn get_rent_structure(this: &WasmStardustIdentityClient) -> JsValue; -} - -impl Debug for WasmStardustIdentityClient { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - f.write_str("WasmStardustIdentityClient") - } -} - -#[async_trait::async_trait(?Send)] -impl StardustIdentityClient for WasmStardustIdentityClient { - async fn get_network_hrp(&self) -> Result { - let promise: Promise = Promise::resolve(&WasmStardustIdentityClient::get_network_hrp(self)); - let result: JsValueResult = JsFuture::from(promise).await.into(); - let js: JsValue = result.to_stardust_error()?; - let network_hrp = match js.as_string() { - Some(hrp) => hrp, - None => js.into_serde().map_err(|err| { - identity_stardust::Error::JsError(format!("get_network_hrp failed to deserialize String: {}", err)) - })?, - }; - Ok(network_hrp) - } - - async fn get_alias_output(&self, id: AliasId) -> Result<(OutputId, AliasOutput), identity_stardust::Error> { - let promise: Promise = Promise::resolve(&WasmStardustIdentityClient::get_alias_output(self, id.to_string())); - let result: JsValueResult = JsFuture::from(promise).await.into(); - let tuple: js_sys::Array = js_sys::Array::from(&result.to_stardust_error()?); - let mut iter: js_sys::ArrayIter = tuple.iter(); - - let output_id: OutputId = iter - .next() - .ok_or_else(|| identity_stardust::Error::JsError("get_alias_output expected a tuple of size 2".to_owned()))? - .into_serde() - .map_err(|err| { - identity_stardust::Error::JsError(format!("get_alias_output failed to deserialize OutputId: {}", err)) - })?; - let alias_dto: AliasOutputDto = iter - .next() - .ok_or_else(|| identity_stardust::Error::JsError("get_alias_output expected a tuple of size 2".to_owned()))? - .into_serde() - .map_err(|err| { - identity_stardust::Error::JsError(format!( - "get_alias_output failed to deserialize AliasOutputDto: {}", - err - )) - })?; - let alias_output = AliasOutput::try_from(&alias_dto).map_err(|err| { - identity_stardust::Error::JsError(format!("get_alias_output failed to convert AliasOutputDto: {}", err)) - })?; - Ok((output_id, alias_output)) - } - - async fn get_rent_structure(&self) -> Result { - let promise: Promise = Promise::resolve(&WasmStardustIdentityClient::get_rent_structure(self)); - let result: JsValueResult = JsFuture::from(promise).await.into(); - let rent_structure: RentStructureBuilder = result - .to_stardust_error()? - .into_serde() - .map_err(|err| identity_stardust::Error::JsError(format!("get_rent_structure failed to deserialize: {}", err)))?; - Ok(rent_structure.finish()) - } -} - -#[wasm_bindgen(typescript_custom_section)] -const I_STARDUST_IDENTITY_CLIENT: &'static str = r#" -import type { IAliasOutput, IRent } from '@iota/types'; -/** Helper interface necessary for `StardustIdentityClientExt`. */ -interface IStardustIdentityClient { - /** - * Return the Bech32 human-readable part (HRP) of the network. - * - * E.g. "iota", "atoi", "smr", "rms". - */ - getNetworkHrp(): Promise; - - /** Resolve an Alias identifier, returning its latest `OutputId` and `AliasOutput`. */ - getAliasOutput(aliasId: string): Promise<[string, IAliasOutput]>; - - /** Return the rent structure of the network, indicating the byte costs for outputs. */ - getRentStructure(): Promise; -}"#; diff --git a/bindings/wasm/src/stardust/mod.rs b/bindings/wasm/src/stardust/mod.rs deleted file mode 100644 index 30e67d9fae..0000000000 --- a/bindings/wasm/src/stardust/mod.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 -pub(crate) use identity_client::WasmStardustIdentityClient; -pub use identity_client_ext::PromiseStardustDocument; -pub use stardust_did::WasmStardustDID; -pub use stardust_did_url::WasmStardustDIDUrl; -pub use stardust_document::WasmStardustDocument; -pub use stardust_document_metadata::WasmStardustDocumentMetadata; -pub use stardust_service::WasmStardustService; -pub use stardust_verification_method::WasmStardustVerificationMethod; -pub use state_metadata_encoding::WasmStateMetadataEncoding; - -mod identity_client; -mod identity_client_ext; -mod stardust_did; -mod stardust_did_url; -mod stardust_document; -mod stardust_document_metadata; -mod stardust_service; -mod stardust_verification_method; -mod state_metadata_encoding; diff --git a/bindings/wasm/src/stardust/stardust_did.rs b/bindings/wasm/src/stardust/stardust_did.rs deleted file mode 100644 index cab7839570..0000000000 --- a/bindings/wasm/src/stardust/stardust_did.rs +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use identity_iota::did::DIDError; -use identity_iota::did::DID; -use identity_stardust::NetworkName; -use identity_stardust::StardustDID; -use wasm_bindgen::prelude::*; - -use crate::error::Result; -use crate::error::WasmResult; -use crate::stardust::WasmStardustDIDUrl; - -/// A DID conforming to the IOTA UTXO DID method specification. -/// -/// @typicalname did -#[wasm_bindgen(js_name = StardustDID, inspectable)] -pub struct WasmStardustDID(pub(crate) StardustDID); - -#[wasm_bindgen(js_class = StardustDID)] -impl WasmStardustDID { - /// The IOTA UTXO DID method name (`"iota"`). - #[wasm_bindgen(getter = METHOD)] - pub fn static_method() -> String { - StardustDID::METHOD.to_owned() - } - - /// The default Tangle network (`"main"`). - #[wasm_bindgen(getter = DEFAULT_NETWORK)] - pub fn static_default_network() -> String { - StardustDID::DEFAULT_NETWORK.to_owned() - } - - // =========================================================================== - // Constructors - // =========================================================================== - - /// Constructs a new `StardustDID` from a byte representation of the tag and the given - /// network name. - /// - /// See also {@link StardustDID.placeholder}. - #[wasm_bindgen(constructor)] - pub fn new(bytes: &[u8], network: String) -> Result { - let network_name: NetworkName = NetworkName::try_from(network).wasm_result()?; - let tag_bytes: &[u8; 32] = bytes - .try_into() - .map_err(|_| DIDError::Other("invalid bytes length for StardustDID tag, expected 32")) - .wasm_result()?; - Ok(Self::from(StardustDID::new(tag_bytes, &network_name))) - } - - /// Creates a new placeholder [`StardustDID`] with the given network name. - /// - /// E.g. `did:iota:smr:0x0000000000000000000000000000000000000000000000000000000000000000`. - #[wasm_bindgen] - pub fn placeholder(network: String) -> Result { - let network_name: NetworkName = NetworkName::try_from(network).wasm_result()?; - Ok(Self::from(StardustDID::placeholder(&network_name))) - } - - /// Parses a `StardustDID` from the input string. - #[wasm_bindgen] - pub fn parse(input: &str) -> Result { - StardustDID::parse(input).map(Self).wasm_result() - } - - // =========================================================================== - // Properties - // =========================================================================== - - /// Returns the Tangle network name of the `StardustDID`. - #[wasm_bindgen(js_name = networkStr)] - pub fn network_str(&self) -> String { - self.0.network_str().to_owned() - } - - /// Returns a copy of the unique tag of the `StardustDID`. - #[wasm_bindgen] - pub fn tag(&self) -> String { - self.0.tag().to_owned() - } - - // =========================================================================== - // DID trait - // =========================================================================== - - /// Returns the `DID` scheme. - /// - /// E.g. - /// - `"did:example:12345678" -> "did"` - /// - `"did:iota:main:12345678" -> "did"` - #[wasm_bindgen] - pub fn scheme(&self) -> String { - self.0.scheme().to_owned() - } - - /// Returns the `DID` authority: the method name and method-id. - /// - /// E.g. - /// - `"did:example:12345678" -> "example:12345678"` - /// - `"did:iota:main:12345678" -> "iota:main:12345678"` - #[wasm_bindgen] - pub fn authority(&self) -> String { - self.0.authority().to_owned() - } - - /// Returns the `DID` method name. - /// - /// E.g. - /// - `"did:example:12345678" -> "example"` - /// - `"did:iota:main:12345678" -> "iota"` - #[wasm_bindgen] - pub fn method(&self) -> String { - self.0.method().to_owned() - } - - /// Returns the `DID` method-specific ID. - /// - /// E.g. - /// - `"did:example:12345678" -> "12345678"` - /// - `"did:iota:main:12345678" -> "main:12345678"` - #[wasm_bindgen(js_name = methodId)] - pub fn method_id(&self) -> String { - self.0.method_id().to_owned() - } - - /// Construct a new `DIDUrl` by joining with a relative DID Url string. - #[wasm_bindgen] - pub fn join(&self, segment: &str) -> Result { - self.0.clone().join(segment).wasm_result().map(WasmStardustDIDUrl) - } - - /// Clones the `DID` into a `DIDUrl`. - #[wasm_bindgen(js_name = toUrl)] - pub fn to_url(&self) -> WasmStardustDIDUrl { - WasmStardustDIDUrl::from(self.0.to_url()) - } - - /// Converts the `DID` into a `DIDUrl`, consuming it. - #[wasm_bindgen(js_name = intoUrl)] - pub fn into_url(self) -> WasmStardustDIDUrl { - WasmStardustDIDUrl::from(self.0.into_url()) - } - - /// Returns the `DID` as a string. - #[allow(clippy::inherent_to_string)] - #[wasm_bindgen(js_name = toString)] - pub fn to_string(&self) -> String { - self.0.to_string() - } -} - -impl_wasm_json!(WasmStardustDID, StardustDID); -impl_wasm_clone!(WasmStardustDID, StardustDID); - -impl From for WasmStardustDID { - fn from(did: StardustDID) -> Self { - Self(did) - } -} diff --git a/bindings/wasm/src/stardust/stardust_did_url.rs b/bindings/wasm/src/stardust/stardust_did_url.rs deleted file mode 100644 index e05ae5a111..0000000000 --- a/bindings/wasm/src/stardust/stardust_did_url.rs +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use identity_stardust::StardustDIDUrl; -use wasm_bindgen::prelude::*; - -use crate::error::Result; -use crate::error::WasmResult; -use crate::stardust::WasmStardustDID; - -/// A DID URL conforming to the IOTA Stardust UTXO DID method specification. -#[wasm_bindgen(js_name = StardustDIDUrl, inspectable)] -pub struct WasmStardustDIDUrl(pub(crate) StardustDIDUrl); - -#[wasm_bindgen(js_class = StardustDIDUrl)] -impl WasmStardustDIDUrl { - /// Parses a `StardustDIDUrl` from the input string. - #[wasm_bindgen] - pub fn parse(input: &str) -> Result { - StardustDIDUrl::parse(input).map(WasmStardustDIDUrl).wasm_result() - } - - /// Return a copy of the `StardustDID` section of the `StardustDIDUrl`. - #[wasm_bindgen] - pub fn did(&self) -> WasmStardustDID { - WasmStardustDID::from(self.0.did().clone()) - } - - /// Return a copy of the relative DID Url as a string, including only the path, query, and fragment. - #[wasm_bindgen(js_name = urlStr)] - pub fn url_str(&self) -> String { - self.0.url().to_string() - } - - /// Returns a copy of the `StardustDIDUrl` method fragment, if any. Excludes the leading '#'. - #[wasm_bindgen] - pub fn fragment(&self) -> Option { - self.0.fragment().map(str::to_owned) - } - - /// Sets the `fragment` component of the `StardustDIDUrl`. - #[wasm_bindgen(js_name = setFragment)] - pub fn set_fragment(&mut self, value: Option) -> Result<()> { - self.0.set_fragment(value.as_deref()).wasm_result() - } - - /// Returns a copy of the `StardustDIDUrl` path. - #[wasm_bindgen] - pub fn path(&self) -> Option { - self.0.path().map(str::to_owned) - } - - /// Sets the `path` component of the `StardustDIDUrl`. - #[wasm_bindgen(js_name = setPath)] - pub fn set_path(&mut self, value: Option) -> Result<()> { - self.0.set_path(value.as_deref()).wasm_result() - } - - /// Returns a copy of the `StardustDIDUrl` method query, if any. Excludes the leading '?'. - #[wasm_bindgen] - pub fn query(&self) -> Option { - self.0.query().map(str::to_owned) - } - - /// Sets the `query` component of the `StardustDIDUrl`. - #[wasm_bindgen(js_name = setQuery)] - pub fn set_query(&mut self, value: Option) -> Result<()> { - self.0.set_query(value.as_deref()).wasm_result() - } - - /// Append a string representing a path, query, and/or fragment, returning a new `StardustDIDUrl`. - /// - /// Must begin with a valid delimiter character: '/', '?', '#'. Overwrites the existing URL - /// segment and any following segments in order of path, query, then fragment. - /// - /// I.e. - /// - joining a path will clear the query and fragment. - /// - joining a query will clear the fragment. - /// - joining a fragment will only overwrite the fragment. - #[wasm_bindgen] - pub fn join(&self, segment: &str) -> Result { - self.0.join(segment).map(WasmStardustDIDUrl::from).wasm_result() - } - - /// Returns the `StardustDIDUrl` as a string. - #[allow(clippy::inherent_to_string)] - #[wasm_bindgen(js_name = toString)] - pub fn to_string(&self) -> String { - self.0.to_string() - } -} - -impl_wasm_json!(WasmStardustDIDUrl, StardustDIDUrl); -impl_wasm_clone!(WasmStardustDIDUrl, StardustDIDUrl); - -impl From for WasmStardustDIDUrl { - fn from(did_url: StardustDIDUrl) -> Self { - Self(did_url) - } -} - -impl From for StardustDIDUrl { - fn from(wasm_did_url: WasmStardustDIDUrl) -> Self { - wasm_did_url.0 - } -} diff --git a/bindings/wasm/src/tangle/client.rs b/bindings/wasm/src/tangle/client.rs deleted file mode 100644 index 1dac0d17ca..0000000000 --- a/bindings/wasm/src/tangle/client.rs +++ /dev/null @@ -1,258 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use core::str::FromStr; -use std::rc::Rc; - -use futures::executor; -use identity_iota::client::Client; -use identity_iota::client::ClientBuilder; -use identity_iota::client::ResolvedIotaDocument; -use identity_iota::client::TangleResolve; -use identity_iota::iota_core::DiffMessage; -use identity_iota::iota_core::IotaDID; -use identity_iota::iota_core::IotaDocument; -use identity_iota::iota_core::MessageId; -use js_sys::Promise; -use wasm_bindgen::prelude::*; -use wasm_bindgen::JsCast; -use wasm_bindgen_futures::future_to_promise; - -use crate::chain::DiffChainHistory; -use crate::chain::PromiseDiffChainHistory; -use crate::chain::PromiseDocumentHistory; -use crate::chain::WasmDocumentHistory; -use crate::common::PromiseBool; -use crate::did::PromiseResolvedDocument; -use crate::did::UWasmIotaDID; -use crate::did::WasmDiffMessage; -use crate::did::WasmDocument; -use crate::did::WasmResolvedDocument; -use crate::error::Result; -use crate::error::WasmResult; -use crate::tangle::IClientConfig; -use crate::tangle::PromiseReceipt; -use crate::tangle::WasmNetwork; -use crate::tangle::WasmReceipt; - -#[wasm_bindgen(js_name = Client)] -pub struct WasmClient { - pub(crate) client: Rc, -} - -#[wasm_bindgen(js_class = Client)] -impl WasmClient { - /// Creates a new `Client` with default settings. - #[wasm_bindgen(constructor)] - pub fn new() -> Result { - // TODO: is there a way to avoid blocking? Async constructors are not supported. - executor::block_on(Client::new()).map(Self::from).wasm_result() - } - - /// Creates a new `Client` with the given settings. - #[wasm_bindgen(js_name = fromConfig)] - pub fn from_config(config: IClientConfig) -> Result { - let builder: ClientBuilder = ClientBuilder::try_from(config)?; - let promise: Promise = - future_to_promise(async move { builder.build().await.map(Self::from).map(Into::into).wasm_result() }); - - // WARNING: this does not validate the return type. Check carefully. - Ok(promise.unchecked_into::()) - } - - /// Returns the `Client` Tangle network. - #[wasm_bindgen] - pub fn network(&self) -> WasmNetwork { - self.client.network().into() - } - - /// Publishes a {@link Document} to the Tangle. - #[wasm_bindgen(js_name = publishDocument)] - pub fn publish_document(&self, document: &WasmDocument) -> Result { - let document: IotaDocument = document.0.clone(); - let client: Rc = self.client.clone(); - - let promise: Promise = future_to_promise(async move { - client - .publish_document(&document) - .await - .map(WasmReceipt) - .map(Into::into) - .wasm_result() - }); - - // WARNING: this does not validate the return type. Check carefully. - Ok(promise.unchecked_into::()) - } - - /// Publishes a `DiffMessage` to the Tangle. - /// - /// @deprecated since 0.5.0, diff chain features are slated for removal. - #[wasm_bindgen(js_name = publishDiff)] - pub fn publish_diff(&self, message_id: &str, diff: &WasmDiffMessage) -> Result { - let message: MessageId = MessageId::from_str(message_id) - .map_err(identity_iota::iota_core::Error::InvalidMessage) - .wasm_result()?; - let diff: DiffMessage = diff.0.clone(); - let client: Rc = self.client.clone(); - - let promise: Promise = future_to_promise(async move { - client - .publish_diff(&message, &diff) - .await - .map(WasmReceipt) - .map(Into::into) - .wasm_result() - }); - - // WARNING: this does not validate the return type. Check carefully. - Ok(promise.unchecked_into::()) - } - - /// Publishes arbitrary JSON data to the specified index on the Tangle. - #[wasm_bindgen(js_name = publishJSON)] - pub fn publish_json(&self, index: String, data: &JsValue) -> Result { - let value: serde_json::Value = data.into_serde().wasm_result()?; - - let client: Rc = self.client.clone(); - let promise: Promise = future_to_promise(async move { - client - .publish_json(&index, &value) - .await - .map(WasmReceipt) - .map(Into::into) - .wasm_result() - }); - - // WARNING: this does not validate the return type. Check carefully. - Ok(promise.unchecked_into::()) - } - - /// Publishes arbitrary JSON data to the specified index on the Tangle. - /// Retries (promotes or reattaches) the message until it’s included (referenced by a milestone). - /// Default interval is 5 seconds and max attempts is 40. - #[wasm_bindgen(js_name = publishJsonWithRetry)] - pub fn publish_json_with_retry( - &self, - index: String, - data: &JsValue, - interval: Option, - max_attempts: Option, - ) -> Result { - let value: serde_json::Value = data.into_serde().wasm_result()?; - - let client: Rc = self.client.clone(); - let promise: Promise = future_to_promise(async move { - client - .publish_json_with_retry( - &index, - &value, - interval.map(|interval| interval as u64), - max_attempts.map(|max_attempts| max_attempts as u64), - ) - .await - .wasm_result() - .and_then(|receipt| JsValue::from_serde(&receipt).wasm_result()) - }); - - Ok(promise) - } - - /// Checks if a message is confirmed by a milestone. - #[wasm_bindgen(js_name = isMessageIncluded)] - #[allow(non_snake_case)] - pub fn is_message_included(&self, messageId: &str) -> Result { - let message: MessageId = MessageId::from_str(messageId) - .map_err(identity_iota::iota_core::Error::InvalidMessage) - .wasm_result()?; - - let client: Rc = self.client.clone(); - let promise: Promise = - future_to_promise(async move { client.is_message_included(&message).await.map(Into::into).wasm_result() }); - // WARNING: this does not validate the return type. Check carefully. - Ok(promise.unchecked_into::()) - } - - /// Fetch the DID document specified by the given `DID`. - #[wasm_bindgen] - pub fn resolve(&self, did: UWasmIotaDID) -> Result { - let did: IotaDID = IotaDID::try_from(did)?; - - let client: Rc = self.client.clone(); - let promise: Promise = future_to_promise(async move { - client - .resolve(&did) - .await - .map(WasmResolvedDocument::from) - .map(Into::into) - .wasm_result() - }); - - // WARNING: this does not validate the return type. Check carefully. - Ok(promise.unchecked_into::()) - } - - /// Returns the message history of the given DID. - #[wasm_bindgen(js_name = resolveHistory)] - pub fn resolve_history(&self, did: UWasmIotaDID) -> Result { - let did: IotaDID = IotaDID::try_from(did)?; - - let client: Rc = self.client.clone(); - let promise: Promise = future_to_promise(async move { - client - .resolve_history(&did) - .await - .map(WasmDocumentHistory::from) - .map(Into::into) - .wasm_result() - }); - - // WARNING: this does not validate the return type. Check carefully. - Ok(promise.unchecked_into::()) - } - - /// Returns the `DiffChainHistory` of a diff chain starting from a document on the - /// integration chain. - /// - /// NOTE: the document must have been published to the tangle and have a valid message id and - /// capability invocation method. - /// - /// @deprecated since 0.5.0, diff chain features are slated for removal. - #[wasm_bindgen(js_name = resolveDiffHistory)] - pub fn resolve_diff_history(&self, document: &WasmResolvedDocument) -> Result { - let resolved_document: ResolvedIotaDocument = document.0.clone(); - - let client: Rc = self.client.clone(); - let promise: Promise = future_to_promise(async move { - client - .resolve_diff_history(&resolved_document) - .await - .map(DiffChainHistory::from) - .map(Into::into) - .wasm_result() - }); - - // WARNING: this does not validate the return type. Check carefully. - Ok(promise.unchecked_into::()) - } -} - -impl From> for WasmClient { - fn from(client: Rc) -> Self { - Self { client } - } -} - -impl From for WasmClient { - fn from(client: Client) -> Self { - Self { - client: Rc::new(client), - } - } -} - -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(typescript_type = "Promise")] - pub type PromiseClient; -} diff --git a/bindings/wasm/src/tangle/client_config.rs b/bindings/wasm/src/tangle/client_config.rs deleted file mode 100644 index ebb5d17307..0000000000 --- a/bindings/wasm/src/tangle/client_config.rs +++ /dev/null @@ -1,365 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::time::Duration; - -use identity_iota::client::ClientBuilder; -use identity_iota::client::DIDMessageEncoding; -use identity_iota::iota_core::Network; -use proc_typescript::typescript; -use wasm_bindgen::prelude::*; - -use crate::error::WasmResult; -use crate::tangle::WasmDIDMessageEncoding; - -/// Try construct a `ClientBuilder` directly from an `IClientConfig` interface. -impl TryFrom for ClientBuilder { - type Error = JsValue; - - fn try_from(config: IClientConfig) -> std::result::Result { - let ClientConfig { - network, - encoding, - nodes, - primary_node, - primary_pow_node, - permanodes, - node_auth, - node_sync_interval, - node_sync_disabled, - quorum, - quorum_size, - quorum_threshold, - local_pow, - fallback_to_local_pow, - tips_interval, - request_timeout, - retry_until_included, - } = config.into_serde::().wasm_result()?; - - let mut builder: ClientBuilder = ClientBuilder::new(); - if let Some(network) = network { - builder = builder.network(network); - } - if let Some(encoding) = encoding { - builder = builder.encoding(DIDMessageEncoding::from(encoding)); - } - if let Some(nodes) = nodes { - builder = builder - .nodes(&nodes.iter().map(AsRef::as_ref).collect::>()) - .wasm_result()?; - } - if let Some(NodeAuth { - url, - jwt, - username, - password, - }) = primary_node - { - builder = builder - .primary_node(&url, jwt, basic_auth(&username, &password)) - .wasm_result()?; - } - if let Some(NodeAuth { - url, - jwt, - username, - password, - }) = primary_pow_node - { - builder = builder - .primary_pow_node(&url, jwt, basic_auth(&username, &password)) - .wasm_result()?; - } - for NodeAuth { - url, - jwt, - username, - password, - } in permanodes.unwrap_or_default() - { - builder = builder - .permanode(&url, jwt, basic_auth(&username, &password)) - .wasm_result()?; - } - for NodeAuth { - url, - jwt, - username, - password, - } in node_auth.unwrap_or_default() - { - builder = builder - .node_auth(&url, jwt, basic_auth(&username, &password)) - .wasm_result()?; - } - if let Some(node_sync_interval) = node_sync_interval { - builder = builder.node_sync_interval(Duration::from_secs(u64::from(node_sync_interval))); - } - if let Some(node_sync_disabled) = node_sync_disabled { - if node_sync_disabled { - builder = builder.node_sync_disabled(); - } - } - if let Some(quorum) = quorum { - builder = builder.quorum(quorum); - } - if let Some(quorum_size) = quorum_size { - builder = builder.quorum_size(quorum_size); - } - if let Some(quorum_threshold) = quorum_threshold { - builder = builder.quorum_threshold(quorum_threshold); - } - if let Some(local_pow) = local_pow { - builder = builder.local_pow(local_pow); - } - if let Some(fallback_to_local_pow) = fallback_to_local_pow { - builder = builder.fallback_to_local_pow(fallback_to_local_pow); - } - if let Some(tips_interval) = tips_interval { - builder = builder.tips_interval(u64::from(tips_interval)); - } - if let Some(request_timeout) = request_timeout { - builder = builder.request_timeout(Duration::from_secs(u64::from(request_timeout))); - } - if let Some(retry_until_included) = retry_until_included { - builder = builder.retry_until_included(retry_until_included); - } - - Ok(builder) - } -} - -/// Helper function to combine a username and password into a basic authentication tuple. -fn basic_auth<'a>(username: &'a Option, password: &'a Option) -> Option<(&'a str, &'a str)> { - username.as_deref().zip(password.as_deref()) -} - -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(typescript_type = "IClientConfig")] - pub type IClientConfig; -} - -/// IOTA node details with optional authentication. -#[derive(Debug, PartialEq, Eq, Deserialize)] -#[typescript(name = "INodeAuth", readonly, optional)] -struct NodeAuth { - #[typescript(optional = false, type = "string")] - url: String, - #[typescript(type = "string")] - jwt: Option, - #[typescript(type = "string")] - username: Option, - #[typescript(type = "string")] - password: Option, -} - -/// {@link Client} configuration options. -#[derive(Default, Deserialize)] -#[serde(rename_all = "camelCase")] -#[typescript(name = "IClientConfig", readonly, optional)] -struct ClientConfig { - /// Sets the IOTA Tangle network. - #[typescript(type = "Network")] - network: Option, - /// Sets the DID message encoding used when publishing to the Tangle. - #[typescript(type = "DIDMessageEncoding")] - encoding: Option, - /// Adds a list of IOTA nodes to use by their URLs. - #[typescript(type = "Array")] - nodes: Option>, - /// Sets an IOTA node by its URL to be used as primary node. - #[typescript(name = "primaryNode", type = "INodeAuth")] - primary_node: Option, - /// Adds an IOTA node by its URL to be used as primary PoW node (for remote PoW). - #[typescript(name = "primaryPowNode", type = "INodeAuth")] - primary_pow_node: Option, - /// Adds a list of IOTA permanodes by their URLs. - #[typescript(type = "Array")] - permanodes: Option>, - /// Adds a list of IOTA nodes to be used by their URLs. - #[typescript(name = "nodeAuth", type = "Array")] - node_auth: Option>, - /// Sets the node sync interval in seconds. - #[typescript(name = "nodeSyncInterval", type = "number")] - node_sync_interval: Option, - /// Disables the node sync process. - #[typescript(name = "nodeSyncDisabled", type = "boolean")] - node_sync_disabled: Option, - /// Enables/disables quorum. - #[typescript(type = "boolean")] - quorum: Option, - /// Sets the number of nodes used for quorum. - #[typescript(name = "quorumSize", type = "number")] - quorum_size: Option, - /// Sets the quorum threshold. - #[typescript(name = "quorumThreshold", type = "number")] - quorum_threshold: Option, - /// Sets whether proof-of-work (PoW) is performed locally or remotely. - /// Default: false. - #[typescript(name = "localPow", type = "boolean")] - local_pow: Option, - /// Sets whether the PoW should be done locally in case a node doesn't support remote PoW. - /// Default: true. - #[typescript(name = "fallbackToLocalPow", type = "boolean")] - fallback_to_local_pow: Option, - /// Sets the number of seconds that new tips will be requested during PoW. - #[typescript(name = "tipsInterval", type = "number")] - tips_interval: Option, - /// Sets the default request timeout. - #[typescript(name = "requestTimeout", type = "number")] - request_timeout: Option, - /// When publishing to the Tangle, sets whether to retry until the message is confirmed by a milestone. - /// Default: true. - #[typescript(name = "retryUntilIncluded", type = "boolean")] - retry_until_included: Option, -} - -#[cfg(test)] -mod tests { - use identity_iota::core::FromJson; - use identity_iota::core::Object; - use wasm_bindgen::JsCast; - use wasm_bindgen_test::*; - - use super::*; - - fn mock_client_config_json() -> JsValue { - JsValue::from_serde( - &Object::from_json( - r#"{ - "network": "dev", - "encoding": 1, - "nodes": ["https://example.com:1", "https://example.com:2"], - "primaryNode": { - "url": "https://example.com:3", - "username": "user", - "password": "pass" - }, - "primaryPowNode": { - "url": "https://example.com:4" - }, - "permanodes": [{ "url": "https://example.com:5" }, { "url": "https://example.com:6" }], - "nodeAuth": [{ "url": "https://example.com:7" }, { "url": "https://example.com:8" }], - "nodeSyncInterval": 42, - "nodeSyncDisabled": true, - "quorum": true, - "quorumSize": 3, - "quorumThreshold": 2, - "localPow": false, - "fallbackToLocalPow": false, - "tipsInterval": 7, - "requestTimeout": 60, - "retryUntilIncluded": false - }"#, - ) - .unwrap(), - ) - .unwrap() - } - - #[wasm_bindgen_test] - fn test_client_config_try_from() { - let json: JsValue = mock_client_config_json(); - let _client_builder: ClientBuilder = ClientBuilder::try_from(json.unchecked_into::()).unwrap(); - } - - #[wasm_bindgen_test] - fn test_client_config_serde() { - let json: JsValue = mock_client_config_json(); - let ClientConfig { - network, - encoding, - nodes, - primary_node, - primary_pow_node, - permanodes, - node_auth, - node_sync_interval, - node_sync_disabled, - quorum, - quorum_size, - quorum_threshold, - local_pow, - fallback_to_local_pow, - tips_interval, - request_timeout, - retry_until_included, - } = json.into_serde::().unwrap(); - assert_eq!(network, Some(Network::Devnet)); - assert_eq!( - encoding.map(DIDMessageEncoding::from), - Some(DIDMessageEncoding::JsonBrotli) - ); - assert_eq!( - nodes, - Some(vec![ - "https://example.com:1".to_owned(), - "https://example.com:2".to_owned(), - ]) - ); - assert_eq!( - primary_node, - Some(NodeAuth { - url: "https://example.com:3".to_owned(), - jwt: None, - username: Some("user".to_owned()), - password: Some("pass".to_owned()), - }) - ); - assert_eq!( - primary_pow_node, - Some(NodeAuth { - url: "https://example.com:4".to_owned(), - jwt: None, - username: None, - password: None, - }) - ); - assert_eq!( - permanodes, - Some(vec![ - NodeAuth { - url: "https://example.com:5".to_owned(), - jwt: None, - username: None, - password: None, - }, - NodeAuth { - url: "https://example.com:6".to_owned(), - jwt: None, - username: None, - password: None, - }, - ]) - ); - assert_eq!( - node_auth, - Some(vec![ - NodeAuth { - url: "https://example.com:7".to_owned(), - jwt: None, - username: None, - password: None, - }, - NodeAuth { - url: "https://example.com:8".to_owned(), - jwt: None, - username: None, - password: None, - }, - ]) - ); - assert_eq!(node_sync_interval, Some(42)); - assert_eq!(node_sync_disabled, Some(true)); - assert_eq!(quorum, Some(true)); - assert_eq!(quorum_size, Some(3)); - assert_eq!(quorum_threshold, Some(2)); - assert_eq!(local_pow, Some(false)); - assert_eq!(fallback_to_local_pow, Some(false)); - assert_eq!(tips_interval, Some(7)); - assert_eq!(request_timeout, Some(60)); - assert_eq!(retry_until_included, Some(false)); - } -} diff --git a/bindings/wasm/src/tangle/explorer.rs b/bindings/wasm/src/tangle/explorer.rs deleted file mode 100644 index 3885372a7e..0000000000 --- a/bindings/wasm/src/tangle/explorer.rs +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::str::FromStr; - -use identity_iota::client::ExplorerUrl; -use identity_iota::iota_core::IotaDID; -use identity_iota::iota_core::MessageId; -use wasm_bindgen::prelude::*; - -use crate::did::UWasmIotaDID; -use crate::error::Result; -use crate::error::WasmResult; - -#[wasm_bindgen(js_name = ExplorerUrl)] -pub struct WasmExplorerUrl(ExplorerUrl); - -#[wasm_bindgen(js_class = ExplorerUrl)] -impl WasmExplorerUrl { - /// Constructs a new Tangle explorer URL from a string. - /// - /// Use `ExplorerUrl::mainnet` or `ExplorerUrl::devnet` unless using a private Tangle - /// or local explorer. - #[wasm_bindgen] - pub fn parse(url: &str) -> Result { - ExplorerUrl::parse(url).map(WasmExplorerUrl).wasm_result() - } - - /// Returns the Tangle explorer URL for the mainnet. - #[wasm_bindgen] - pub fn mainnet() -> WasmExplorerUrl { - Self(ExplorerUrl::mainnet().clone()) - } - - /// Returns the Tangle explorer URL for the devnet. - #[wasm_bindgen] - pub fn devnet() -> WasmExplorerUrl { - Self(ExplorerUrl::devnet().clone()) - } - - /// Returns the web explorer URL of the given `message_id`. - /// - /// E.g. https://explorer.iota.org/mainnet/message/{message_id} - #[wasm_bindgen(js_name = messageUrl)] - pub fn message_url(&self, message_id: &str) -> Result { - let message_id = MessageId::from_str(message_id) - .map_err(identity_iota::iota_core::Error::InvalidMessage) - .wasm_result()?; - self.0.message_url(&message_id).map(|url| url.to_string()).wasm_result() - } - - /// Returns the web identity resolver URL for the given DID. - /// - /// E.g. https://explorer.iota.org/mainnet/identity-resolver/{did} - #[wasm_bindgen(js_name = resolverUrl)] - pub fn resolver_url(&self, did: UWasmIotaDID) -> Result { - let did: IotaDID = IotaDID::try_from(did)?; - self.0.resolver_url(&did).map(|url| url.to_string()).wasm_result() - } - - #[allow(clippy::inherent_to_string, clippy::wrong_self_convention)] - #[wasm_bindgen(js_name = toString)] - pub fn to_string(&self) -> String { - self.0.to_string() - } -} - -impl_wasm_json!(WasmExplorerUrl, ExplorerUrl); - -impl From for ExplorerUrl { - fn from(other: WasmExplorerUrl) -> Self { - other.0 - } -} - -impl From for WasmExplorerUrl { - fn from(other: ExplorerUrl) -> Self { - Self(other) - } -} diff --git a/bindings/wasm/src/tangle/message.rs b/bindings/wasm/src/tangle/message.rs deleted file mode 100644 index d2ff3a3627..0000000000 --- a/bindings/wasm/src/tangle/message.rs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use identity_iota::client::DIDMessageEncoding; -use serde_repr::Deserialize_repr; -use serde_repr::Serialize_repr; -use wasm_bindgen::prelude::*; - -#[wasm_bindgen(js_name = DIDMessageEncoding)] -#[derive(Serialize_repr, Deserialize_repr)] -#[repr(u8)] -pub enum WasmDIDMessageEncoding { - Json = 0, - JsonBrotli = 1, -} - -impl From for WasmDIDMessageEncoding { - fn from(encoding: DIDMessageEncoding) -> Self { - match encoding { - DIDMessageEncoding::Json => Self::Json, - DIDMessageEncoding::JsonBrotli => Self::JsonBrotli, - } - } -} - -impl From for DIDMessageEncoding { - fn from(encoding: WasmDIDMessageEncoding) -> Self { - match encoding { - WasmDIDMessageEncoding::Json => Self::Json, - WasmDIDMessageEncoding::JsonBrotli => Self::JsonBrotli, - } - } -} diff --git a/bindings/wasm/src/tangle/mod.rs b/bindings/wasm/src/tangle/mod.rs deleted file mode 100644 index cf7ffb3f3a..0000000000 --- a/bindings/wasm/src/tangle/mod.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -pub use self::client::*; -pub use self::client_config::*; -pub use self::explorer::*; -pub use self::message::*; -pub use self::network::*; -pub use self::receipt::*; -pub use self::resolver::*; - -mod client; -mod client_config; -mod explorer; -mod message; -mod network; -mod receipt; -mod resolver; diff --git a/bindings/wasm/src/tangle/network.rs b/bindings/wasm/src/tangle/network.rs deleted file mode 100644 index fc88b166b4..0000000000 --- a/bindings/wasm/src/tangle/network.rs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use identity_iota::iota_core::Network; -use wasm_bindgen::prelude::*; - -use crate::error::Result; -use crate::error::WasmResult; - -#[wasm_bindgen(js_name = Network)] -pub struct WasmNetwork(pub(crate) Network); - -#[wasm_bindgen(js_class = Network)] -impl WasmNetwork { - /// Parses the provided string to a `Network`. - /// - /// Errors if the name is invalid. - #[wasm_bindgen(js_name = tryFromName)] - pub fn try_from_name(name: String) -> Result { - Network::try_from_name(name).map(Self).wasm_result() - } - - #[wasm_bindgen] - pub fn mainnet() -> WasmNetwork { - Self(Network::Mainnet) - } - - #[wasm_bindgen] - pub fn devnet() -> WasmNetwork { - Self(Network::Devnet) - } - - /// Returns a copy of the network name. - #[wasm_bindgen] - pub fn name(&self) -> String { - self.0.name_str().to_owned() - } - - /// Returns a copy of the node URL of the Tangle network. - #[wasm_bindgen(js_name = defaultNodeURL)] - pub fn default_node_url(&self) -> Option { - self.0.default_node_url().map(ToString::to_string) - } - - #[allow(clippy::inherent_to_string, clippy::wrong_self_convention)] - #[wasm_bindgen(js_name = toString)] - pub fn to_string(&self) -> String { - self.0.name_str().to_owned() - } -} - -impl_wasm_json!(WasmNetwork, Network); -impl_wasm_clone!(WasmNetwork, Network); - -impl From for Network { - fn from(other: WasmNetwork) -> Self { - other.0 - } -} - -impl From for WasmNetwork { - fn from(other: Network) -> Self { - Self(other) - } -} diff --git a/bindings/wasm/src/tangle/receipt.rs b/bindings/wasm/src/tangle/receipt.rs deleted file mode 100644 index b600219689..0000000000 --- a/bindings/wasm/src/tangle/receipt.rs +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use identity_iota::client::Receipt; -use wasm_bindgen::prelude::*; - -use crate::tangle::WasmNetwork; - -#[wasm_bindgen(js_name = Receipt, inspectable)] -pub struct WasmReceipt(pub(crate) Receipt); - -// Workaround for Typescript type annotations on async function returns. -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(typescript_type = "Promise")] - pub type PromiseReceipt; -} - -#[wasm_bindgen(js_class = Receipt)] -impl WasmReceipt { - /// Returns a copy of the associated IOTA Tangle `Network`. - #[wasm_bindgen] - pub fn network(&self) -> WasmNetwork { - WasmNetwork::from(self.0.network()) - } - - /// Returns a copy of the message `id`. - #[wasm_bindgen(js_name = messageId)] - pub fn message_id(&self) -> String { - self.0.message_id().to_string() - } - - /// Returns a copy of the message `network_id`. - #[wasm_bindgen(js_name = networkId)] - pub fn network_id(&self) -> String { - // NOTE: do not return u64 to avoid BigInt64Array/BigUint64Array compatibility issues. - self.0.network_id().to_string() - } - - /// Returns a copy of the message `nonce`. - #[wasm_bindgen] - pub fn nonce(&self) -> String { - // NOTE: do not return u64 to avoid BigInt64Array/BigUint64Array compatibility issues. - self.0.nonce().to_string() - } -} - -impl_wasm_json!(WasmReceipt, Receipt); -impl_wasm_clone!(WasmReceipt, Receipt); - -impl From for WasmReceipt { - fn from(receipt: Receipt) -> Self { - Self(receipt) - } -} - -impl From for Receipt { - fn from(receipt: WasmReceipt) -> Self { - receipt.0 - } -} diff --git a/bindings/wasm/src/tangle/resolver.rs b/bindings/wasm/src/tangle/resolver.rs deleted file mode 100644 index 76a4844fb6..0000000000 --- a/bindings/wasm/src/tangle/resolver.rs +++ /dev/null @@ -1,350 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::collections::HashSet; -use std::rc::Rc; - -use futures::executor; -use identity_iota::client::Client; -use identity_iota::client::ClientBuilder; -use identity_iota::client::ResolvedIotaDocument; -use identity_iota::client::Resolver; -use identity_iota::client::ResolverBuilder; -use identity_iota::credential::Presentation; -use identity_iota::credential::PresentationValidationOptions; -use identity_iota::credential::PresentationValidator; -use identity_iota::credential::ValidatorDocument; -use identity_iota::iota_core::IotaDID; -use identity_iota::iota_core::IotaDocument; -use identity_iota::iota_core::NetworkName; -use js_sys::Promise; -use wasm_bindgen::prelude::*; -use wasm_bindgen::JsCast; -use wasm_bindgen_futures::future_to_promise; - -use crate::chain::DiffChainHistory; -use crate::chain::PromiseDiffChainHistory; -use crate::chain::PromiseDocumentHistory; -use crate::chain::WasmDocumentHistory; -use crate::common::PromiseVoid; -use crate::credential::WasmCredential; -use crate::credential::WasmFailFast; -use crate::credential::WasmPresentation; -use crate::credential::WasmPresentationValidationOptions; -use crate::did::ArrayDocumentOrResolvedDocument; -use crate::did::DocumentOrResolvedDocument; -use crate::did::PromiseArrayResolvedDocument; -use crate::did::PromiseResolvedDocument; -use crate::did::UWasmIotaDID; -use crate::did::WasmResolvedDocument; -use crate::error::Result; -use crate::error::WasmResult; -use crate::tangle::IClientConfig; -use crate::tangle::WasmClient; - -#[wasm_bindgen(js_name = Resolver)] -pub struct WasmResolver(pub(crate) Rc>>); - -#[wasm_bindgen] -extern "C" { - // Workaround for Typescript type annotations on async function returns. - #[wasm_bindgen(typescript_type = "Promise")] - pub type PromiseResolver; -} - -#[wasm_bindgen(js_class = Resolver)] -impl WasmResolver { - /// Constructs a new `Resolver` with a default `Client` for - /// the `Mainnet`. - #[wasm_bindgen(constructor)] - pub fn new() -> Result { - // TODO: any way to avoid blocking? WasmClient does this too... - executor::block_on(Resolver::>::new()) - .map(Self::from) - .wasm_result() - } - - /// Returns a {@link ResolverBuilder} to construct a new `Resolver`. - #[wasm_bindgen] - pub fn builder() -> WasmResolverBuilder { - WasmResolverBuilder::new() - } - - /// Returns the `Client` corresponding to the given network name if one exists. - #[wasm_bindgen(js_name = getClient)] - pub fn get_client(&self, network_name: String) -> Option { - let network_name: NetworkName = NetworkName::try_from(network_name).ok()?; - self.0.get_client(&network_name).cloned().map(WasmClient::from) - } - - /// Fetches the `Document` of the given `DID`. - #[wasm_bindgen] - pub fn resolve(&self, did: UWasmIotaDID) -> Result { - let did: IotaDID = IotaDID::try_from(did)?; - - let resolver: Rc>> = Rc::clone(&self.0); - let promise: Promise = future_to_promise(async move { - resolver - .resolve(&did) - .await - .map(WasmResolvedDocument::from) - .map(Into::into) - .wasm_result() - }); - - // WARNING: this does not validate the return type. Check carefully. - Ok(promise.unchecked_into::()) - } - - /// Fetches the `DocumentHistory` of the given `DID`. - #[wasm_bindgen(js_name = resolveHistory)] - pub fn resolve_history(&self, did: UWasmIotaDID) -> Result { - let did: IotaDID = IotaDID::try_from(did)?; - - let resolver: Rc>> = Rc::clone(&self.0); - let promise: Promise = future_to_promise(async move { - resolver - .resolve_history(&did) - .await - .map(WasmDocumentHistory::from) - .map(Into::into) - .wasm_result() - }); - - // WARNING: this does not validate the return type. Check carefully. - Ok(promise.unchecked_into::()) - } - - /// Returns the `DiffChainHistory` of a diff chain starting from a `Document` on the - /// integration chain. - /// - /// NOTE: the document must have been published to the Tangle and have a valid message id. - /// - /// @deprecated since 0.5.0, diff chain features are slated for removal. - #[wasm_bindgen(js_name = resolveDiffHistory)] - pub fn resolve_diff_history(&self, document: &WasmResolvedDocument) -> Result { - let resolved_document: ResolvedIotaDocument = document.0.clone(); - - let resolver: Rc>> = Rc::clone(&self.0); - let promise: Promise = future_to_promise(async move { - resolver - .resolve_diff_history(&resolved_document) - .await - .map(DiffChainHistory::from) - .map(Into::into) - .wasm_result() - }); - - // WARNING: this does not validate the return type. Check carefully. - Ok(promise.unchecked_into::()) - } - - /// Fetches the DID Document of the issuer on a `Credential`. - /// - /// ### Errors - /// - /// Errors if the issuer URL is not a valid `DID` or document resolution fails. - #[wasm_bindgen(js_name = resolveCredentialIssuer)] - pub fn resolve_credential_issuer(&self, credential: &WasmCredential) -> Result { - // TODO: reimplemented function to avoid cloning the entire credential. - // Would be solved with Rc internal representation, pending memory leak discussions. - let issuer: IotaDID = IotaDID::parse(credential.0.issuer.url().as_str()).wasm_result()?; - - let resolver: Rc>> = Rc::clone(&self.0); - let promise: Promise = future_to_promise(async move { - resolver - .resolve(&issuer) - .await - .map(WasmResolvedDocument::from) - .map(Into::into) - .wasm_result() - }); - - // WARNING: this does not validate the return type. Check carefully. - Ok(promise.unchecked_into::()) - } - - /// Fetches all DID Documents of `Credential` issuers contained in a `Presentation`. - /// Issuer documents are returned in arbitrary order. - /// - /// ### Errors - /// - /// Errors if any issuer URL is not a valid `DID` or document resolution fails. - #[wasm_bindgen(js_name = resolvePresentationIssuers)] - pub fn resolve_presentation_issuers(&self, presentation: &WasmPresentation) -> Result { - // TODO: reimplemented function to avoid cloning the entire presentation. - // Would be solved with Rc internal representation, pending memory leak discussions. - - // Extract unique issuers. - let issuers: HashSet = presentation - .0 - .verifiable_credential - .iter() - .map(|credential| IotaDID::parse(credential.issuer.url().as_str()).wasm_result()) - .collect::>()?; - - let resolver: Rc>> = Rc::clone(&self.0); - let promise: Promise = future_to_promise(async move { - // Resolve issuers concurrently. - Ok( - futures::future::try_join_all( - issuers - .iter() - .map(|issuer| resolver.resolve(issuer)) - .collect::>(), - ) - .await - .wasm_result()? - .into_iter() - .map(WasmResolvedDocument::from) - .map(JsValue::from) - .collect::() - .into(), - ) - }); - - // WARNING: this does not validate the return type. Check carefully. - Ok(promise.unchecked_into::()) - } - - /// Fetches the DID Document of the holder of a `Presentation`. - /// - /// ### Errors - /// - /// Errors if the holder URL is missing, is not a valid `DID`, or document resolution fails. - #[wasm_bindgen(js_name = resolvePresentationHolder)] - pub fn resolve_presentation_holder(&self, presentation: &WasmPresentation) -> Result { - // TODO: reimplemented function to avoid cloning the entire presentation. - // Would be solved with Rc internal representation, pending memory leak discussions. - let holder: IotaDID = PresentationValidator::extract_holder(&presentation.0).wasm_result()?; - - let resolver: Rc>> = Rc::clone(&self.0); - let promise: Promise = future_to_promise(async move { - resolver - .resolve(&holder) - .await - .map(WasmResolvedDocument::from) - .map(Into::into) - .wasm_result() - }); - - // WARNING: this does not validate the return type. Check carefully. - Ok(promise.unchecked_into::()) - } - - /// Verifies a `Presentation`. - /// - /// ### Important - /// See `PresentationValidator::validate` for information about which properties get - /// validated and what is expected of the optional arguments `holder` and `issuer`. - /// - /// ### Resolution - /// The DID Documents for the `holder` and `issuers` are optionally resolved if not given. - /// If you already have up-to-date versions of these DID Documents, you may want - /// to use `PresentationValidator::validate`. - /// See also `Resolver::resolvePresentationIssuers` and `Resolver::resolvePresentationHolder`. - /// - /// ### Errors - /// Errors from resolving the holder and issuer DID Documents, if not provided, will be returned immediately. - /// Otherwise, errors from validating the presentation and its credentials will be returned - /// according to the `fail_fast` parameter. - #[wasm_bindgen(js_name = verifyPresentation)] - pub fn verify_presentation( - &self, - presentation: &WasmPresentation, - options: &WasmPresentationValidationOptions, - fail_fast: WasmFailFast, - holder: Option, - issuers: Option, - ) -> Result { - // TODO: reimplemented function to avoid cloning the entire presentation and validation options. - // Would be solved with Rc internal representation, pending memory leak discussions. - let holder: Option = holder.map(|js| js.into_serde().wasm_result()).transpose()?; - let issuers: Option> = issuers.map(|js| js.into_serde().wasm_result()).transpose()?; - - let resolver: Rc>> = Rc::clone(&self.0); - let presentation: Presentation = presentation.0.clone(); - let options: PresentationValidationOptions = options.0.clone(); - - let promise: Promise = future_to_promise(async move { - let issuer_refs: Option> = issuers - .as_ref() - .map(|issuers| issuers.iter().map(ValidatorDocument::as_validator).collect()); - resolver - .verify_presentation( - &presentation, - &options, - fail_fast.into(), - holder.as_ref().map(ValidatorDocument::as_validator), - issuer_refs.as_deref(), - ) - .await - .map(|_| JsValue::UNDEFINED) - .wasm_result() - }); - - // WARNING: this does not validate the return type. Check carefully. - Ok(promise.unchecked_into::()) - } -} - -impl From>> for WasmResolver { - fn from(resolver: Resolver>) -> Self { - WasmResolver(Rc::new(resolver)) - } -} - -/// Builder for configuring [`Clients`][Client] when constructing a [`Resolver`]. -#[wasm_bindgen(js_name = ResolverBuilder)] -pub struct WasmResolverBuilder(ResolverBuilder>); - -#[allow(clippy::new_without_default)] -#[wasm_bindgen(js_class = ResolverBuilder)] -impl WasmResolverBuilder { - /// Constructs a new `ResolverBuilder` with no `Clients` configured. - #[wasm_bindgen(constructor)] - pub fn new() -> WasmResolverBuilder { - WasmResolverBuilder(ResolverBuilder::>::new()) - } - - /// Inserts a `Client`. - /// - /// NOTE: replaces any previous `Client` or `Config` with the same network name. - #[wasm_bindgen] - #[must_use] - pub fn client(mut self, client: &WasmClient) -> WasmResolverBuilder { - self.0 = self.0.client(Rc::clone(&client.client)); - self - } - - /// Inserts a `Config` used to create a `Client`. - /// - /// NOTE: replaces any previous `Client` or `Config` with the same network name. - #[wasm_bindgen(js_name = clientConfig)] - pub fn client_config(mut self, config: IClientConfig) -> Result { - self.0 = self.0.client_builder(ClientBuilder::try_from(config)?); - Ok(self) - } - - /// Constructs a new [`Resolver`] based on the builder configuration. - #[wasm_bindgen] - pub fn build(self) -> PromiseResolver { - // WARNING: this does not validate the return type. Check carefully. - future_to_promise(async move { - self - .0 - .build() - .await - .map(WasmResolver::from) - .map(Into::into) - .wasm_result() - }) - .unchecked_into::() - } -} - -impl From>> for WasmResolverBuilder { - fn from(builder: ResolverBuilder>) -> Self { - Self(builder) - } -} diff --git a/bindings/wasm/tests/credentials.ts b/bindings/wasm/tests/credentials.ts index b56d57d075..0f12dc3ad5 100644 --- a/bindings/wasm/tests/credentials.ts +++ b/bindings/wasm/tests/credentials.ts @@ -11,9 +11,9 @@ const { Presentation, ProofOptions, RevocationBitmap, - StardustDocument, - StardustService, - StardustVerificationMethod, + IotaDocument, + IotaService, + IotaVerificationMethod, StatusCheck, SubjectHolderRelationship, Timestamp, @@ -163,21 +163,21 @@ describe('CredentialValidator, PresentationValidator', function () { describe('#validate()', function () { it('should work', async () => { // Set up issuer & subject DID documents. - const issuerDoc = new StardustDocument("iota"); + const issuerDoc = new IotaDocument("iota"); const issuerKeys = new KeyPair(KeyType.Ed25519); - issuerDoc.insertMethod(new StardustVerificationMethod(issuerDoc.id(), KeyType.Ed25519, issuerKeys.public(), "#iss-0"), MethodScope.VerificationMethod()); + issuerDoc.insertMethod(new IotaVerificationMethod(issuerDoc.id(), KeyType.Ed25519, issuerKeys.public(), "#iss-0"), MethodScope.VerificationMethod()); // Add RevocationBitmap service. const revocationBitmap = new RevocationBitmap(); - issuerDoc.insertService(new StardustService({ + issuerDoc.insertService(new IotaService({ id: issuerDoc.id().join("#my-revocation-service"), type: RevocationBitmap.type(), serviceEndpoint: revocationBitmap.toEndpoint() })) - const subjectDoc = new StardustDocument("iota"); + const subjectDoc = new IotaDocument("iota"); const subjectKeys = new KeyPair(KeyType.Ed25519); - subjectDoc.insertMethod(new StardustVerificationMethod(subjectDoc.id(), KeyType.Ed25519, subjectKeys.public(), "#sub-0"), MethodScope.VerificationMethod()); + subjectDoc.insertMethod(new IotaVerificationMethod(subjectDoc.id(), KeyType.Ed25519, subjectKeys.public(), "#sub-0"), MethodScope.VerificationMethod()); const subjectDID = subjectDoc.id(); const issuerDID = issuerDoc.id(); diff --git a/bindings/wasm/tests/document.ts b/bindings/wasm/tests/document.ts deleted file mode 100644 index 0593b6abef..0000000000 --- a/bindings/wasm/tests/document.ts +++ /dev/null @@ -1,50 +0,0 @@ -export {}; - -const assert = require('assert'); -const { - Document, - KeyType, - KeyPair, - Service, -} = require("../node"); - -describe('Document', function () { - describe('#insertService()', function () { - it('should take one type', async () => { - const keypair = new KeyPair(KeyType.Ed25519); - const doc = new Document(keypair); - - // Test single type. - const fragment1 = "new-service-1"; - doc.insertService(new Service({ - id: doc.id().toUrl().join('#' + fragment1), - type: "LinkedDomains", - serviceEndpoint: { - "origins": ["https://iota.org/", "https://example.com/"] - }, - })); - const service = doc.resolveService(fragment1); - assert.deepStrictEqual(service.id().fragment(), fragment1); - assert.deepStrictEqual(service.type(), ["LinkedDomains"]); - assert.deepStrictEqual(service.serviceEndpoint(), new Map([ - ["origins", ["https://iota.org/", "https://example.com/"]], - ])); - }); - it('should take multiple types', async () => { - const keypair = new KeyPair(KeyType.Ed25519); - const doc = new Document(keypair); - - // Test multiple types. - const fragment1 = "new-service-1"; - doc.insertService(new Service({ - id: doc.id().toUrl().join('#' + fragment1), - type: ["LinkedDomains", "ExampleType"], - serviceEndpoint: ["https://example.com/", "https://iota.org/"], - })); - const service = doc.resolveService(fragment1); - assert.deepStrictEqual(service.id().fragment(), fragment1); - assert.deepStrictEqual(service.type(), ["LinkedDomains", "ExampleType"]); - assert.deepStrictEqual(service.serviceEndpoint(), ["https://example.com/", "https://iota.org/"]); - }); - }); -}); diff --git a/bindings/wasm/tests/stardust.ts b/bindings/wasm/tests/iota.ts similarity index 83% rename from bindings/wasm/tests/stardust.ts rename to bindings/wasm/tests/iota.ts index 5026fe341b..2d9e71ec0b 100644 --- a/bindings/wasm/tests/stardust.ts +++ b/bindings/wasm/tests/iota.ts @@ -1,4 +1,4 @@ -export {}; +export { }; const assert = require('assert'); const { @@ -7,10 +7,10 @@ const { MethodScope, MethodType, MethodRelationship, - StardustDID, - StardustDocument, - StardustService, - StardustVerificationMethod, + IotaDID, + IotaDocument, + IotaService, + IotaVerificationMethod, Timestamp, } = require("../node"); @@ -18,15 +18,15 @@ const aliasIdBytes = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, const aliasIdHex = "0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"; const networkName = "smr"; -describe('StardustDID', function () { +describe('IotaDID', function () { describe('#constructor', function () { it('should work', () => { - const did = new StardustDID(aliasIdBytes, networkName); - assert.deepStrictEqual(did.toString(), "did:" + StardustDID.METHOD + ":" + networkName + ":" + aliasIdHex); + const did = new IotaDID(aliasIdBytes, networkName); + assert.deepStrictEqual(did.toString(), "did:" + IotaDID.METHOD + ":" + networkName + ":" + aliasIdHex); assert.deepStrictEqual(did.tag(), aliasIdHex); - assert.deepStrictEqual(did.method(), StardustDID.METHOD); + assert.deepStrictEqual(did.method(), IotaDID.METHOD); assert.deepStrictEqual(did.networkStr(), networkName); - assert.deepStrictEqual(did.authority(), StardustDID.METHOD + ":" + networkName + ":" + aliasIdHex); + assert.deepStrictEqual(did.authority(), IotaDID.METHOD + ":" + networkName + ":" + aliasIdHex); assert.deepStrictEqual(did.methodId(), networkName + ":" + aliasIdHex); assert.deepStrictEqual(did.scheme(), "did"); }); @@ -34,36 +34,36 @@ describe('StardustDID', function () { describe('#placeholder()', function () { it('should be zeroes', () => { const expectedTag = "0x0000000000000000000000000000000000000000000000000000000000000000"; - const did = StardustDID.placeholder(networkName); - assert.deepStrictEqual(did.toString(), "did:" + StardustDID.METHOD + ":" + networkName + ":" + expectedTag); + const did = IotaDID.placeholder(networkName); + assert.deepStrictEqual(did.toString(), "did:" + IotaDID.METHOD + ":" + networkName + ":" + expectedTag); assert.deepStrictEqual(did.tag(), expectedTag); - assert.deepStrictEqual(did.method(), StardustDID.METHOD); + assert.deepStrictEqual(did.method(), IotaDID.METHOD); assert.deepStrictEqual(did.networkStr(), networkName); - assert.deepStrictEqual(did.authority(), StardustDID.METHOD + ":" + networkName + ":" + expectedTag); + assert.deepStrictEqual(did.authority(), IotaDID.METHOD + ":" + networkName + ":" + expectedTag); assert.deepStrictEqual(did.methodId(), networkName + ":" + expectedTag); assert.deepStrictEqual(did.scheme(), "did"); }); }); }); -describe('StardustDocument', function () { +describe('IotaDocument', function () { describe('#constructors', function () { it('new should generate a placeholder', () => { - const doc = new StardustDocument(networkName); - assert.deepStrictEqual(doc.id().toString(), StardustDID.placeholder(networkName).toString()); + const doc = new IotaDocument(networkName); + assert.deepStrictEqual(doc.id().toString(), IotaDID.placeholder(networkName).toString()); }); it('newWithId should work', () => { - const did = new StardustDID(aliasIdBytes, networkName); - const doc = StardustDocument.newWithId(did); + const did = new IotaDID(aliasIdBytes, networkName); + const doc = IotaDocument.newWithId(did); assert.deepStrictEqual(doc.id().toString(), did.toString()); }); }); describe('#insert/resolve/removeMethod', function () { it('should work', async () => { - const doc = new StardustDocument(networkName); + const doc = new IotaDocument(networkName); const fragment = "new-method-1"; const scope = MethodScope.AssertionMethod(); - const method = new StardustVerificationMethod(doc.id(), KeyType.Ed25519, aliasIdBytes, fragment); + const method = new IotaVerificationMethod(doc.id(), KeyType.Ed25519, aliasIdBytes, fragment); // Add. doc.insertMethod(method, scope); @@ -89,9 +89,9 @@ describe('StardustDocument', function () { }); describe('#attach/detachMethodRelationship', function () { it('should work', async () => { - const doc = new StardustDocument(networkName); + const doc = new IotaDocument(networkName); const fragment = "new-method-1"; - const method = new StardustVerificationMethod(doc.id(), KeyType.Ed25519, aliasIdBytes, fragment); + const method = new IotaVerificationMethod(doc.id(), KeyType.Ed25519, aliasIdBytes, fragment); doc.insertMethod(method, MethodScope.VerificationMethod()); assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.VerificationMethod()).toJSON(), method.toJSON()); @@ -116,11 +116,11 @@ describe('StardustDocument', function () { }); describe('#insert/resolve/removeService', function () { it('should work', async () => { - const doc = new StardustDocument(networkName); + const doc = new IotaDocument(networkName); // Add. const fragment1 = "new-service-1"; - const service = new StardustService({ + const service = new IotaService({ id: doc.id().toUrl().join('#' + fragment1), type: ["LinkedDomains", "ExampleType"], serviceEndpoint: ["https://example.com/", "https://iota.org/"], @@ -145,7 +145,7 @@ describe('StardustDocument', function () { }); describe('#metadata', function () { it('should work', () => { - const doc = new StardustDocument(networkName); + const doc = new IotaDocument(networkName); const previousCreated = doc.metadataCreated(); const previousUpdated = doc.metadataUpdated(); @@ -183,7 +183,7 @@ describe('StardustDocument', function () { }); describe('#properties', function () { it('should work', () => { - const doc = new StardustDocument(networkName); + const doc = new IotaDocument(networkName); assert.deepStrictEqual(doc.properties(), new Map()); const properties = new Map() diff --git a/bindings/wasm/tests/account.ts b/bindings/wasm/tests/legacy/account.ts similarity index 97% rename from bindings/wasm/tests/account.ts rename to bindings/wasm/tests/legacy/account.ts index 6e30c04b9c..7a22be6f36 100644 --- a/bindings/wasm/tests/account.ts +++ b/bindings/wasm/tests/legacy/account.ts @@ -1,4 +1,6 @@ -export {}; +// TODO: Remove or reuse depending on what we do with the account. +// Note that this test is not executed as long as it sits in the legacy directory. +export { }; const assert = require('assert'); const { diff --git a/bindings/wasm/tests/resolver.ts b/bindings/wasm/tests/resolver.ts index dd22d09f04..965085846b 100644 --- a/bindings/wasm/tests/resolver.ts +++ b/bindings/wasm/tests/resolver.ts @@ -2,10 +2,10 @@ export {}; import { CoreDocument, - StardustDID, + IotaDID, MixedResolver, Presentation, - StardustDocument, + IotaDocument, CoreDID, FailFast, PresentationValidationOptions @@ -18,7 +18,7 @@ const issuerBarDocJSON = require("../../../identity_credential/tests/fixtures/si const holderFooDocJSON = require("../../../identity_credential/tests/fixtures/signed_presentation/subject_foo_doc.json"); const presentation = Presentation.fromJSON(presentationJSON); const holderFooDoc = CoreDocument.fromJSON(holderFooDocJSON); -const issuerIotaDoc: StardustDocument = StardustDocument.fromJSON(issuerIotaDocJSON); +const issuerIotaDoc: IotaDocument = IotaDocument.fromJSON(issuerIotaDocJSON); const issuerBarDoc: CoreDocument = CoreDocument.fromJSON(issuerBarDocJSON); describe("Resolver", function () { @@ -26,7 +26,7 @@ describe("Resolver", function () { it("should accept a correct presentation when configured correctly", async () => { // mock method handlers const resolveDidIota = async function (did_input: string) { - const parsedDid: StardustDID = StardustDID.parse(did_input); + const parsedDid: IotaDID = IotaDID.parse(did_input); if (issuerIotaDoc.id().toString() == parsedDid.toString()) { return issuerIotaDoc; } else { @@ -52,7 +52,7 @@ describe("Resolver", function () { } }; - let handlerMap: Map Promise> = new Map(); + let handlerMap: Map Promise> = new Map(); handlerMap.set("iota", resolveDidIota); handlerMap.set("foo", resolveDidFoo); handlerMap.set("bar", resolveDidBar); @@ -135,7 +135,7 @@ describe("Resolver", function () { return issuerIotaDoc; }; - let handlerMap: Map Promise> = new Map(); + let handlerMap: Map Promise> = new Map(); handlerMap.set("iota", resolveDidIotaMisconfigured); handlerMap.set("foo", resolveDidFooMisconfigured); handlerMap.set("bar", resolveDidBarMisconfigured); diff --git a/bindings/wasm/tests/wasm.rs b/bindings/wasm/tests/wasm.rs index de0679c0c2..c67abb9c2e 100644 --- a/bindings/wasm/tests/wasm.rs +++ b/bindings/wasm/tests/wasm.rs @@ -4,8 +4,10 @@ use std::borrow::Cow; use identity_iota::core::Timestamp; -use identity_iota::iota_core::IotaDID; -use identity_wasm::account::types::WasmKeyLocation; +use identity_iota::iota::IotaDID; +use identity_wasm::crypto::WasmProofOptions; +use identity_wasm::did::WasmVerifierOptions; +use serde_json::json; use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; use wasm_bindgen_test::*; @@ -13,12 +15,17 @@ use wasm_bindgen_test::*; use identity_wasm::common::WasmTimestamp; use identity_wasm::crypto::WasmKeyPair; use identity_wasm::crypto::WasmKeyType; -use identity_wasm::did::WasmDIDUrl; -use identity_wasm::did::WasmDocument; -use identity_wasm::did::WasmIotaDID; use identity_wasm::did::WasmMethodScope; -use identity_wasm::did::WasmVerificationMethod; use identity_wasm::error::WasmError; +use identity_wasm::iota::WasmIotaDID; +use identity_wasm::iota::WasmIotaDIDUrl; +use identity_wasm::iota::WasmIotaDocument; +use identity_wasm::iota::WasmIotaVerificationMethod; + +/// Generate a random byte array of the length of an Alias Id. +fn random_alias_id() -> [u8; 32] { + rand::random() +} #[wasm_bindgen_test] fn test_keypair() { @@ -48,47 +55,47 @@ fn test_js_error_from_wasm_error() { #[wasm_bindgen_test] fn test_did() { - let key = WasmKeyPair::new(WasmKeyType::Ed25519).unwrap(); - let did = WasmIotaDID::new(&key.public(), None).unwrap(); + let alias_bytes = random_alias_id(); + let did = WasmIotaDID::new(&alias_bytes, IotaDID::DEFAULT_NETWORK.to_owned()).unwrap(); - assert_eq!(did.network_str(), "main"); + assert_eq!(did.network_str(), WasmIotaDID::static_default_network()); let parsed = WasmIotaDID::parse(&did.to_string()).unwrap(); assert_eq!(did.to_string(), parsed.to_string()); - let base58 = WasmIotaDID::new(&key.public(), Some("dev".to_owned())).unwrap(); + let smr_did = WasmIotaDID::new(&alias_bytes, "smr".to_owned()).unwrap(); - assert_eq!(base58.tag(), did.tag()); - assert_eq!(base58.network_str(), "dev"); + assert_eq!(smr_did.tag(), did.tag()); + assert_eq!(smr_did.network_str(), "smr"); } #[wasm_bindgen_test] fn test_did_methods() { - let tag = "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV"; - let did_str = format!("did:iota:dev:{tag}"); + let tag = "0xdfda8bcfb959c3e6ef261343c3e1a8310e9c8294eeafee326a4e96d65dbeaca0"; + let did_str = format!("did:iota:smr:{tag}"); let did = WasmIotaDID::parse(&did_str).unwrap(); assert_eq!(did.to_string(), did_str); assert_eq!(did.tag(), tag); - assert_eq!(did.network_str(), "dev"); + assert_eq!(did.network_str(), "smr"); assert_eq!(did.scheme(), "did"); assert_eq!(did.method(), "iota"); - assert_eq!(did.method_id(), format!("dev:{tag}")); - assert_eq!(did.authority(), format!("iota:dev:{tag}")); + assert_eq!(did.method_id(), format!("smr:{tag}")); + assert_eq!(did.authority(), format!("iota:smr:{tag}")); } #[wasm_bindgen_test] fn test_did_url() { // Base DID Url - let key = WasmKeyPair::new(WasmKeyType::Ed25519).unwrap(); - let did = WasmIotaDID::new(&key.public(), None).unwrap(); + let alias_bytes = random_alias_id(); + let did = WasmIotaDID::new(&alias_bytes, WasmIotaDID::static_default_network()).unwrap(); let did_url = did.to_url(); assert_eq!(did.to_string(), did_url.to_string()); - let parsed_from_did = WasmDIDUrl::parse(&did.to_string()).unwrap(); - let parsed_from_did_url = WasmDIDUrl::parse(&did_url.to_string()).unwrap(); + let parsed_from_did = WasmIotaDIDUrl::parse(&did.to_string()).unwrap(); + let parsed_from_did_url = WasmIotaDIDUrl::parse(&did_url.to_string()).unwrap(); assert_eq!(did_url.to_string(), parsed_from_did.to_string()); assert_eq!(did_url.to_string(), parsed_from_did_url.to_string()); @@ -106,183 +113,150 @@ fn test_did_url() { #[wasm_bindgen_test] fn test_document_new() { - let keypair: WasmKeyPair = WasmKeyPair::new(WasmKeyType::Ed25519).unwrap(); - let document: WasmDocument = WasmDocument::new(&keypair, None, None).unwrap(); - assert_eq!(document.id().network_str(), "main"); - assert!(document.default_signing_method().is_ok()); + let document: WasmIotaDocument = WasmIotaDocument::new("atoi".to_owned()).unwrap(); + assert_eq!( + document.id().tag(), + "0x0000000000000000000000000000000000000000000000000000000000000000" + ); + assert_eq!(document.id().network_str(), "atoi"); + assert!(document.metadata_created().is_some()); + assert!(document.metadata_updated().is_some()); } #[wasm_bindgen_test] -fn test_document_sign_self() { +fn test_document_sign() { let keypair: WasmKeyPair = WasmKeyPair::new(WasmKeyType::Ed25519).unwrap(); // Sign with DIDUrl method query. { - let mut document: WasmDocument = WasmDocument::new(&keypair, None, None).unwrap(); + let mut document: WasmIotaDocument = WasmIotaDocument::new(WasmIotaDID::static_default_network()).unwrap(); + let method = WasmIotaVerificationMethod::new( + &document.id(), + WasmKeyType::Ed25519, + keypair.public(), + "#sign-0".to_owned(), + ) + .unwrap(); document - .sign_self( - &keypair, - &JsValue::from(document.default_signing_method().unwrap().id()).unchecked_into(), + .insert_method(&method, &WasmMethodScope::authentication()) + .unwrap(); + + let data = JsValue::from_serde(&json!({ + "answer": 42, + "nested": { + "property": "string", + } + })) + .unwrap(); + + // Sign with DIDUrl. + let signed = document + .sign_data( + &data, + keypair.private(), + &JsValue::from(method.id()).unchecked_into(), + &WasmProofOptions::default(), ) .unwrap(); - assert!(document.verify_document(&document).is_ok()); - } - // Sign with string method query. - { - let mut document: WasmDocument = WasmDocument::new(&keypair, None, None).unwrap(); - document - .sign_self( - &keypair, - &JsValue::from_str(&document.default_signing_method().unwrap().id().to_string()).unchecked_into(), + // Sign with string method query. + let signed_str = document + .sign_data( + &data, + keypair.private(), + &JsValue::from_str("#sign-0").unchecked_into(), + &WasmProofOptions::default(), ) .unwrap(); - assert!(document.verify_document(&document).is_ok()); + + assert!(document.verify_data(&signed, &WasmVerifierOptions::default()).unwrap()); + assert_eq!( + signed.into_serde::().unwrap(), + signed_str.into_serde::().unwrap() + ); } } #[wasm_bindgen_test] fn test_document_resolve_method() { - let keypair: WasmKeyPair = WasmKeyPair::new(WasmKeyType::Ed25519).unwrap(); - let mut document: WasmDocument = WasmDocument::new(&keypair, None, None).unwrap(); - let default_method: WasmVerificationMethod = document.default_signing_method().unwrap(); + let mut document: WasmIotaDocument = WasmIotaDocument::new(WasmIotaDID::static_default_network()).unwrap(); - let keypair_new: WasmKeyPair = WasmKeyPair::new(WasmKeyType::Ed25519).unwrap(); - let method_new: WasmVerificationMethod = WasmVerificationMethod::new( + let keypair: WasmKeyPair = WasmKeyPair::new(WasmKeyType::Ed25519).unwrap(); + let method: WasmIotaVerificationMethod = WasmIotaVerificationMethod::new( &document.id(), WasmKeyType::Ed25519, - keypair_new.public(), + keypair.public(), "new-key".to_owned(), ) .unwrap(); document - .insert_method(&method_new, &WasmMethodScope::authentication()) + .insert_method(&method, &WasmMethodScope::authentication()) .unwrap(); // Resolve with DIDUrl method query. assert_eq!( document - .resolve_method(&JsValue::from(default_method.id()).unchecked_into(), None) - .unwrap() - .unwrap() - .id() - .to_string(), - default_method.id().to_string() - ); - assert_eq!( - document - .resolve_method(&JsValue::from(method_new.id()).unchecked_into(), None) + .resolve_method(&JsValue::from(method.id()).unchecked_into(), None) .unwrap() .unwrap() .id() .to_string(), - method_new.id().to_string() + method.id().to_string() ); // Resolve with string method query. assert_eq!( document - .resolve_method( - &JsValue::from_str(&default_method.id().to_string()).unchecked_into(), - None, - ) - .unwrap() - .unwrap() - .id() - .to_string(), - default_method.id().to_string() - ); - assert_eq!( - document - .resolve_method(&JsValue::from_str(&method_new.id().to_string()).unchecked_into(), None) + .resolve_method(&JsValue::from_str(&method.id().to_string()).unchecked_into(), None) .unwrap() .unwrap() .id() .to_string(), - method_new.id().to_string() + method.id().to_string() ); // Resolve with string fragment method query. assert_eq!( document .resolve_method( - &JsValue::from_str(&default_method.id().fragment().unwrap()).unchecked_into(), + &JsValue::from_str(&method.id().fragment().unwrap()).unchecked_into(), None, ) .unwrap() .unwrap() .id() .to_string(), - default_method.id().to_string() - ); - assert_eq!( - document - .resolve_method( - &JsValue::from_str(&method_new.id().fragment().unwrap()).unchecked_into(), - None, - ) - .unwrap() - .unwrap() - .id() - .to_string(), - method_new.id().to_string() + method.id().to_string() ); // Resolve with correct verification method relationship. assert_eq!( document .resolve_method( - &JsValue::from(default_method.id()).unchecked_into(), - Some(JsValue::from(WasmMethodScope::capability_invocation()).unchecked_into()), - ) - .unwrap() - .unwrap() - .id() - .to_string(), - default_method.id().to_string() - ); - assert_eq!( - document - .resolve_method( - &JsValue::from(method_new.id()).unchecked_into(), + &JsValue::from(method.id()).unchecked_into(), Some(JsValue::from(WasmMethodScope::authentication()).unchecked_into()), ) .unwrap() .unwrap() .id() .to_string(), - method_new.id().to_string() + method.id().to_string() ); // Resolve with wrong verification method relationship. assert!(document .resolve_method( - &JsValue::from(default_method.id()).unchecked_into(), - Some(JsValue::from(WasmMethodScope::key_agreement()).unchecked_into()), - ) - .unwrap() - .is_none()); - assert!(document - .resolve_method( - &JsValue::from(method_new.id()).unchecked_into(), + &JsValue::from(method.id()).unchecked_into(), Some(JsValue::from(WasmMethodScope::assertion_method()).unchecked_into()), ) .unwrap() .is_none()); } -#[wasm_bindgen_test] -fn test_document_network() { - let keypair: WasmKeyPair = WasmKeyPair::new(WasmKeyType::Ed25519).unwrap(); - let document: WasmDocument = WasmDocument::new(&keypair, Some("dev".to_owned()), None).unwrap(); - - assert_eq!(document.id().network_str(), "dev"); -} - #[wasm_bindgen_test] fn test_did_serde() { // Ensure JSON deserialization of DID and strings match (for UWasmDID duck-typed parameters). - let expected_str: &str = "did:iota:H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV"; + let expected_str: &str = "did:iota:0xdfda8bcfb959c3e6ef261343c3e1a8310e9c8294eeafee326a4e96d65dbeaca0"; let expected: IotaDID = IotaDID::parse(expected_str).unwrap(); // Check WasmDID deserialization. @@ -314,60 +288,27 @@ fn test_timestamp_serde() { assert_eq!(wasm_de.to_rfc3339(), expected_str); } -#[wasm_bindgen_test] -fn test_sign_document() { - let keypair1: WasmKeyPair = WasmKeyPair::new(WasmKeyType::Ed25519).unwrap(); - let document1: WasmDocument = WasmDocument::new(&keypair1, None, None).unwrap(); - - // Replace the default signing method. - let mut document2: WasmDocument = document1.deep_clone(); - let keypair2: WasmKeyPair = WasmKeyPair::new(WasmKeyType::Ed25519).unwrap(); - let method: WasmVerificationMethod = WasmVerificationMethod::new( - &document2.id(), - keypair2.type_(), - keypair2.public(), - "#method-2".to_owned(), - ) - .unwrap(); - document2 - .insert_method(&method, &WasmMethodScope::capability_invocation()) - .unwrap(); - document2 - .remove_method(&document1.default_signing_method().unwrap().id()) - .unwrap(); - - // Sign update using original document. - assert!(document1.verify_document(&document2).is_err()); - document1 - .sign_document( - &mut document2, - &keypair1, - &JsValue::from(document1.default_signing_method().unwrap().id()).unchecked_into(), - ) - .unwrap(); - document1.verify_document(&document2).unwrap(); -} - +// TODO: Remove or reuse depending on what we do with the account. depending on what we do with the account. // This test should be matched by a test with equivalent test vector in Rust // to ensure hashes are consistent across architectures. -#[wasm_bindgen_test] -fn test_hash_is_consistent() { - let test_vector_1: [u8; 32] = [ - 187, 104, 26, 87, 133, 152, 0, 180, 17, 232, 218, 46, 190, 140, 102, 34, 42, 94, 9, 101, 87, 249, 167, 237, 194, - 182, 240, 2, 150, 78, 110, 218, - ]; - - let test_vector_2: [u8; 32] = [ - 125, 153, 99, 21, 23, 190, 149, 109, 84, 120, 40, 91, 181, 57, 67, 254, 11, 25, 152, 214, 84, 46, 105, 186, 16, 39, - 141, 151, 100, 163, 138, 222, - ]; - - let location_1 = WasmKeyLocation::new(WasmKeyType::Ed25519, "".to_owned(), test_vector_1.to_vec()); - let location_2 = WasmKeyLocation::new(WasmKeyType::Ed25519, "".to_owned(), test_vector_2.to_vec()); - - assert_eq!(location_1.to_string().split(':').last().unwrap(), "74874706796298672"); - assert_eq!( - location_2.to_string().split(':').last().unwrap(), - "10201576743536852223" - ); -} +// #[wasm_bindgen_test] +// fn test_hash_is_consistent() { +// let test_vector_1: [u8; 32] = [ +// 187, 104, 26, 87, 133, 152, 0, 180, 17, 232, 218, 46, 190, 140, 102, 34, 42, 94, 9, 101, 87, 249, 167, 237, 194, +// 182, 240, 2, 150, 78, 110, 218, +// ]; + +// let test_vector_2: [u8; 32] = [ +// 125, 153, 99, 21, 23, 190, 149, 109, 84, 120, 40, 91, 181, 57, 67, 254, 11, 25, 152, 214, 84, 46, 105, 186, 16, +// 39, 141, 151, 100, 163, 138, 222, +// ]; + +// let location_1 = WasmKeyLocation::new(WasmKeyType::Ed25519, "".to_owned(), test_vector_1.to_vec()); +// let location_2 = WasmKeyLocation::new(WasmKeyType::Ed25519, "".to_owned(), test_vector_2.to_vec()); + +// assert_eq!(location_1.to_string().split(':').last().unwrap(), "74874706796298672"); +// assert_eq!( +// location_2.to_string().split(':').last().unwrap(), +// "10201576743536852223" +// ); +// } diff --git a/documentation/docs/concepts/decentralized_identifiers/create.mdx b/documentation/docs/concepts/decentralized_identifiers/create.mdx index 7c3f10c99c..3a76512d31 100644 --- a/documentation/docs/concepts/decentralized_identifiers/create.mdx +++ b/documentation/docs/concepts/decentralized_identifiers/create.mdx @@ -29,7 +29,7 @@ Select your programming language of choice and press the green play button to ex nodeReplitLink="https://repl.it/@IOTAFoundation/Create-DID-07?lite=true" rustContent={createDidRustExample} nodeGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/bindings/wasm/examples-account/src/create_did.ts" - rustGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/identity_stardust/examples/ex0_create_did.rs" + rustGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/examples/0_basic/0_create_did.rs" /> This examples creates a new address with funds using the faucet on the testnet. Next, it creates a new DID Document with a verification method. This DID Document is then published in an Alias Output making the DID available in the ledger state and resolvable by any node. @@ -43,7 +43,7 @@ Updating the identity is also possible, which will be discussed in the next sect In order to create a Alias Output, an existing output is required to make a transaction. For that a new Address is generated and a Basic Output controlled by this address is created using the faucet. -A DID Document is then created which includes one verification method. At this point the DID itself is unknown since the Alias Output is not published yet and didn't get an `Alias ID` assigned. For that a placeholder `did:stardust:0x0000000000000000000000000000000000000000000000000000000000000000` is used to reference the DID inside the document. +A DID Document is then created which includes one verification method. At this point the DID itself is unknown since the Alias Output is not published yet and didn't get an `Alias ID` assigned. For that a placeholder `did:iota:0x0000000000000000000000000000000000000000000000000000000000000000` is used to reference the DID inside the document. An Alias Output is created which contains an encoded version of the DID Document in its `State Metadata` and has the state controller and the governor set to the generated Ed25519 address. Note that controllers don't have to be Ed25519 addresses, they can be any type of output. However, they must be unlocked in order perform a state or governance transition when the DID Document is updated or destroyed. diff --git a/documentation/docs/concepts/decentralized_identifiers/delete.mdx b/documentation/docs/concepts/decentralized_identifiers/delete.mdx index 8ad2aeed02..43d23752c1 100644 --- a/documentation/docs/concepts/decentralized_identifiers/delete.mdx +++ b/documentation/docs/concepts/decentralized_identifiers/delete.mdx @@ -10,9 +10,9 @@ keywords: --- import deactivate_did_rs from '!!raw-loader!../../../../examples/0_basic/3_deactivate_did.rs'; -import deactivate_did_js from '!!raw-loader!../../../../bindings/wasm/examples-stardust/src/ex3_deactivate_did.ts'; +import deactivate_did_js from '!!raw-loader!../../../../bindings/wasm/examples/src/ex3_deactivate_did.ts'; import delete_did_rs from '!!raw-loader!../../../../examples/0_basic/4_delete_did.rs'; -import delete_did_js from '!!raw-loader!../../../../bindings/wasm/examples-stardust/src/ex4_delete_did.ts'; +import delete_did_js from '!!raw-loader!../../../../bindings/wasm/examples/src/ex4_delete_did.ts'; import CodeSnippet from '../../../src/components/CodeSnippetComponent' @@ -42,7 +42,7 @@ The following example demonstrates deactivating and reactivating an IOTA DID Doc @@ -69,6 +69,6 @@ The following example demonstrates a governor destroying an IOTA Identity and se diff --git a/documentation/docs/concepts/decentralized_identifiers/private_tangle.mdx b/documentation/docs/concepts/decentralized_identifiers/private_tangle.mdx deleted file mode 100644 index 8a26f834d8..0000000000 --- a/documentation/docs/concepts/decentralized_identifiers/private_tangle.mdx +++ /dev/null @@ -1,46 +0,0 @@ ---- -title: Create a DID on a Private Tangle -sidebar_label: Create a DID on a Private Tangle -description: Create a DID on a Private Tangle using the IOTA Identity Rust Library or its WASM bindings -image: /img/Identity_icon.png -keywords: -- Rust -- WASM ---- -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -import CodeBlock from '@theme/CodeBlock'; -import private_tangle_js from '!!raw-loader!../../../../bindings/wasm/examples/src/private_tangle.js'; -import private_tangle_rs from '!!raw-loader!../../../../examples_legacy/low-level-api/private_tangle.rs'; -import account_private_tangle_rs from '!!raw-loader!../../../../examples_legacy/account/config.rs'; - -## Example - -This example shows how you can create a DID on a private tangle. You can run it together with a local [Hornet node](https://wiki.iota.org/hornet/welcome). - -### Account Module (Recommended) - - - {account_private_tangle_rs} - - -### Low-level API - - - - - {private_tangle_rs} - - - - - {private_tangle_js} - - diff --git a/documentation/docs/concepts/decentralized_identifiers/resolve.mdx b/documentation/docs/concepts/decentralized_identifiers/resolve.mdx index 0a920d7077..e4ed526525 100644 --- a/documentation/docs/concepts/decentralized_identifiers/resolve.mdx +++ b/documentation/docs/concepts/decentralized_identifiers/resolve.mdx @@ -7,7 +7,7 @@ keywords: - Resolve --- import resolve_did_rs from '!!raw-loader!../../../../examples/0_basic/2_resolve_did.rs'; -import resolve_did_js from '!!raw-loader!../../../../bindings/wasm/examples-stardust/src/ex2_resolve_did.ts'; +import resolve_did_js from '!!raw-loader!../../../../bindings/wasm/examples/src/ex2_resolve_did.ts'; import CodeSnippet from '../../../src/components/CodeSnippetComponent'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; @@ -34,23 +34,28 @@ Multiple networks can be supported by a `Resolver` simply by adding multiple cli ```rust -use identity_resolver::Resolver; -use identity_stardust::{StardustDID, StardustDocument, StardustIdentityClientExt}; +use identity_iota::iota::IotaDID; +use identity_iota::iota::IotaDocument; +use identity_iota::resolver::Resolver; use iota_client::Client; #[tokio::main] async fn main() { - // Configure a client for the Shimmer testnet "rms". - let node_url = "https://api.testnet.shimmer.network/"; - let client = Client::builder().with_primary_node(node_url, None).unwrap().finish().unwrap(); - - // Construct a resolver using the client. - let mut resolver = Resolver::::new(); - resolver.attach_iota_handler(client); - - // Parse the DID and resolve its DID Document. - let did = StardustDID::parse("did:iota:rms:0x7b48b06232b8a1e7a31c314cab1ceedb84e2e9dd2b1fae79b67eaa4595f15e47").unwrap(); - let document: StardustDocument = resolver.resolve(&did).await.unwrap(); + // Configure a client for the Shimmer testnet "rms". + let node_url = "https://api.testnet.shimmer.network/"; + let client = Client::builder() + .with_primary_node(node_url, None) + .unwrap() + .finish() + .unwrap(); + + // Construct a resolver using the client. + let mut resolver = Resolver::::new(); + resolver.attach_iota_handler(client); + + // Parse the DID and resolve its DID Document. + let did = IotaDID::parse("did:iota:rms:0x7b48b06232b8a1e7a31c314cab1ceedb84e2e9dd2b1fae79b67eaa4595f15e47").unwrap(); + let document: IotaDocument = resolver.resolve(&did).await.unwrap(); } ``` @@ -58,7 +63,7 @@ async fn main() { ```js -const { MixedResolver, StardustDID, StardustIdentityClient } = require('@iota/identity-wasm/node'); +const { MixedResolver, IotaDID, IotaIdentityClient } = require('@iota/identity-wasm/node'); const { Client } = require('@cycraig/iota-client-wasm/node'); // Configure a client for the Shimmer testnet "rms". @@ -67,7 +72,7 @@ const client = new Client({ primaryNode: nodeUrl, localPow: true, }); -const didClient = new StardustIdentityClient(client); +const didClient = new IotaIdentityClient(client); // Construct a resolver using the client. const resolver = new MixedResolver({ @@ -75,7 +80,7 @@ const resolver = new MixedResolver({ }); // Parse the DID and resolve its DID Document. -const did = StardustDID.parse("did:iota:rms:0x7b48b06232b8a1e7a31c314cab1ceedb84e2e9dd2b1fae79b67eaa4595f15e47"); +const did = IotaDID.parse("did:iota:rms:0x7b48b06232b8a1e7a31c314cab1ceedb84e2e9dd2b1fae79b67eaa4595f15e47"); const document = await resolver.resolve(did); ``` @@ -90,18 +95,24 @@ The `Client` can also be used directly, to resolve individual DIDs from its conf ```rust -use identity_stardust::{StardustDID, StardustDocument, StardustIdentityClientExt}; +use identity_iota::iota::IotaDID; +use identity_iota::iota::IotaDocument; +use identity_iota::iota::IotaIdentityClientExt; use iota_client::Client; #[tokio::main] async fn main() { - // Configure a client for the Shimmer testnet "rms". - let node_url = "https://api.testnet.shimmer.network/"; - let client = Client::builder().with_primary_node(node_url, None).unwrap().finish().unwrap(); - - // Parse the DID and resolve its DID Document. - let did = StardustDID::parse("did:iota:rms:0x7b48b06232b8a1e7a31c314cab1ceedb84e2e9dd2b1fae79b67eaa4595f15e47").unwrap(); - let document: StardustDocument = client.resolve_did(&did).await.unwrap(); + // Configure a client for the Shimmer testnet "rms". + let node_url = "https://api.testnet.shimmer.network/"; + let client = Client::builder() + .with_primary_node(node_url, None) + .unwrap() + .finish() + .unwrap(); + + // Parse the DID and resolve its DID Document. + let did = IotaDID::parse("did:iota:rms:0x7b48b06232b8a1e7a31c314cab1ceedb84e2e9dd2b1fae79b67eaa4595f15e47").unwrap(); + let document: IotaDocument = client.resolve_did(&did).await.unwrap(); } ``` @@ -109,7 +120,7 @@ async fn main() { ```js -const { StardustDID, StardustIdentityClient } = require('@iota/identity-wasm/node'); +const { IotaDID, IotaIdentityClient } = require('@iota/identity-wasm/node'); const { Client } = require('@cycraig/iota-client-wasm/node'); // Configure a client for the Shimmer testnet "rms". @@ -118,10 +129,10 @@ const client = new Client({ primaryNode: nodeUrl, localPow: true, }); -const didClient = new StardustIdentityClient(client); +const didClient = new IotaIdentityClient(client); // Parse the DID and resolve its DID Document. -const did = StardustDID.parse("did:iota:rms:0x7b48b06232b8a1e7a31c314cab1ceedb84e2e9dd2b1fae79b67eaa4595f15e47"); +const did = IotaDID.parse("did:iota:rms:0x7b48b06232b8a1e7a31c314cab1ceedb84e2e9dd2b1fae79b67eaa4595f15e47"); const document = await didClient.resolveDid(did); ``` @@ -143,6 +154,6 @@ The following example creates a new IOTA DID, publishes it and then resolves its diff --git a/documentation/sidebars.js b/documentation/sidebars.js index ad6eb80068..dfe032c2da 100644 --- a/documentation/sidebars.js +++ b/documentation/sidebars.js @@ -49,7 +49,6 @@ module.exports = { 'concepts/decentralized_identifiers/update', 'concepts/decentralized_identifiers/resolve', 'concepts/decentralized_identifiers/delete', - 'concepts/decentralized_identifiers/private_tangle', ], 'Verifiable Credentials': [ 'concepts/verifiable_credentials/overview', diff --git a/examples/0_basic/0_create_did.rs b/examples/0_basic/0_create_did.rs index b07b8fcbca..581da20b99 100644 --- a/examples/0_basic/0_create_did.rs +++ b/examples/0_basic/0_create_did.rs @@ -2,24 +2,25 @@ // SPDX-License-Identifier: Apache-2.0 use anyhow::Context; -use examples::get_address_with_funds; -use examples::random_stronghold_path; -use examples::NETWORK_ENDPOINT; -use identity_core::convert::ToJson; -use identity_core::crypto::KeyPair; -use identity_core::crypto::KeyType; -use identity_did::verification::MethodScope; -use identity_stardust::NetworkName; -use identity_stardust::StardustClientExt; -use identity_stardust::StardustDocument; -use identity_stardust::StardustIdentityClientExt; -use identity_stardust::StardustVerificationMethod; use iota_client::block::address::Address; use iota_client::block::output::AliasOutput; use iota_client::secret::stronghold::StrongholdSecretManager; use iota_client::secret::SecretManager; use iota_client::Client; +use examples::get_address_with_funds; +use examples::random_stronghold_path; +use examples::NETWORK_ENDPOINT; +use identity_iota::core::ToJson; +use identity_iota::crypto::KeyPair; +use identity_iota::crypto::KeyType; +use identity_iota::did::MethodScope; +use identity_iota::iota::IotaClientExt; +use identity_iota::iota::IotaDocument; +use identity_iota::iota::IotaIdentityClientExt; +use identity_iota::iota::IotaVerificationMethod; +use identity_iota::iota::NetworkName; + /// Demonstrates how to create a DID Document and publish it in a new Alias Output. #[tokio::main] async fn main() -> anyhow::Result<()> { @@ -43,12 +44,12 @@ async fn main() -> anyhow::Result<()> { // Create a new DID document with a placeholder DID. // The DID will be derived from the Alias Id of the Alias Output after publishing. - let mut document: StardustDocument = StardustDocument::new(&network_name); + let mut document: IotaDocument = IotaDocument::new(&network_name); // Insert a new Ed25519 verification method in the DID document. let keypair: KeyPair = KeyPair::new(KeyType::Ed25519)?; - let method: StardustVerificationMethod = - StardustVerificationMethod::new(document.id().clone(), keypair.type_(), keypair.public(), "#key-1")?; + let method: IotaVerificationMethod = + IotaVerificationMethod::new(document.id().clone(), keypair.type_(), keypair.public(), "#key-1")?; document.insert_method(method, MethodScope::VerificationMethod)?; // Construct an Alias Output containing the DID document, with the wallet address @@ -57,7 +58,7 @@ async fn main() -> anyhow::Result<()> { println!("Alias Output: {}", alias_output.to_json()?); // Publish the Alias Output and get the published DID document. - let document: StardustDocument = client.publish_did_output(&secret_manager, alias_output).await?; + let document: IotaDocument = client.publish_did_output(&secret_manager, alias_output).await?; println!("Published DID document: {:#}", document); Ok(()) diff --git a/examples/0_basic/1_update_did.rs b/examples/0_basic/1_update_did.rs index 59ca2ad742..feaa4fc35a 100644 --- a/examples/0_basic/1_update_did.rs +++ b/examples/0_basic/1_update_did.rs @@ -4,19 +4,19 @@ use examples::create_did; use examples::random_stronghold_path; use examples::NETWORK_ENDPOINT; -use identity_core::common::Timestamp; -use identity_core::convert::FromJson; -use identity_core::json; -use identity_did::did::DID; -use identity_did::service::Service; -use identity_did::verification::MethodRelationship; -use identity_stardust::block::address::Address; -use identity_stardust::block::output::RentStructure; -use identity_stardust::StardustClientExt; -use identity_stardust::StardustDID; -use identity_stardust::StardustDocument; -use identity_stardust::StardustIdentityClientExt; -use identity_stardust::StardustService; +use identity_iota::core::json; +use identity_iota::core::FromJson; +use identity_iota::core::Timestamp; +use identity_iota::did::MethodRelationship; +use identity_iota::did::Service; +use identity_iota::did::DID; +use identity_iota::iota::block::address::Address; +use identity_iota::iota::block::output::RentStructure; +use identity_iota::iota::IotaClientExt; +use identity_iota::iota::IotaDID; +use identity_iota::iota::IotaDocument; +use identity_iota::iota::IotaIdentityClientExt; +use identity_iota::iota::IotaService; use iota_client::block::output::AliasOutput; use iota_client::block::output::AliasOutputBuilder; use iota_client::secret::stronghold::StrongholdSecretManager; @@ -37,10 +37,10 @@ async fn main() -> anyhow::Result<()> { ); // Create a new DID in an Alias Output for us to modify. - let (_, did): (Address, StardustDID) = create_did(&client, &mut secret_manager).await?; + let (_, did): (Address, IotaDID) = create_did(&client, &mut secret_manager).await?; // Resolve the latest state of the document. - let mut document: StardustDocument = client.resolve_did(&did).await?; + let mut document: IotaDocument = client.resolve_did(&did).await?; // Attach a new method relationship to the existing method. document.attach_method_relationship( @@ -49,7 +49,7 @@ async fn main() -> anyhow::Result<()> { )?; // Add a new Service. - let service: StardustService = Service::from_json_value(json!({ + let service: IotaService = Service::from_json_value(json!({ "id": document.id().to_url().join("#linked-domain")?, "type": "LinkedDomains", "serviceEndpoint": "https://iota.org/" @@ -68,7 +68,7 @@ async fn main() -> anyhow::Result<()> { .finish()?; // Publish the updated Alias Output. - let updated: StardustDocument = client.publish_did_output(&secret_manager, alias_output).await?; + let updated: IotaDocument = client.publish_did_output(&secret_manager, alias_output).await?; println!("Updated DID document: {:#}", updated); Ok(()) diff --git a/examples/0_basic/2_resolve_did.rs b/examples/0_basic/2_resolve_did.rs index 155a90c29e..9c501c5296 100644 --- a/examples/0_basic/2_resolve_did.rs +++ b/examples/0_basic/2_resolve_did.rs @@ -4,11 +4,11 @@ use examples::create_did; use examples::random_stronghold_path; use examples::NETWORK_ENDPOINT; -use identity_resolver::Resolver; -use identity_stardust::block::address::Address; -use identity_stardust::StardustDID; -use identity_stardust::StardustDocument; -use identity_stardust::StardustIdentityClientExt; +use identity_iota::iota::block::address::Address; +use identity_iota::iota::IotaDID; +use identity_iota::iota::IotaDocument; +use identity_iota::iota::IotaIdentityClientExt; +use identity_iota::prelude::Resolver; use iota_client::block::output::AliasOutput; use iota_client::secret::stronghold::StrongholdSecretManager; use iota_client::secret::SecretManager; @@ -28,22 +28,22 @@ async fn main() -> anyhow::Result<()> { ); // Create a new DID in an Alias Output for us to resolve. - let (_, did): (Address, StardustDID) = create_did(&client, &mut secret_manager).await?; + let (_, did): (Address, IotaDID) = create_did(&client, &mut secret_manager).await?; - // We can resolve a `StardustDID` with the client itself. + // We can resolve a `IotaDID` with the client itself. // Resolve the associated Alias Output and extract the DID document from it. - let client_document: StardustDocument = client.resolve_did(&did).await?; + let client_document: IotaDocument = client.resolve_did(&did).await?; println!("Client resolved DID Document: {:#}", client_document); // We can also create a `Resolver` that has additional convenience methods, // for example to resolve presentation issuers or to verify presentations. - let mut resolver = Resolver::::new(); + let mut resolver = Resolver::::new(); // We need to register a handler that can resolve IOTA DIDs. // This convenience method only requires us to provide a client. resolver.attach_iota_handler(client.clone()); - let resolver_document: StardustDocument = resolver.resolve(&did).await.unwrap(); + let resolver_document: IotaDocument = resolver.resolve(&did).await.unwrap(); // Client and Resolver resolve to the same document in this case. assert_eq!(client_document, resolver_document); diff --git a/examples/0_basic/3_deactivate_did.rs b/examples/0_basic/3_deactivate_did.rs index f62109d921..00e2fbaf16 100644 --- a/examples/0_basic/3_deactivate_did.rs +++ b/examples/0_basic/3_deactivate_did.rs @@ -4,11 +4,11 @@ use examples::create_did; use examples::random_stronghold_path; use examples::NETWORK_ENDPOINT; -use identity_stardust::block::address::Address; -use identity_stardust::StardustClientExt; -use identity_stardust::StardustDID; -use identity_stardust::StardustDocument; -use identity_stardust::StardustIdentityClientExt; +use identity_iota::iota::block::address::Address; +use identity_iota::iota::IotaClientExt; +use identity_iota::iota::IotaDID; +use identity_iota::iota::IotaDocument; +use identity_iota::iota::IotaIdentityClientExt; use iota_client::block::output::AliasOutput; use iota_client::block::output::AliasOutputBuilder; use iota_client::secret::stronghold::StrongholdSecretManager; @@ -29,10 +29,10 @@ async fn main() -> anyhow::Result<()> { ); // Create a new DID in an Alias Output for us to modify. - let (_, did): (Address, StardustDID) = create_did(&client, &mut secret_manager).await?; + let (_, did): (Address, IotaDID) = create_did(&client, &mut secret_manager).await?; // Resolve the latest state of the DID document, so we can reactivate it later. - let document: StardustDocument = client.resolve_did(&did).await?; + let document: IotaDocument = client.resolve_did(&did).await?; // Deactivate the DID by publishing an empty document. // This process can be reversed since the Alias Output is not destroyed. @@ -50,7 +50,7 @@ async fn main() -> anyhow::Result<()> { // Resolving a deactivated DID returns an empty DID document // with its `deactivated` metadata field set to `true`. - let deactivated: StardustDocument = client.resolve_did(&did).await?; + let deactivated: IotaDocument = client.resolve_did(&did).await?; println!("Deactivated DID document: {:#}", deactivated); assert_eq!(deactivated.metadata.deactivated, Some(true)); @@ -65,7 +65,7 @@ async fn main() -> anyhow::Result<()> { client.publish_did_output(&secret_manager, reactivated_output).await?; // Resolve the reactivated DID document. - let reactivated: StardustDocument = client.resolve_did(&did).await?; + let reactivated: IotaDocument = client.resolve_did(&did).await?; assert_eq!(document, reactivated); assert!(!reactivated.metadata.deactivated.unwrap_or_default()); diff --git a/examples/0_basic/4_delete_did.rs b/examples/0_basic/4_delete_did.rs index 9353ff9c8e..b2a52e5de3 100644 --- a/examples/0_basic/4_delete_did.rs +++ b/examples/0_basic/4_delete_did.rs @@ -4,10 +4,10 @@ use examples::create_did; use examples::random_stronghold_path; use examples::NETWORK_ENDPOINT; -use identity_stardust::Error; -use identity_stardust::StardustClientExt; -use identity_stardust::StardustDID; -use identity_stardust::StardustIdentityClientExt; +use identity_iota::iota::Error; +use identity_iota::iota::IotaClientExt; +use identity_iota::iota::IotaDID; +use identity_iota::iota::IotaIdentityClientExt; use iota_client::block::address::Address; use iota_client::secret::stronghold::StrongholdSecretManager; use iota_client::secret::SecretManager; @@ -27,7 +27,7 @@ async fn main() -> anyhow::Result<()> { ); // Create a new DID in an Alias Output for us to modify. - let (address, did): (Address, StardustDID) = create_did(&client, &mut secret_manager).await?; + let (address, did): (Address, IotaDID) = create_did(&client, &mut secret_manager).await?; // Deletes the Alias Output and its contained DID Document, rendering the DID permanently destroyed. // This operation is *not* reversible. @@ -38,7 +38,7 @@ async fn main() -> anyhow::Result<()> { let error: Error = client.resolve_did(&did).await.unwrap_err(); assert!(matches!( error, - identity_stardust::Error::DIDResolutionError(iota_client::Error::NotFound) + identity_iota::iota::Error::DIDResolutionError(iota_client::Error::NotFound) )); Ok(()) diff --git a/examples/1_advanced/0_did_controls_did.rs b/examples/1_advanced/0_did_controls_did.rs index 6dff01e714..6896635684 100644 --- a/examples/1_advanced/0_did_controls_did.rs +++ b/examples/1_advanced/0_did_controls_did.rs @@ -6,17 +6,17 @@ use std::ops::Deref; use examples::create_did; use examples::random_stronghold_path; use examples::NETWORK_ENDPOINT; -use identity_core::crypto::KeyPair; -use identity_core::crypto::KeyType; -use identity_did::verification::MethodScope; -use identity_stardust::block::output::AliasId; -use identity_stardust::block::output::UnlockCondition; -use identity_stardust::NetworkName; -use identity_stardust::StardustClientExt; -use identity_stardust::StardustDID; -use identity_stardust::StardustDocument; -use identity_stardust::StardustIdentityClientExt; -use identity_stardust::StardustVerificationMethod; +use identity_iota::crypto::KeyPair; +use identity_iota::crypto::KeyType; +use identity_iota::did::MethodScope; +use identity_iota::iota::block::output::AliasId; +use identity_iota::iota::block::output::UnlockCondition; +use identity_iota::iota::IotaClientExt; +use identity_iota::iota::IotaDID; +use identity_iota::iota::IotaDocument; +use identity_iota::iota::IotaIdentityClientExt; +use identity_iota::iota::IotaVerificationMethod; +use identity_iota::iota::NetworkName; use iota_client::block::address::Address; use iota_client::block::address::AliasAddress; use iota_client::block::output::feature::IssuerFeature; @@ -47,14 +47,14 @@ async fn main() -> anyhow::Result<()> { ); // Create a new DID for the company. - let (_, company_did): (Address, StardustDID) = create_did(&client, &mut secret_manager).await?; + let (_, company_did): (Address, IotaDID) = create_did(&client, &mut secret_manager).await?; // Get the current byte costs and network name. let rent_structure: RentStructure = client.get_rent_structure().await?; let network_name: NetworkName = client.network_name().await?; // Construct a new DID document for the subsidiary. - let subsidiary_document: StardustDocument = StardustDocument::new(&network_name); + let subsidiary_document: IotaDocument = IotaDocument::new(&network_name); // Create a DID for the subsidiary that is controlled by the parent company's DID. // This means the subsidiary's Alias Output can only be updated or destroyed by @@ -77,7 +77,7 @@ async fn main() -> anyhow::Result<()> { .finish()?; // Publish the subsidiary's DID. - let mut subsidiary_document: StardustDocument = client.publish_did_output(&secret_manager, subsidiary_alias).await?; + let mut subsidiary_document: IotaDocument = client.publish_did_output(&secret_manager, subsidiary_alias).await?; // ===================================== // Update the subsidiary's Alias Output. @@ -86,7 +86,7 @@ async fn main() -> anyhow::Result<()> { // Add a verification method to the subsidiary. // This only serves as an example for updating the subsidiary DID. let keypair: KeyPair = KeyPair::new(KeyType::Ed25519)?; - let method: StardustVerificationMethod = StardustVerificationMethod::new( + let method: IotaVerificationMethod = IotaVerificationMethod::new( subsidiary_document.id().clone(), keypair.type_(), keypair.public(), @@ -105,7 +105,7 @@ async fn main() -> anyhow::Result<()> { // // This works because `secret_manager` can unlock the company's Alias Output, // which is required in order to update the subsidiary's Alias Output. - let subsidiary_document: StardustDocument = client.publish_did_output(&secret_manager, subsidiary_alias).await?; + let subsidiary_document: IotaDocument = client.publish_did_output(&secret_manager, subsidiary_alias).await?; // =================================================================== // Determine the controlling company's DID given the subsidiary's DID. @@ -131,10 +131,10 @@ async fn main() -> anyhow::Result<()> { }; // Reconstruct the company's DID from the Alias Id and the network. - let company_did = StardustDID::new(company_alias_id.deref(), &network_name); + let company_did = IotaDID::new(company_alias_id.deref(), &network_name); // Resolve the company's DID document. - let company_document: StardustDocument = client.resolve_did(&company_did).await?; + let company_document: IotaDocument = client.resolve_did(&company_did).await?; println!("Company: {company_document:#}"); println!("Subsidiary: {subsidiary_document:#}"); diff --git a/examples/1_advanced/1_did_issues_nft.rs b/examples/1_advanced/1_did_issues_nft.rs index 796d70b4cb..230f76ce42 100644 --- a/examples/1_advanced/1_did_issues_nft.rs +++ b/examples/1_advanced/1_did_issues_nft.rs @@ -4,11 +4,11 @@ use examples::create_did; use examples::random_stronghold_path; use examples::NETWORK_ENDPOINT; -use identity_stardust::block::output::feature::MetadataFeature; -use identity_stardust::NetworkName; -use identity_stardust::StardustDID; -use identity_stardust::StardustDocument; -use identity_stardust::StardustIdentityClientExt; +use identity_iota::iota::block::output::feature::MetadataFeature; +use identity_iota::iota::IotaDID; +use identity_iota::iota::IotaDocument; +use identity_iota::iota::IotaIdentityClientExt; +use identity_iota::iota::NetworkName; use iota_client::api_types::responses::OutputResponse; use iota_client::block::address::Address; use iota_client::block::address::AliasAddress; @@ -52,7 +52,7 @@ async fn main() -> anyhow::Result<()> { ); // Create a new DID for the manufacturer. - let (_, manufacturer_did): (Address, StardustDID) = create_did(&client, &mut secret_manager).await?; + let (_, manufacturer_did): (Address, IotaDID) = create_did(&client, &mut secret_manager).await?; // Get the current byte cost. let rent_structure: RentStructure = client.get_rent_structure().await?; @@ -120,10 +120,10 @@ async fn main() -> anyhow::Result<()> { // Reconstruct the manufacturer's DID from the Alias Id. let network: NetworkName = client.network_name().await?; - let manufacturer_did: StardustDID = StardustDID::new(&*manufacturer_alias_id, &network); + let manufacturer_did: IotaDID = IotaDID::new(&*manufacturer_alias_id, &network); // Resolve the issuer of the NFT. - let manufacturer_document: StardustDocument = client.resolve_did(&manufacturer_did).await?; + let manufacturer_document: IotaDocument = client.resolve_did(&manufacturer_did).await?; println!("The issuer of the Digital Product Passport NFT is: {manufacturer_document:#}"); diff --git a/examples/1_advanced/2_nft_owns_did.rs b/examples/1_advanced/2_nft_owns_did.rs index bb6f663f81..7f934b7757 100644 --- a/examples/1_advanced/2_nft_owns_did.rs +++ b/examples/1_advanced/2_nft_owns_did.rs @@ -5,12 +5,12 @@ use examples::create_did_document; use examples::get_address_with_funds; use examples::random_stronghold_path; use examples::NETWORK_ENDPOINT; -use identity_stardust::block::address::NftAddress; -use identity_stardust::block::output::AliasOutput; -use identity_stardust::NetworkName; -use identity_stardust::StardustClientExt; -use identity_stardust::StardustDocument; -use identity_stardust::StardustIdentityClientExt; +use identity_iota::iota::block::address::NftAddress; +use identity_iota::iota::block::output::AliasOutput; +use identity_iota::iota::IotaClientExt; +use identity_iota::iota::IotaDocument; +use identity_iota::iota::IotaIdentityClientExt; +use identity_iota::iota::NetworkName; use iota_client::api_types::responses::OutputResponse; use iota_client::block::address::Address; use iota_client::block::output::unlock_condition::AddressUnlockCondition; @@ -78,14 +78,14 @@ async fn main() -> anyhow::Result<()> { let network: NetworkName = client.network_name().await?; // Construct a DID document for the subsidiary. - let document: StardustDocument = create_did_document(&network)?; + let document: IotaDocument = create_did_document(&network)?; // Create a new DID for the car that is owned by the car NFT. let car_did_output: AliasOutput = client .new_did_output(Address::Nft(car_nft_id.into()), document, Some(rent_structure)) .await?; - let car_document: StardustDocument = client.publish_did_output(&secret_manager, car_did_output).await?; + let car_document: IotaDocument = client.publish_did_output(&secret_manager, car_did_output).await?; // ============================================ // Determine the car's NFT given the car's DID. diff --git a/examples/1_advanced/3_did_issues_tokens.rs b/examples/1_advanced/3_did_issues_tokens.rs index b6ebaa8952..d0601e2ab9 100644 --- a/examples/1_advanced/3_did_issues_tokens.rs +++ b/examples/1_advanced/3_did_issues_tokens.rs @@ -7,18 +7,18 @@ use examples::create_did; use examples::get_address; use examples::random_stronghold_path; use examples::NETWORK_ENDPOINT; -use identity_core::common::Duration; -use identity_core::common::Timestamp; -use identity_stardust::block::output::unlock_condition::AddressUnlockCondition; -use identity_stardust::block::output::unlock_condition::ExpirationUnlockCondition; -use identity_stardust::block::output::BasicOutput; -use identity_stardust::block::output::BasicOutputBuilder; -use identity_stardust::block::output::Output; -use identity_stardust::block::output::OutputId; -use identity_stardust::NetworkName; -use identity_stardust::StardustDID; -use identity_stardust::StardustDocument; -use identity_stardust::StardustIdentityClientExt; +use identity_iota::core::Duration; +use identity_iota::core::Timestamp; +use identity_iota::iota::block::output::unlock_condition::AddressUnlockCondition; +use identity_iota::iota::block::output::unlock_condition::ExpirationUnlockCondition; +use identity_iota::iota::block::output::BasicOutput; +use identity_iota::iota::block::output::BasicOutputBuilder; +use identity_iota::iota::block::output::Output; +use identity_iota::iota::block::output::OutputId; +use identity_iota::iota::IotaDID; +use identity_iota::iota::IotaDocument; +use identity_iota::iota::IotaIdentityClientExt; +use identity_iota::iota::NetworkName; use iota_client::api_types::responses::OutputResponse; use iota_client::block::address::Address; use iota_client::block::address::AliasAddress; @@ -64,14 +64,14 @@ async fn main() -> anyhow::Result<()> { // Create a new DID for the authority. - let (_, authority_did): (Address, StardustDID) = create_did(&client, &mut secret_manager).await?; + let (_, authority_did): (Address, IotaDID) = create_did(&client, &mut secret_manager).await?; let rent_structure: RentStructure = client.get_rent_structure().await?; // We want to update the foundry counter of the authority's Alias Output, so we create an // updated version of the output. We pass in the previous document, // because we don't want to modify it in this update. - let authority_document: StardustDocument = client.resolve_did(&authority_did).await?; + let authority_document: IotaDocument = client.resolve_did(&authority_did).await?; let authority_alias_output: AliasOutput = client.update_did_output(authority_document).await?; // We will add one foundry to this Alias Output. @@ -135,10 +135,10 @@ async fn main() -> anyhow::Result<()> { // Reconstruct the DID of the authority. let network: NetworkName = client.network_name().await?; - let authority_did: StardustDID = StardustDID::new(authority_alias_id.deref(), &network); + let authority_did: IotaDID = IotaDID::new(authority_alias_id.deref(), &network); // Resolve the authority's DID document. - let authority_document: StardustDocument = client.resolve_did(&authority_did).await?; + let authority_document: IotaDocument = client.resolve_did(&authority_did).await?; println!("The authority's DID is: {authority_document:#}"); diff --git a/examples/1_advanced/4_key_exchange.rs b/examples/1_advanced/4_key_exchange.rs index a14877d391..230e2378cd 100644 --- a/examples/1_advanced/4_key_exchange.rs +++ b/examples/1_advanced/4_key_exchange.rs @@ -5,19 +5,19 @@ use anyhow::Context; use examples::get_address_with_funds; use examples::random_stronghold_path; use examples::NETWORK_ENDPOINT; -use identity_core::crypto::KeyPair; -use identity_core::crypto::KeyType; -use identity_core::crypto::X25519; -use identity_did::verification::MethodScope; -use identity_stardust::block::address::Address; -use identity_stardust::block::output::AliasOutput; -use identity_stardust::block::output::RentStructure; -use identity_stardust::NetworkName; -use identity_stardust::StardustClientExt; -use identity_stardust::StardustDID; -use identity_stardust::StardustDocument; -use identity_stardust::StardustIdentityClientExt; -use identity_stardust::StardustVerificationMethod; +use identity_iota::crypto::KeyPair; +use identity_iota::crypto::KeyType; +use identity_iota::crypto::X25519; +use identity_iota::did::MethodScope; +use identity_iota::iota::block::address::Address; +use identity_iota::iota::block::output::AliasOutput; +use identity_iota::iota::block::output::RentStructure; +use identity_iota::iota::IotaClientExt; +use identity_iota::iota::IotaDID; +use identity_iota::iota::IotaDocument; +use identity_iota::iota::IotaIdentityClientExt; +use identity_iota::iota::IotaVerificationMethod; +use identity_iota::iota::NetworkName; use iota_client::secret::stronghold::StrongholdSecretManager; use iota_client::secret::SecretManager; use iota_client::Client; @@ -52,41 +52,41 @@ async fn main() -> anyhow::Result<()> { let rent_structure: RentStructure = client.get_rent_structure().await?; // Alice creates and publishes their DID Document. - let (alice_did, alice_x25519): (StardustDID, KeyPair) = { + let (alice_did, alice_x25519): (IotaDID, KeyPair) = { // Create a DID Document. - let mut alice_document: StardustDocument = StardustDocument::new(&network); + let mut alice_document: IotaDocument = IotaDocument::new(&network); // Insert a new X25519 KeyAgreement verification method. let x25519: KeyPair = KeyPair::new(KeyType::X25519)?; - let method: StardustVerificationMethod = - StardustVerificationMethod::new(alice_document.id().clone(), KeyType::X25519, x25519.public(), "kex-0")?; + let method: IotaVerificationMethod = + IotaVerificationMethod::new(alice_document.id().clone(), KeyType::X25519, x25519.public(), "kex-0")?; alice_document.insert_method(method, MethodScope::key_agreement())?; // Publish the DID. let alice_output: AliasOutput = client .new_did_output(address, alice_document, Some(rent_structure.clone())) .await?; - let alice_document: StardustDocument = client.publish_did_output(&secret_manager, alice_output).await?; + let alice_document: IotaDocument = client.publish_did_output(&secret_manager, alice_output).await?; (alice_document.id().clone(), x25519) }; // Bob creates and publishes their DID Document. - let (bob_did, bob_x25519): (StardustDID, KeyPair) = { + let (bob_did, bob_x25519): (IotaDID, KeyPair) = { // Create a DID Document. - let mut bob_document: StardustDocument = StardustDocument::new(&network); + let mut bob_document: IotaDocument = IotaDocument::new(&network); // Insert a new X25519 KeyAgreement verification method. let x25519: KeyPair = KeyPair::new(KeyType::X25519)?; - let method: StardustVerificationMethod = - StardustVerificationMethod::new(bob_document.id().clone(), KeyType::X25519, x25519.public(), "kex-0")?; + let method: IotaVerificationMethod = + IotaVerificationMethod::new(bob_document.id().clone(), KeyType::X25519, x25519.public(), "kex-0")?; bob_document.insert_method(method, MethodScope::key_agreement())?; // Publish the DID. let bob_output: AliasOutput = client .new_did_output(address, bob_document, Some(rent_structure)) .await?; - let bob_document: StardustDocument = client.publish_did_output(&secret_manager, bob_output).await?; + let bob_document: IotaDocument = client.publish_did_output(&secret_manager, bob_output).await?; (bob_document.id().clone(), x25519) }; @@ -99,8 +99,8 @@ async fn main() -> anyhow::Result<()> { let alice_shared_secret_key: [u8; 32] = { // Alice: resolves Bob's DID Document and extracts their public key. - let bob_document: StardustDocument = client.resolve_did(&bob_did).await?; - let bob_method: &StardustVerificationMethod = bob_document + let bob_document: IotaDocument = client.resolve_did(&bob_did).await?; + let bob_method: &IotaVerificationMethod = bob_document .core_document() .resolve_method("kex-0", Some(MethodScope::key_agreement())) .unwrap(); @@ -112,8 +112,8 @@ async fn main() -> anyhow::Result<()> { let bob_shared_secret_key: [u8; 32] = { // Bob: resolves Alice's DID Document and extracts their public key. - let alice_document: StardustDocument = client.resolve_did(&alice_did).await?; - let alice_method: &StardustVerificationMethod = alice_document + let alice_document: IotaDocument = client.resolve_did(&alice_did).await?; + let alice_method: &IotaVerificationMethod = alice_document .core_document() .resolve_method("kex-0", Some(MethodScope::key_agreement())) .unwrap(); diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 28bc00a478..f6073d2923 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -7,10 +7,7 @@ publish = false [dependencies] anyhow = "1.0.62" -identity_core = { path = "../identity_core" } -identity_did = { path = "../identity_did" } -identity_resolver = { path = "../identity_resolver" } -identity_stardust = { path = "../identity_stardust" } +identity_iota = { path = "../identity_iota" } iota-client = { version = "2.0.0-beta.3", default-features = false, features = ["tls", "stronghold"] } primitive-types = "0.11.1" rand = "0.8.5" diff --git a/examples/utils/utils.rs b/examples/utils/utils.rs index 932a8b56bd..79f6870c6b 100644 --- a/examples/utils/utils.rs +++ b/examples/utils/utils.rs @@ -2,15 +2,15 @@ use std::path::PathBuf; use anyhow::Context; -use identity_core::crypto::KeyPair; -use identity_core::crypto::KeyType; -use identity_did::verification::MethodScope; -use identity_stardust::NetworkName; -use identity_stardust::StardustClientExt; -use identity_stardust::StardustDID; -use identity_stardust::StardustDocument; -use identity_stardust::StardustIdentityClientExt; -use identity_stardust::StardustVerificationMethod; +use identity_iota::crypto::KeyPair; +use identity_iota::crypto::KeyType; +use identity_iota::did::MethodScope; +use identity_iota::iota::IotaClientExt; +use identity_iota::iota::IotaDID; +use identity_iota::iota::IotaDocument; +use identity_iota::iota::IotaIdentityClientExt; +use identity_iota::iota::IotaVerificationMethod; +use identity_iota::iota::NetworkName; use iota_client::block::address::Address; use iota_client::block::output::AliasOutput; @@ -28,18 +28,18 @@ pub static FAUCET_URL: &str = "https://faucet.testnet.shimmer.network/api/enqueu /// /// Its functionality is equivalent to the "create DID" example /// and exists for convenient calling from the other examples. -pub async fn create_did(client: &Client, secret_manager: &mut SecretManager) -> anyhow::Result<(Address, StardustDID)> { +pub async fn create_did(client: &Client, secret_manager: &mut SecretManager) -> anyhow::Result<(Address, IotaDID)> { let address: Address = get_address_with_funds(client, secret_manager) .await .context("failed to get address with funds")?; let network_name: NetworkName = client.network_name().await?; - let document: StardustDocument = create_did_document(&network_name)?; + let document: IotaDocument = create_did_document(&network_name)?; let alias_output: AliasOutput = client.new_did_output(address, document, None).await?; - let document: StardustDocument = client.publish_did_output(secret_manager, alias_output).await?; + let document: IotaDocument = client.publish_did_output(secret_manager, alias_output).await?; Ok((address, document.id().clone())) } @@ -48,13 +48,13 @@ pub async fn create_did(client: &Client, secret_manager: &mut SecretManager) -> /// /// Its functionality is equivalent to the "create DID" example /// and exists for convenient calling from the other examples. -pub fn create_did_document(network_name: &NetworkName) -> anyhow::Result { - let mut document: StardustDocument = StardustDocument::new(network_name); +pub fn create_did_document(network_name: &NetworkName) -> anyhow::Result { + let mut document: IotaDocument = IotaDocument::new(network_name); let keypair: KeyPair = KeyPair::new(KeyType::Ed25519)?; - let method: StardustVerificationMethod = - StardustVerificationMethod::new(document.id().clone(), keypair.type_(), keypair.public(), "#key-1")?; + let method: IotaVerificationMethod = + IotaVerificationMethod::new(document.id().clone(), keypair.type_(), keypair.public(), "#key-1")?; document.insert_method(method, MethodScope::VerificationMethod)?; @@ -75,7 +75,7 @@ pub async fn get_address_with_funds(client: &Client, stronghold: &mut SecretMana /// Initializes the [`SecretManager`] with a new mnemonic, if necessary, /// and generates an address from the given [`SecretManager`]. pub async fn get_address(client: &Client, secret_manager: &mut SecretManager) -> anyhow::Result
{ - let keypair = identity_core::crypto::KeyPair::new(KeyType::Ed25519)?; + let keypair = KeyPair::new(KeyType::Ed25519)?; let mnemonic = iota_client::crypto::keys::bip39::wordlist::encode(keypair.private().as_ref(), &bip39::wordlist::ENGLISH) .map_err(|err| anyhow::anyhow!(format!("{err:?}")))?; diff --git a/examples_legacy/Cargo.toml b/examples_legacy/Cargo.toml index 07e7ab1acd..d561521a1f 100644 --- a/examples_legacy/Cargo.toml +++ b/examples_legacy/Cargo.toml @@ -5,7 +5,11 @@ edition = "2021" publish = false [dependencies] +identity_account = { path = "../identity_account" } +identity_account_storage = { path = "../identity_account_storage" } identity_iota = { path = "../identity_iota" } +identity_iota_client_legacy = { path = "../identity_iota_client_legacy" } +identity_iota_core_legacy = { path = "../identity_iota_core_legacy" } pretty_env_logger = { version = "0.4" } rand = { version = "0.8" } tokio = { version = "1.17.0", features = ["full"] } diff --git a/examples_legacy/account/config.rs b/examples_legacy/account/config.rs index 014b6d59f3..1151d986c3 100644 --- a/examples_legacy/account/config.rs +++ b/examples_legacy/account/config.rs @@ -3,16 +3,16 @@ //! cargo run --example account_config -use identity_iota::account::Account; -use identity_iota::account::AccountBuilder; -use identity_iota::account::AutoSave; -use identity_iota::account::IdentitySetup; -use identity_iota::account::Result; -use identity_iota::account_storage::MemStore; -use identity_iota::client::ClientBuilder; -use identity_iota::client::ExplorerUrl; -use identity_iota::iota_core::IotaDID; -use identity_iota::iota_core::Network; +use identity_account::account::Account; +use identity_account::account::AccountBuilder; +use identity_account::account::AutoSave; +use identity_account::types::IdentitySetup; +use identity_account::Result; +use identity_account_storage::storage::MemStore; +use identity_iota_client_legacy::tangle::ClientBuilder; +use identity_iota_client_legacy::tangle::ExplorerUrl; +use identity_iota_core_legacy::did::IotaDID; +use identity_iota_core_legacy::tangle::Network; #[tokio::main] async fn main() -> Result<()> { diff --git a/examples_legacy/account/create_did.rs b/examples_legacy/account/create_did.rs index ce99e7931e..e442b24d60 100644 --- a/examples_legacy/account/create_did.rs +++ b/examples_legacy/account/create_did.rs @@ -5,12 +5,12 @@ use std::path::PathBuf; -use identity_iota::account::Account; -use identity_iota::account::IdentitySetup; -use identity_iota::account::Result; -use identity_iota::account_storage::Stronghold; -use identity_iota::client::ExplorerUrl; -use identity_iota::iota_core::IotaDID; +use identity_account::account::Account; +use identity_account::types::IdentitySetup; +use identity_account::Result; +use identity_account_storage::stronghold::Stronghold; +use identity_iota_client_legacy::tangle::ExplorerUrl; +use identity_iota_core_legacy::did::IotaDID; #[tokio::main] async fn main() -> Result<()> { diff --git a/examples_legacy/account/create_vc.rs b/examples_legacy/account/create_vc.rs index fdf432ae43..48282ae826 100644 --- a/examples_legacy/account/create_vc.rs +++ b/examples_legacy/account/create_vc.rs @@ -7,11 +7,11 @@ //! //! cargo run --example account_create_vc -use identity_iota::account::Account; -use identity_iota::account::AccountBuilder; -use identity_iota::account::IdentitySetup; -use identity_iota::account::MethodContent; -use identity_iota::account::Result; +use identity_account::account::Account; +use identity_account::account::AccountBuilder; +use identity_account::types::IdentitySetup; +use identity_account::types::MethodContent; +use identity_account::Result; use identity_iota::core::json; use identity_iota::core::FromJson; use identity_iota::core::ToJson; diff --git a/examples_legacy/account/create_vp.rs b/examples_legacy/account/create_vp.rs index 8534195661..24288208e4 100644 --- a/examples_legacy/account/create_vp.rs +++ b/examples_legacy/account/create_vp.rs @@ -6,12 +6,11 @@ //! //! cargo run --example account_create_vp -use identity_iota::account::Account; -use identity_iota::account::AccountBuilder; -use identity_iota::account::IdentitySetup; -use identity_iota::account::MethodContent; -use identity_iota::account::Result; -use identity_iota::client::Resolver; +use identity_account::account::Account; +use identity_account::account::AccountBuilder; +use identity_account::types::IdentitySetup; +use identity_account::types::MethodContent; +use identity_account::Result; use identity_iota::core::json; use identity_iota::core::Duration; use identity_iota::core::FromJson; @@ -29,6 +28,7 @@ use identity_iota::credential::Subject; use identity_iota::credential::SubjectHolderRelationship; use identity_iota::crypto::ProofOptions; use identity_iota::did::verifiable::VerifierOptions; +use identity_iota_client_legacy::tangle::Resolver; #[tokio::main] async fn main() -> Result<()> { diff --git a/examples_legacy/account/encryption.rs b/examples_legacy/account/encryption.rs index 0e91712b01..91f6f91042 100644 --- a/examples_legacy/account/encryption.rs +++ b/examples_legacy/account/encryption.rs @@ -7,20 +7,20 @@ use std::path::PathBuf; -use identity_iota::account::Account; -use identity_iota::account::AccountBuilder; -use identity_iota::account::IdentitySetup; -use identity_iota::account::MethodContent; -use identity_iota::account::Result; -use identity_iota::account_storage::AgreementInfo; -use identity_iota::account_storage::CekAlgorithm; -use identity_iota::account_storage::EncryptedData; -use identity_iota::account_storage::EncryptionAlgorithm; -use identity_iota::account_storage::Stronghold; -use identity_iota::client::ResolvedIotaDocument; -use identity_iota::client::Resolver; +use identity_account::account::Account; +use identity_account::account::AccountBuilder; +use identity_account::types::IdentitySetup; +use identity_account::types::MethodContent; +use identity_account::Result; +use identity_account_storage::stronghold::Stronghold; +use identity_account_storage::types::AgreementInfo; +use identity_account_storage::types::CekAlgorithm; +use identity_account_storage::types::EncryptedData; +use identity_account_storage::types::EncryptionAlgorithm; use identity_iota::did::MethodScope; -use identity_iota::iota_core::IotaVerificationMethod; +use identity_iota_client_legacy::document::ResolvedIotaDocument; +use identity_iota_client_legacy::tangle::Resolver; +use identity_iota_core_legacy::document::IotaVerificationMethod; #[tokio::main] async fn main() -> Result<()> { diff --git a/examples_legacy/account/lazy.rs b/examples_legacy/account/lazy.rs index a69941c935..4e95b56815 100644 --- a/examples_legacy/account/lazy.rs +++ b/examples_legacy/account/lazy.rs @@ -4,13 +4,13 @@ //! cargo run --example account_lazy use std::path::PathBuf; -use identity_iota::account::Account; -use identity_iota::account::IdentitySetup; -use identity_iota::account::Result; -use identity_iota::account_storage::Stronghold; -use identity_iota::client::ExplorerUrl; +use identity_account::account::Account; +use identity_account::types::IdentitySetup; +use identity_account::Result; +use identity_account_storage::stronghold::Stronghold; use identity_iota::core::Url; -use identity_iota::iota_core::IotaDID; +use identity_iota_client_legacy::tangle::ExplorerUrl; +use identity_iota_core_legacy::did::IotaDID; #[tokio::main] async fn main() -> Result<()> { diff --git a/examples_legacy/account/manipulate_did.rs b/examples_legacy/account/manipulate_did.rs index 25e6bab742..46f3b163db 100644 --- a/examples_legacy/account/manipulate_did.rs +++ b/examples_legacy/account/manipulate_did.rs @@ -5,15 +5,15 @@ use std::path::PathBuf; -use identity_iota::account::Account; -use identity_iota::account::IdentitySetup; -use identity_iota::account::MethodContent; -use identity_iota::account::Result; -use identity_iota::account_storage::Stronghold; -use identity_iota::client::ExplorerUrl; +use identity_account::account::Account; +use identity_account::types::IdentitySetup; +use identity_account::types::MethodContent; +use identity_account::Result; +use identity_account_storage::stronghold::Stronghold; use identity_iota::core::Url; use identity_iota::did::MethodRelationship; -use identity_iota::iota_core::IotaDID; +use identity_iota_client_legacy::tangle::ExplorerUrl; +use identity_iota_core_legacy::did::IotaDID; #[tokio::main] async fn main() -> Result<()> { diff --git a/examples_legacy/account/multiple_identities.rs b/examples_legacy/account/multiple_identities.rs index 0ce420c247..b1d1b6e50c 100644 --- a/examples_legacy/account/multiple_identities.rs +++ b/examples_legacy/account/multiple_identities.rs @@ -11,8 +11,8 @@ use identity_iota::account::IdentitySetup; use identity_iota::account::MethodContent; use identity_iota::account::Result; use identity_iota::account_storage::Stronghold; -use identity_iota::client::ExplorerUrl; -use identity_iota::iota_core::IotaDID; +use identity_iota_client_legacy::tangle::ExplorerUrl; +use identity_iota::iota::IotaDID; #[tokio::main] async fn main() -> Result<()> { diff --git a/examples_legacy/account/revoke_vc.rs b/examples_legacy/account/revoke_vc.rs index 3154c345ea..062129b174 100644 --- a/examples_legacy/account/revoke_vc.rs +++ b/examples_legacy/account/revoke_vc.rs @@ -12,13 +12,11 @@ //! //! cargo run --example account_revoke_vc -use identity_iota::account::Account; -use identity_iota::account::AccountBuilder; -use identity_iota::account::IdentitySetup; -use identity_iota::account::MethodContent; -use identity_iota::account::Result; -use identity_iota::client::ResolvedIotaDocument; -use identity_iota::client::Resolver; +use identity_account::account::Account; +use identity_account::account::AccountBuilder; +use identity_account::types::IdentitySetup; +use identity_account::types::MethodContent; +use identity_account::Result; use identity_iota::core::json; use identity_iota::core::FromJson; use identity_iota::core::Url; @@ -34,6 +32,8 @@ use identity_iota::credential::ValidationError; use identity_iota::crypto::ProofOptions; use identity_iota::did::RevocationBitmap; use identity_iota::did::DID; +use identity_iota_client_legacy::document::ResolvedIotaDocument; +use identity_iota_client_legacy::tangle::Resolver; #[tokio::main] async fn main() -> Result<()> { diff --git a/examples_legacy/account/signing.rs b/examples_legacy/account/signing.rs index 3c848d69c3..458bb109dd 100644 --- a/examples_legacy/account/signing.rs +++ b/examples_legacy/account/signing.rs @@ -5,13 +5,11 @@ use std::path::PathBuf; -use identity_iota::account::Account; -use identity_iota::account::IdentitySetup; -use identity_iota::account::MethodContent; -use identity_iota::account::Result; -use identity_iota::account_storage::Stronghold; -use identity_iota::client::ExplorerUrl; -use identity_iota::client::ResolvedIotaDocument; +use identity_account::account::Account; +use identity_account::types::IdentitySetup; +use identity_account::types::MethodContent; +use identity_account::Result; +use identity_account_storage::stronghold::Stronghold; use identity_iota::core::json; use identity_iota::core::FromJson; use identity_iota::core::Url; @@ -21,8 +19,10 @@ use identity_iota::crypto::KeyPair; use identity_iota::crypto::ProofOptions; use identity_iota::did::verifiable::VerifierOptions; use identity_iota::did::DID; -use identity_iota::iota_core::IotaDID; use identity_iota::prelude::*; +use identity_iota_client_legacy::document::ResolvedIotaDocument; +use identity_iota_client_legacy::tangle::ExplorerUrl; +use identity_iota_core_legacy::did::IotaDID; #[tokio::main] async fn main() -> Result<()> { diff --git a/examples_legacy/account/unchecked.rs b/examples_legacy/account/unchecked.rs index 8f1f3a7892..94845ab8f1 100644 --- a/examples_legacy/account/unchecked.rs +++ b/examples_legacy/account/unchecked.rs @@ -5,14 +5,14 @@ use std::path::PathBuf; -use identity_iota::account::Account; -use identity_iota::account::IdentitySetup; -use identity_iota::account::Result; -use identity_iota::account_storage::Stronghold; -use identity_iota::client::ExplorerUrl; +use identity_account::account::Account; +use identity_account::types::IdentitySetup; +use identity_account::Result; +use identity_account_storage::stronghold::Stronghold; use identity_iota::core::Timestamp; -use identity_iota::iota_core::IotaDID; -use identity_iota::prelude::IotaDocument; +use identity_iota_client_legacy::tangle::ExplorerUrl; +use identity_iota_core_legacy::did::IotaDID; +use identity_iota_core_legacy::document::IotaDocument; #[tokio::main] async fn main() -> Result<()> { diff --git a/examples_legacy/getting_started.rs b/examples_legacy/getting_started.rs index 4368b5b1d4..84f3cad8ef 100644 --- a/examples_legacy/getting_started.rs +++ b/examples_legacy/getting_started.rs @@ -5,7 +5,7 @@ //! //! cargo run --example getting_started -use identity_iota::client::Result; +use identity_iota_client_legacy::Result; #[tokio::main] async fn main() -> Result<()> { diff --git a/examples_legacy/low-level-api/common.rs b/examples_legacy/low-level-api/common.rs index f3cf3a804b..10c0d49b96 100644 --- a/examples_legacy/low-level-api/common.rs +++ b/examples_legacy/low-level-api/common.rs @@ -14,8 +14,8 @@ use identity_iota::credential::CredentialBuilder; use identity_iota::credential::Subject; use identity_iota::did::MethodScope; use identity_iota::did::DID; -use identity_iota::client::Receipt; -use identity_iota::iota_core::IotaVerificationMethod; +use identity_iota_client_legacy::Receipt; +use identity_iota::iota::IotaVerificationMethod; use identity_iota::prelude::*; /// Helper that takes two DID Documents (identities) for issuer and subject, and diff --git a/examples_legacy/low-level-api/create_did.rs b/examples_legacy/low-level-api/create_did.rs index 1436646873..20ef61313f 100644 --- a/examples_legacy/low-level-api/create_did.rs +++ b/examples_legacy/low-level-api/create_did.rs @@ -6,9 +6,12 @@ //! //! cargo run --example create_did -use identity_iota::client::ExplorerUrl; -use identity_iota::client::Receipt; use identity_iota::prelude::*; +use identity_iota_client_legacy::tangle::Client; +use identity_iota_client_legacy::tangle::ExplorerUrl; +use identity_iota_client_legacy::tangle::Receipt; +use identity_iota_client_legacy::Result; +use identity_iota_core_legacy::document::IotaDocument; pub async fn run() -> Result<(IotaDocument, KeyPair, Receipt)> { // Create a client instance to send messages to the Tangle. diff --git a/examples_legacy/low-level-api/key_exchange.rs b/examples_legacy/low-level-api/key_exchange.rs index bcd7598ea3..2ca9f5e961 100644 --- a/examples_legacy/low-level-api/key_exchange.rs +++ b/examples_legacy/low-level-api/key_exchange.rs @@ -5,15 +5,18 @@ //! //! cargo run --example key_exchange -use identity_iota::client::Receipt; -use identity_iota::client::ResolvedIotaDocument; -use identity_iota::client::TangleResolve; use identity_iota::crypto::KeyType; use identity_iota::crypto::X25519; use identity_iota::did::MethodScope; -use identity_iota::iota_core::IotaDID; -use identity_iota::iota_core::IotaVerificationMethod; use identity_iota::prelude::*; +use identity_iota_client_legacy::document::ResolvedIotaDocument; +use identity_iota_client_legacy::tangle::Client; +use identity_iota_client_legacy::tangle::Receipt; +use identity_iota_client_legacy::tangle::TangleResolve; +use identity_iota_client_legacy::Result; +use identity_iota_core_legacy::did::IotaDID; +use identity_iota_core_legacy::document::IotaDocument; +use identity_iota_core_legacy::document::IotaVerificationMethod; mod create_did; diff --git a/examples_legacy/low-level-api/manipulate_did.rs b/examples_legacy/low-level-api/manipulate_did.rs index 7aaef0fca4..f32a867bf8 100644 --- a/examples_legacy/low-level-api/manipulate_did.rs +++ b/examples_legacy/low-level-api/manipulate_did.rs @@ -6,17 +6,20 @@ //! //! cargo run --example update_did -use identity_iota::client::ExplorerUrl; -use identity_iota::client::Receipt; use identity_iota::core::json; use identity_iota::core::FromJson; use identity_iota::core::Timestamp; use identity_iota::did::MethodScope; use identity_iota::did::Service; use identity_iota::did::DID; -use identity_iota::iota_core::IotaService; -use identity_iota::iota_core::IotaVerificationMethod; use identity_iota::prelude::*; +use identity_iota_client_legacy::tangle::Client; +use identity_iota_client_legacy::tangle::ExplorerUrl; +use identity_iota_client_legacy::tangle::Receipt; +use identity_iota_client_legacy::Result; +use identity_iota_core_legacy::document::IotaDocument; +use identity_iota_core_legacy::document::IotaService; +use identity_iota_core_legacy::document::IotaVerificationMethod; mod create_did; diff --git a/examples_legacy/low-level-api/private_tangle.rs b/examples_legacy/low-level-api/private_tangle.rs index 3b889a9c35..0edfee5f5a 100644 --- a/examples_legacy/low-level-api/private_tangle.rs +++ b/examples_legacy/low-level-api/private_tangle.rs @@ -9,13 +9,16 @@ //! //! cargo run --example private_tangle -use identity_iota::client::ClientBuilder; -use identity_iota::client::DIDMessageEncoding; -use identity_iota::client::ExplorerUrl; -use identity_iota::client::Receipt; use identity_iota::crypto::KeyType; -use identity_iota::iota_core::Network; use identity_iota::prelude::*; +use identity_iota_client_legacy::tangle::Client; +use identity_iota_client_legacy::tangle::ClientBuilder; +use identity_iota_client_legacy::tangle::DIDMessageEncoding; +use identity_iota_client_legacy::tangle::ExplorerUrl; +use identity_iota_client_legacy::tangle::Receipt; +use identity_iota_client_legacy::Result; +use identity_iota_core_legacy::document::IotaDocument; +use identity_iota_core_legacy::tangle::Network; #[tokio::main] pub async fn main() -> Result<()> { diff --git a/examples_legacy/low-level-api/resolve_did.rs b/examples_legacy/low-level-api/resolve_did.rs index d57ca54834..8564753e4d 100644 --- a/examples_legacy/low-level-api/resolve_did.rs +++ b/examples_legacy/low-level-api/resolve_did.rs @@ -8,11 +8,13 @@ //! //! cargo run --example resolve_did -use identity_iota::client::Receipt; -use identity_iota::client::ResolvedIotaDocument; -use identity_iota::client::Resolver; -use identity_iota::iota_core::IotaDID; use identity_iota::prelude::*; +use identity_iota_client_legacy::document::ResolvedIotaDocument; +use identity_iota_client_legacy::tangle::Receipt; +use identity_iota_client_legacy::tangle::Resolver; +use identity_iota_client_legacy::Result; +use identity_iota_core_legacy::did::IotaDID; +use identity_iota_core_legacy::document::IotaDocument; mod create_did; diff --git a/examples_legacy/low-level-api/resolve_history.rs b/examples_legacy/low-level-api/resolve_history.rs index 3c301b2524..231a5a6912 100644 --- a/examples_legacy/low-level-api/resolve_history.rs +++ b/examples_legacy/low-level-api/resolve_history.rs @@ -6,10 +6,6 @@ //! //! cargo run --example did_history -use identity_iota::client::Client; -use identity_iota::client::DocumentHistory; -use identity_iota::client::Receipt; -use identity_iota::client::Result; use identity_iota::core::json; use identity_iota::core::FromJson; use identity_iota::core::Timestamp; @@ -17,10 +13,14 @@ use identity_iota::crypto::KeyPair; use identity_iota::did::MethodScope; use identity_iota::did::Service; use identity_iota::did::DID; -use identity_iota::iota_core::IotaDocument; -use identity_iota::iota_core::IotaService; -use identity_iota::iota_core::IotaVerificationMethod; use identity_iota::prelude::*; +use identity_iota_client_legacy::chain::DocumentHistory; +use identity_iota_client_legacy::tangle::Client; +use identity_iota_client_legacy::tangle::Receipt; +use identity_iota_client_legacy::Result; +use identity_iota_core_legacy::document::IotaDocument; +use identity_iota_core_legacy::document::IotaService; +use identity_iota_core_legacy::document::IotaVerificationMethod; mod create_did; diff --git a/identity_account/Cargo.toml b/identity_account/Cargo.toml index 15cba9bb11..d7aa74b873 100644 --- a/identity_account/Cargo.toml +++ b/identity_account/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" homepage = "https://www.iota.org" keywords = ["iota", "tangle", "identity", "did"] license = "Apache-2.0" +publish = false readme = "./README.md" repository = "https://github.com/iotaledger/identity.rs" description = "High-level interface for managing IOTA DID Documents." @@ -15,8 +16,8 @@ identity_account_storage = { version = "=0.6.0", path = "../identity_account_sto identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } identity_credential = { version = "=0.6.0", path = "../identity_credential", default-features = false } identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } -identity_iota_client = { version = "=0.6.0", path = "../identity_iota_client", default-features = false } -identity_iota_core = { version = "=0.6.0", path = "../identity_iota_core", default-features = false } +identity_iota_client_legacy = { version = "=0.6.0", path = "../identity_iota_client_legacy", default-features = false } +identity_iota_core_legacy = { version = "=0.6.0", path = "../identity_iota_core_legacy", default-features = false } log = { version = "0.4", default-features = false } paste = { version = "1.0" } rand = { version = "0.8", default-features = false, features = ["std", "std_rng"] } @@ -38,4 +39,4 @@ send-sync-storage = ["identity_account_storage/send-sync-storage"] encryption = ["identity_account_storage/encryption"] # Enables revocation with `RevocationBitmap2022`. -revocation-bitmap = ["identity_iota_client/revocation-bitmap"] +revocation-bitmap = ["identity_iota_client_legacy/revocation-bitmap"] diff --git a/identity_account/src/account/account.rs b/identity_account/src/account/account.rs index 787bf3a5f8..88401802e2 100644 --- a/identity_account/src/account/account.rs +++ b/identity_account/src/account/account.rs @@ -17,18 +17,18 @@ use identity_core::convert::ToJson; use identity_core::crypto::KeyType; use identity_core::crypto::ProofOptions; use identity_core::crypto::SetSignature; -use identity_iota_client::chain::DocumentChain; -use identity_iota_client::document::ResolvedIotaDocument; -use identity_iota_client::tangle::Client; -use identity_iota_client::tangle::PublishType; -use identity_iota_client::tangle::SharedPtr; -use identity_iota_core::did::IotaDID; -use identity_iota_core::did::IotaDIDUrl; -use identity_iota_core::diff::DiffMessage; -use identity_iota_core::document::IotaDocument; -use identity_iota_core::document::IotaVerificationMethod; -use identity_iota_core::tangle::MessageId; -use identity_iota_core::tangle::MessageIdExt; +use identity_iota_client_legacy::chain::DocumentChain; +use identity_iota_client_legacy::document::ResolvedIotaDocument; +use identity_iota_client_legacy::tangle::Client; +use identity_iota_client_legacy::tangle::PublishType; +use identity_iota_client_legacy::tangle::SharedPtr; +use identity_iota_core_legacy::did::IotaDID; +use identity_iota_core_legacy::did::IotaDIDUrl; +use identity_iota_core_legacy::diff::DiffMessage; +use identity_iota_core_legacy::document::IotaDocument; +use identity_iota_core_legacy::document::IotaVerificationMethod; +use identity_iota_core_legacy::tangle::MessageId; +use identity_iota_core_legacy::tangle::MessageIdExt; use serde::Serialize; use crate::account::AccountBuilder; @@ -120,7 +120,7 @@ where // Ensure the DID matches the client network. if did.network_str() != setup.client.network().name_str() { return Err(Error::IotaClientError( - identity_iota_client::Error::IncompatibleNetwork(format!( + identity_iota_client_legacy::Error::IncompatibleNetwork(format!( "DID network {} does not match account network {}", did.network_str(), setup.client.network().name_str() @@ -574,9 +574,9 @@ mod account_revocation { use crate::account::PublishOptions; use crate::Result; use identity_did::did::DID; - use identity_iota_client::tangle::Client; - use identity_iota_client::tangle::SharedPtr; - use identity_iota_core::did::IotaDIDUrl; + use identity_iota_client_legacy::tangle::Client; + use identity_iota_client_legacy::tangle::SharedPtr; + use identity_iota_core_legacy::did::IotaDIDUrl; impl Account where @@ -622,9 +622,9 @@ mod account_encryption { use identity_account_storage::types::EncryptionAlgorithm; use identity_account_storage::types::KeyLocation; use identity_core::crypto::PublicKey; - use identity_iota_client::tangle::Client; - use identity_iota_client::tangle::SharedPtr; - use identity_iota_core::document::IotaVerificationMethod; + use identity_iota_client_legacy::tangle::Client; + use identity_iota_client_legacy::tangle::SharedPtr; + use identity_iota_core_legacy::document::IotaVerificationMethod; impl Account where diff --git a/identity_account/src/account/builder.rs b/identity_account/src/account/builder.rs index caf419757a..f85c34c6e3 100644 --- a/identity_account/src/account/builder.rs +++ b/identity_account/src/account/builder.rs @@ -5,10 +5,10 @@ use std::sync::Arc; use identity_account_storage::storage::MemStore; use identity_account_storage::storage::Storage; -use identity_iota_client::tangle::Client; -use identity_iota_client::tangle::ClientBuilder; -use identity_iota_client::tangle::SharedPtr; -use identity_iota_core::did::IotaDID; +use identity_iota_client_legacy::tangle::Client; +use identity_iota_client_legacy::tangle::ClientBuilder; +use identity_iota_client_legacy::tangle::SharedPtr; +use identity_iota_core_legacy::did::IotaDID; use crate::account::Account; use crate::error::Result; @@ -100,7 +100,7 @@ where } } - /// Sets the IOTA Tangle [`Client`], this determines the [`Network`][identity_iota_core::tangle::Network] + /// Sets the IOTA Tangle [`Client`], this determines the [`Network`][identity_iota_core_legacy::tangle::Network] /// used by the identity. /// [`Accounts`](Account) created by the same [`AccountBuilder`] will share the same [`Client`]. /// @@ -113,7 +113,7 @@ where self } - /// Sets the IOTA Tangle [`Client`], this determines the [`Network`][identity_iota_core::tangle::Network] + /// Sets the IOTA Tangle [`Client`], this determines the [`Network`][identity_iota_core_legacy::tangle::Network] /// used by the identity. /// [`Accounts`](Account) created by the same [`AccountBuilder`] will share the same [`Client`]. /// diff --git a/identity_account/src/account/config.rs b/identity_account/src/account/config.rs index f4b7107095..0ff3a291f2 100644 --- a/identity_account/src/account/config.rs +++ b/identity_account/src/account/config.rs @@ -4,8 +4,8 @@ use std::sync::Arc; use identity_account_storage::storage::Storage; -use identity_iota_client::tangle::Client; -use identity_iota_client::tangle::SharedPtr; +use identity_iota_client_legacy::tangle::Client; +use identity_iota_client_legacy::tangle::SharedPtr; use serde::Deserialize; use serde::Serialize; diff --git a/identity_account/src/account/publish_options.rs b/identity_account/src/account/publish_options.rs index 8caae93a06..bf83e6d522 100644 --- a/identity_account/src/account/publish_options.rs +++ b/identity_account/src/account/publish_options.rs @@ -37,7 +37,7 @@ impl PublishOptions { /// verification relationship. /// /// If this is not set, the method used is determined by - /// [`IotaDocument`][identity_iota_core::document::IotaDocument::default_signing_method] + /// [`IotaDocument`][identity_iota_core_legacy::document::IotaDocument::default_signing_method] #[must_use] pub fn sign_with(mut self, fragment: impl Into) -> Self { self.sign_with = Some(fragment.into()); diff --git a/identity_account/src/error.rs b/identity_account/src/error.rs index ad08000fc8..82ffaee642 100644 --- a/identity_account/src/error.rs +++ b/identity_account/src/error.rs @@ -21,12 +21,12 @@ pub enum Error { /// Caused by errors from the [identity_account_storage] crate. #[error(transparent)] AccountCoreError(#[from] identity_account_storage::Error), - /// Caused by errors from the [identity_iota_client] crate. + /// Caused by errors from the [identity_iota_client_legacy] crate. #[error(transparent)] - IotaClientError(#[from] identity_iota_client::Error), - /// Caused by errors from the [identity_iota_core] crate. + IotaClientError(#[from] identity_iota_client_legacy::Error), + /// Caused by errors from the [identity_iota_core_legacy] crate. #[error(transparent)] - IotaCoreError(#[from] identity_iota_core::Error), + IotaCoreError(#[from] identity_iota_core_legacy::Error), /// Caused by attempting to find an identity that does not exist. #[error("Identity not found")] IdentityNotFound, diff --git a/identity_account/src/tests/account.rs b/identity_account/src/tests/account.rs index 1caab4ddc3..7ccf1e9121 100644 --- a/identity_account/src/tests/account.rs +++ b/identity_account/src/tests/account.rs @@ -15,15 +15,15 @@ use identity_core::crypto::ProofOptions; use identity_did::did::CoreDID; use identity_did::utils::Queryable; use identity_did::verification::MethodScope; -use identity_iota_client::chain::DocumentChain; -use identity_iota_client::tangle::Client; -use identity_iota_client::tangle::ClientBuilder; -use identity_iota_core::did::IotaDID; -use identity_iota_core::diff::DiffMessage; -use identity_iota_core::document::IotaDocument; -use identity_iota_core::tangle::MessageId; -use identity_iota_core::tangle::MessageIdExt; -use identity_iota_core::tangle::Network; +use identity_iota_client_legacy::chain::DocumentChain; +use identity_iota_client_legacy::tangle::Client; +use identity_iota_client_legacy::tangle::ClientBuilder; +use identity_iota_core_legacy::did::IotaDID; +use identity_iota_core_legacy::diff::DiffMessage; +use identity_iota_core_legacy::document::IotaDocument; +use identity_iota_core_legacy::tangle::MessageId; +use identity_iota_core_legacy::tangle::MessageIdExt; +use identity_iota_core_legacy::tangle::Network; use crate::account::Account; use crate::account::AccountBuilder; @@ -299,7 +299,7 @@ async fn test_account_publish_options_sign_with() { .publish_with_options(PublishOptions::default().sign_with("non-existent-method")) .await .unwrap_err(), - Error::IotaCoreError(identity_iota_core::Error::InvalidDoc( + Error::IotaCoreError(identity_iota_core_legacy::Error::InvalidDoc( identity_did::Error::MethodNotFound )) )); @@ -310,7 +310,7 @@ async fn test_account_publish_options_sign_with() { .publish_with_options(PublishOptions::default().sign_with(auth_method)) .await .unwrap_err(), - Error::IotaCoreError(identity_iota_core::Error::InvalidDoc( + Error::IotaCoreError(identity_iota_core_legacy::Error::InvalidDoc( identity_did::Error::MethodNotFound )) )); @@ -321,7 +321,7 @@ async fn test_account_publish_options_sign_with() { .publish_with_options(PublishOptions::default().sign_with(invalid_signing_method)) .await .unwrap_err(), - Error::IotaCoreError(identity_iota_core::Error::InvalidDocumentSigningMethodType), + Error::IotaCoreError(identity_iota_core_legacy::Error::InvalidDocumentSigningMethodType), )); assert!(account @@ -589,7 +589,7 @@ async fn network_resilient_test( let test_attempt = f(test_run).await; match test_attempt { - error @ Err(Error::IotaClientError(identity_iota_client::Error::ClientError(_))) => { + error @ Err(Error::IotaClientError(identity_iota_client_legacy::Error::ClientError(_))) => { eprintln!("test run {} errored with {:?}", test_run, error); if test_run == test_runs - 1 { diff --git a/identity_account/src/tests/updates.rs b/identity_account/src/tests/updates.rs index 846d150080..63b1309fa5 100644 --- a/identity_account/src/tests/updates.rs +++ b/identity_account/src/tests/updates.rs @@ -20,11 +20,11 @@ use identity_did::utils::Queryable; use identity_did::verification::MethodRelationship; use identity_did::verification::MethodScope; use identity_did::verification::MethodType; -use identity_iota_client::tangle::ClientBuilder; -use identity_iota_core::did::IotaDID; -use identity_iota_core::document::IotaDocument; -use identity_iota_core::document::IotaVerificationMethod; -use identity_iota_core::tangle::Network; +use identity_iota_client_legacy::tangle::ClientBuilder; +use identity_iota_core_legacy::did::IotaDID; +use identity_iota_core_legacy::document::IotaDocument; +use identity_iota_core_legacy::document::IotaVerificationMethod; +use identity_iota_core_legacy::tangle::Network; use crate::account::Account; use crate::account::AccountConfig; @@ -442,7 +442,7 @@ async fn test_attach_method_relationship() -> Result<()> { assert!(matches!( err, - Error::IotaCoreError(identity_iota_core::Error::InvalidDoc( + Error::IotaCoreError(identity_iota_core_legacy::Error::InvalidDoc( identity_did::Error::InvalidMethodEmbedded )) )); @@ -525,7 +525,7 @@ async fn test_detach_method_relationship() -> Result<()> { assert!(matches!( err, - Error::IotaCoreError(identity_iota_core::Error::InvalidDoc( + Error::IotaCoreError(identity_iota_core_legacy::Error::InvalidDoc( identity_did::Error::InvalidMethodEmbedded )) )); @@ -620,7 +620,7 @@ async fn test_delete_method() -> Result<()> { assert!(matches!( output.unwrap_err(), - Error::IotaCoreError(identity_iota_core::Error::InvalidDoc( + Error::IotaCoreError(identity_iota_core_legacy::Error::InvalidDoc( identity_did::Error::MethodNotFound )) )); diff --git a/identity_account/src/tests/util.rs b/identity_account/src/tests/util.rs index 3a6e951aa9..3923b1a260 100644 --- a/identity_account/src/tests/util.rs +++ b/identity_account/src/tests/util.rs @@ -6,8 +6,8 @@ use std::sync::Arc; use identity_account_storage::storage::MemStore; use identity_account_storage::storage::Storage; use identity_account_storage::storage::Stronghold; -use identity_iota_client::tangle::ClientBuilder; -use identity_iota_core::tangle::Network; +use identity_iota_client_legacy::tangle::ClientBuilder; +use identity_iota_core_legacy::tangle::Network; use rand::distributions::DistString; use rand::rngs::OsRng; diff --git a/identity_account/src/types/identity_state.rs b/identity_account/src/types/identity_state.rs index 26299cdc95..b24e04103d 100644 --- a/identity_account/src/types/identity_state.rs +++ b/identity_account/src/types/identity_state.rs @@ -4,7 +4,7 @@ use identity_account_storage::identity::ChainState; use identity_core::convert::FromJson; use identity_core::convert::ToJson; -use identity_iota_core::document::IotaDocument; +use identity_iota_core_legacy::document::IotaDocument; use serde::Deserialize; use serde::Serialize; diff --git a/identity_account/src/types/identity_updater.rs b/identity_account/src/types/identity_updater.rs index b12d2c2c06..d1baf690f5 100644 --- a/identity_account/src/types/identity_updater.rs +++ b/identity_account/src/types/identity_updater.rs @@ -1,8 +1,8 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use identity_iota_client::tangle::Client; -use identity_iota_client::tangle::SharedPtr; +use identity_iota_client_legacy::tangle::Client; +use identity_iota_client_legacy::tangle::SharedPtr; use crate::account::Account; diff --git a/identity_account/src/updates/macros.rs b/identity_account/src/updates/macros.rs index 6fbbf45c9c..b56b595745 100644 --- a/identity_account/src/updates/macros.rs +++ b/identity_account/src/updates/macros.rs @@ -36,7 +36,7 @@ macro_rules! impl_update_builder { #[derive(Debug)] pub struct [<$ident Builder>]<'account, C> where - C: identity_iota_client::tangle::SharedPtr, + C: identity_iota_client_legacy::tangle::SharedPtr, { account: &'account mut Account, $( @@ -46,7 +46,7 @@ macro_rules! impl_update_builder { impl<'account, C> [<$ident Builder>]<'account, C> where - C: identity_iota_client::tangle::SharedPtr, + C: identity_iota_client_legacy::tangle::SharedPtr, { $( #[must_use] @@ -58,7 +58,7 @@ macro_rules! impl_update_builder { pub fn new(account: &'account mut Account) -> [<$ident Builder>]<'account, C> where - C: identity_iota_client::tangle::SharedPtr, + C: identity_iota_client_legacy::tangle::SharedPtr, { [<$ident Builder>] { account, @@ -81,7 +81,7 @@ macro_rules! impl_update_builder { impl<'account, C> $crate::types::IdentityUpdater<'account, C> where - C: identity_iota_client::tangle::SharedPtr, + C: identity_iota_client_legacy::tangle::SharedPtr, { /// Creates a new builder to modify the identity. See the documentation of the return type for details. pub fn [<$ident:snake>](&'account mut self) -> [<$ident Builder>]<'account, C> { diff --git a/identity_account/src/updates/update.rs b/identity_account/src/updates/update.rs index 766922d84b..5167a391f9 100644 --- a/identity_account/src/updates/update.rs +++ b/identity_account/src/updates/update.rs @@ -25,14 +25,14 @@ use identity_did::utils::Queryable; use identity_did::verification::MethodRef; use identity_did::verification::MethodRelationship; use identity_did::verification::MethodScope; -use identity_iota_client::tangle::Client; -use identity_iota_client::tangle::SharedPtr; -use identity_iota_core::did::IotaDID; -use identity_iota_core::did::IotaDIDUrl; -use identity_iota_core::document::IotaDocument; -use identity_iota_core::document::IotaService; -use identity_iota_core::document::IotaVerificationMethod; -use identity_iota_core::tangle::NetworkName; +use identity_iota_client_legacy::tangle::Client; +use identity_iota_client_legacy::tangle::SharedPtr; +use identity_iota_core_legacy::did::IotaDID; +use identity_iota_core_legacy::did::IotaDIDUrl; +use identity_iota_core_legacy::document::IotaDocument; +use identity_iota_core_legacy::document::IotaService; +use identity_iota_core_legacy::document::IotaVerificationMethod; +use identity_iota_core_legacy::tangle::NetworkName; use crate::account::Account; use crate::error::Result; diff --git a/identity_account_storage/Cargo.toml b/identity_account_storage/Cargo.toml index 46a8b3c30e..b08a86ba91 100644 --- a/identity_account_storage/Cargo.toml +++ b/identity_account_storage/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" homepage = "https://www.iota.org" keywords = ["iota", "tangle", "identity", "storage"] license = "Apache-2.0" +publish = false readme = "./README.md" repository = "https://github.com/iotaledger/identity.rs" description = "Secure storage for Decentralized Identifiers and Verifiable Credentials." @@ -18,7 +19,7 @@ futures = { version = "0.3", optional = true } hashbrown = { version = "0.11", features = ["serde"] } identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } -identity_iota_core = { version = "=0.6.0", path = "../identity_iota_core", default-features = false } +identity_iota_core_legacy = { version = "=0.6.0", path = "../identity_iota_core_legacy", default-features = false } iota-crypto = { version = "0.12.1", default-features = false, features = ["hmac", "pbkdf", "sha", "std", "aes-gcm", "aes-kw"] } iota_stronghold = { version = "0.6.4", default-features = false, features = ["std"], optional = true } once_cell = { version = "1.7", default-features = false, features = ["std"], optional = true } @@ -35,14 +36,13 @@ rusty-fork = { version = "0.3" } tokio = { version = "1.17.0", default-features = false, features = ["macros", "rt", "rt-multi-thread", "sync"] } [features] -default = ["stronghold", "send-sync-storage", "storage-test-suite", "encryption"] -stronghold = [ - "iota_stronghold", - "tokio", - "futures", - "once_cell", - "rand", +default = [ + "stronghold", + "send-sync-storage", + "storage-test-suite", + "encryption", ] +stronghold = ["iota_stronghold", "tokio", "futures", "once_cell", "rand"] # Enables `Send` + `Sync` bounds for the Storage trait. send-sync-storage = [] # Exposes Storage `test_suite` module. diff --git a/identity_account_storage/src/error.rs b/identity_account_storage/src/error.rs index 80f30979a6..43f33a7ac3 100644 --- a/identity_account_storage/src/error.rs +++ b/identity_account_storage/src/error.rs @@ -12,7 +12,7 @@ pub enum Error { /// Caused by errors from the [identity_core] crate. #[error(transparent)] CoreError(#[from] identity_core::Error), - /// Caused by errors from the [`identity_iota_core`] crate. + /// Caused by errors from the [`identity_iota_core_legacy`] crate. #[error("DID creation failed: {0}")] DIDCreationError(String), /// Caused by errors from the [identity_did] crate. diff --git a/identity_account_storage/src/identity/chain_state.rs b/identity_account_storage/src/identity/chain_state.rs index 8bc4673495..a7415aa344 100644 --- a/identity_account_storage/src/identity/chain_state.rs +++ b/identity_account_storage/src/identity/chain_state.rs @@ -1,8 +1,8 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use identity_iota_core::tangle::MessageId; -use identity_iota_core::tangle::MessageIdExt; +use identity_iota_core_legacy::tangle::MessageId; +use identity_iota_core_legacy::tangle::MessageIdExt; use serde::Deserialize; use serde::Serialize; diff --git a/identity_account_storage/src/storage/memstore.rs b/identity_account_storage/src/storage/memstore.rs index be710e57b2..530a74a7c4 100644 --- a/identity_account_storage/src/storage/memstore.rs +++ b/identity_account_storage/src/storage/memstore.rs @@ -21,8 +21,8 @@ use identity_core::crypto::Sign; #[cfg(feature = "encryption")] use identity_core::crypto::X25519; use identity_did::did::CoreDID; -use identity_iota_core::did::IotaDID; -use identity_iota_core::tangle::NetworkName; +use identity_iota_core_legacy::did::IotaDID; +use identity_iota_core_legacy::tangle::NetworkName; use std::sync::RwLockReadGuard; use std::sync::RwLockWriteGuard; use zeroize::Zeroize; diff --git a/identity_account_storage/src/storage/stronghold.rs b/identity_account_storage/src/storage/stronghold.rs index a0e8102e58..3635a816f7 100644 --- a/identity_account_storage/src/storage/stronghold.rs +++ b/identity_account_storage/src/storage/stronghold.rs @@ -14,8 +14,8 @@ use identity_core::crypto::PrivateKey; use identity_core::crypto::PublicKey; use identity_core::crypto::X25519; use identity_did::did::CoreDID; -use identity_iota_core::did::IotaDID; -use identity_iota_core::tangle::NetworkName; +use identity_iota_core_legacy::did::IotaDID; +use identity_iota_core_legacy::tangle::NetworkName; use iota_stronghold::procedures; use iota_stronghold::procedures::ProcedureError; use iota_stronghold::procedures::Sha2Hash; diff --git a/identity_account_storage/src/storage/test_suite.rs b/identity_account_storage/src/storage/test_suite.rs index b4987ddc42..08038e0fe4 100644 --- a/identity_account_storage/src/storage/test_suite.rs +++ b/identity_account_storage/src/storage/test_suite.rs @@ -13,12 +13,12 @@ use identity_core::crypto::KeyPair; use identity_core::crypto::KeyType; use identity_core::crypto::PrivateKey; use identity_core::crypto::PublicKey; -use identity_iota_core::did::IotaDID; -use identity_iota_core::document::IotaDocument; -use identity_iota_core::document::IotaVerificationMethod; -use identity_iota_core::tangle::MessageId; -use identity_iota_core::tangle::Network; -use identity_iota_core::tangle::NetworkName; +use identity_iota_core_legacy::did::IotaDID; +use identity_iota_core_legacy::document::IotaDocument; +use identity_iota_core_legacy::document::IotaVerificationMethod; +use identity_iota_core_legacy::tangle::MessageId; +use identity_iota_core_legacy::tangle::Network; +use identity_iota_core_legacy::tangle::NetworkName; use crate::identity::ChainState; use crate::types::AgreementInfo; diff --git a/identity_account_storage/src/storage/traits.rs b/identity_account_storage/src/storage/traits.rs index dba5a12c96..e09600aced 100644 --- a/identity_account_storage/src/storage/traits.rs +++ b/identity_account_storage/src/storage/traits.rs @@ -9,7 +9,7 @@ use identity_core::crypto::KeyType; use identity_core::crypto::PrivateKey; use identity_core::crypto::PublicKey; use identity_did::did::CoreDID; -use identity_iota_core::tangle::NetworkName; +use identity_iota_core_legacy::tangle::NetworkName; use crate::error::Result; #[cfg(feature = "encryption")] diff --git a/identity_account_storage/src/stronghold/client_path.rs b/identity_account_storage/src/stronghold/client_path.rs index 3270b441a9..d70448bef3 100644 --- a/identity_account_storage/src/stronghold/client_path.rs +++ b/identity_account_storage/src/stronghold/client_path.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use identity_did::did::CoreDID; -use identity_iota_core::did::IotaDID; +use identity_iota_core_legacy::did::IotaDID; /// A helper type to ensure a consistently generated client path, for DIDs and strings /// to avoid having `Vec` everywhere. diff --git a/identity_account_storage/src/stronghold/test_util.rs b/identity_account_storage/src/stronghold/test_util.rs index 4428802ede..6ee3fd751c 100644 --- a/identity_account_storage/src/stronghold/test_util.rs +++ b/identity_account_storage/src/stronghold/test_util.rs @@ -13,8 +13,8 @@ pub(crate) fn random_temporary_path() -> String { file.to_str().unwrap().to_owned() } -pub(crate) fn random_did() -> identity_iota_core::did::IotaDID { - identity_iota_core::did::IotaDID::new(&random_bytes()).unwrap() +pub(crate) fn random_did() -> identity_iota_core_legacy::did::IotaDID { + identity_iota_core_legacy::did::IotaDID::new(&random_bytes()).unwrap() } pub(crate) fn random_key_location() -> crate::types::KeyLocation { diff --git a/identity_account_storage/src/stronghold/tests.rs b/identity_account_storage/src/stronghold/tests.rs index cd2337e17e..c8098b109a 100644 --- a/identity_account_storage/src/stronghold/tests.rs +++ b/identity_account_storage/src/stronghold/tests.rs @@ -14,7 +14,7 @@ use identity_core::crypto::PublicKey; use identity_core::crypto::X25519; use identity_core::utils::Base::Base16Lower; use identity_core::utils::BaseEncoding; -use identity_iota_core::did::IotaDID; +use identity_iota_core_legacy::did::IotaDID; use crate::storage::stronghold::aead_decrypt; use crate::storage::stronghold::aead_encrypt; diff --git a/identity_account_storage/src/types/did_type.rs b/identity_account_storage/src/types/did_type.rs index 6d1eafebbe..cabfd8b3a5 100644 --- a/identity_account_storage/src/types/did_type.rs +++ b/identity_account_storage/src/types/did_type.rs @@ -4,6 +4,6 @@ /// Supported types representing a DID that can be generated by the [`Storage`](crate::storage::Storage) interface. #[derive(Clone, Copy, Debug)] pub enum DIDType { - /// Corresponds to [`IotaDID`](identity_iota_core::did::IotaDID). + /// Corresponds to [`IotaDID`](identity_iota_core_legacy::did::IotaDID). IotaDID, } diff --git a/identity_account_storage/src/types/key_location.rs b/identity_account_storage/src/types/key_location.rs index 639e8195b2..d646c1cc30 100644 --- a/identity_account_storage/src/types/key_location.rs +++ b/identity_account_storage/src/types/key_location.rs @@ -8,7 +8,7 @@ use core::fmt::Result; use identity_core::crypto::KeyType; use identity_did::verification::MethodData; use identity_did::verification::MethodType; -use identity_iota_core::document::IotaVerificationMethod; +use identity_iota_core_legacy::document::IotaVerificationMethod; use seahash::SeaHasher; use std::hash::Hash; use std::hash::Hasher; diff --git a/identity_agent/Cargo.toml b/identity_agent/Cargo.toml index 435b364295..7436b0cf2a 100644 --- a/identity_agent/Cargo.toml +++ b/identity_agent/Cargo.toml @@ -15,8 +15,6 @@ async-trait = { version = "0.1", default-features = false } dashmap = { version = "5.3", default-features = false } futures = { version = "0.3", default-features = false } identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } -identity_iota_client = { version = "=0.6.0", path = "../identity_iota_client", default-features = false } -identity_iota_core = { version = "=0.6.0", path = "../identity_iota_core", default-features = false } libp2p = { version = "0.45", default-features = false, features = ["tcp-tokio", "dns-tokio", "websocket", "request-response", "noise", "yamux"] } log = { version = "0.4", default-features = false } serde = { version = "1.0", default-features = false, features = ["derive"] } @@ -28,6 +26,7 @@ uuid = { version = "0.8", default-features = false, features = ["v4", "serde"] } [dev-dependencies] criterion = { version = "0.3", default-features = false, features = ["stable"] } identity_account = { version = "=0.6.0", path = "../identity_account", default-features = false, features = ["send-sync-storage"] } +identity_iota_core_legacy = { version = "=0.6.0", path = "../identity_iota_core_legacy", default-features = false } pretty_env_logger = { version = "0.4", default-features = false } [[bench]] diff --git a/identity_agent/benches/agent.rs b/identity_agent/benches/agent.rs index d2626b2d1f..a349ed61c3 100644 --- a/identity_agent/benches/agent.rs +++ b/identity_agent/benches/agent.rs @@ -76,8 +76,8 @@ mod remote_account { use identity_agent::agent::Handler; use identity_agent::agent::HandlerRequest; use identity_agent::agent::RequestContext; - use identity_iota_core::did::IotaDID; - use identity_iota_core::document::IotaDocument; + use identity_iota_core_legacy::did::IotaDID; + use identity_iota_core_legacy::document::IotaDocument; use serde::Deserialize; use serde::Serialize; use std::sync::Arc; diff --git a/identity_agent/benches/didcomm.rs b/identity_agent/benches/didcomm.rs index 00eb33dcf1..7bfae1ed6f 100644 --- a/identity_agent/benches/didcomm.rs +++ b/identity_agent/benches/didcomm.rs @@ -13,18 +13,13 @@ use identity_agent::didcomm::DidCommPlaintextMessage; use identity_agent::didcomm::ThreadId; use identity_agent::Multiaddr; -use identity_core::crypto::KeyPair; -use identity_core::crypto::KeyType; -use identity_iota_core::document::IotaDocument; use test_handler::PresentationOffer; use test_handler::PresentationRequest; use test_handler::TestHandler; async fn setup() -> (DidCommAgent, AgentId, DidCommAgent) { let addr: Multiaddr = "/ip4/0.0.0.0/tcp/0".parse().unwrap(); - let mut builder = DidCommAgentBuilder::new().identity(DidCommAgentIdentity { - document: IotaDocument::new(&KeyPair::new(KeyType::Ed25519).unwrap()).unwrap(), - }); + let mut builder = DidCommAgentBuilder::new().identity(DidCommAgentIdentity::new()); builder.attach_didcomm(TestHandler); @@ -34,9 +29,7 @@ async fn setup() -> (DidCommAgent, AgentId, DidCommAgent) { let receiver_agent_id = receiver.agent_id(); let mut sender: DidCommAgent = DidCommAgentBuilder::new() - .identity(DidCommAgentIdentity { - document: IotaDocument::new(&KeyPair::new(KeyType::Ed25519).unwrap()).unwrap(), - }) + .identity(DidCommAgentIdentity::new()) .build() .await .unwrap(); diff --git a/identity_agent/src/didcomm/agent.rs b/identity_agent/src/didcomm/agent.rs index 8c840c746b..11fbdf0a57 100644 --- a/identity_agent/src/didcomm/agent.rs +++ b/identity_agent/src/didcomm/agent.rs @@ -6,7 +6,6 @@ use std::sync::Arc; use dashmap::DashMap; use futures::channel::oneshot; -use identity_iota_core::document::IotaDocument; use libp2p::Multiaddr; use serde::de::DeserializeOwned; @@ -31,11 +30,15 @@ use crate::p2p::ThreadRequest; /// The identity of a [`DidCommAgent`]. /// /// Note: Currently an incomplete implementation. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub struct DidCommAgentIdentity { // TODO: This type is meant to be used in a future update. - #[allow(dead_code)] - pub document: IotaDocument, +} + +impl DidCommAgentIdentity { + pub fn new() -> Self { + Self {} + } } /// The internal state of a [`DidCommAgent`]. diff --git a/identity_agent/src/tests/handler.rs b/identity_agent/src/tests/handler.rs index feb76e3a00..522c54855a 100644 --- a/identity_agent/src/tests/handler.rs +++ b/identity_agent/src/tests/handler.rs @@ -8,7 +8,7 @@ use std::sync::Arc; use std::task::Poll; use futures::pin_mut; -use identity_iota_core::did::IotaDID; +use identity_iota_core_legacy::did::IotaDID; use libp2p::request_response::OutboundFailure; use libp2p::Multiaddr; diff --git a/identity_agent/src/tests/mod.rs b/identity_agent/src/tests/mod.rs index a2ba0de383..a785874e49 100644 --- a/identity_agent/src/tests/mod.rs +++ b/identity_agent/src/tests/mod.rs @@ -6,8 +6,6 @@ mod handler; mod presentation; mod remote_account; -use identity_core::crypto::KeyPair; -use identity_iota_core::document::IotaDocument; use libp2p::identity::Keypair; use libp2p::Multiaddr; @@ -77,9 +75,5 @@ async fn default_listening_didcomm_agent( } fn default_identity() -> DidCommAgentIdentity { - let keypair: KeyPair = KeyPair::new(identity_core::crypto::KeyType::Ed25519).unwrap(); - - DidCommAgentIdentity { - document: IotaDocument::new(&keypair).unwrap(), - } + DidCommAgentIdentity::new() } diff --git a/identity_agent/src/tests/remote_account/handler.rs b/identity_agent/src/tests/remote_account/handler.rs index d6e56b3032..f8b1445e5b 100644 --- a/identity_agent/src/tests/remote_account/handler.rs +++ b/identity_agent/src/tests/remote_account/handler.rs @@ -6,8 +6,8 @@ use std::sync::Arc; use dashmap::DashMap; use identity_account::account::Account; use identity_account::account::AccountBuilder; -use identity_iota_core::did::IotaDID; -use identity_iota_core::document::IotaDocument; +use identity_iota_core_legacy::did::IotaDID; +use identity_iota_core_legacy::document::IotaDocument; use tokio::sync::Mutex; use crate::agent::Handler; diff --git a/identity_agent/src/tests/remote_account/requests.rs b/identity_agent/src/tests/remote_account/requests.rs index 6ad3b22fb2..098af9a792 100644 --- a/identity_agent/src/tests/remote_account/requests.rs +++ b/identity_agent/src/tests/remote_account/requests.rs @@ -2,8 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 use identity_account::types::IdentitySetup; -use identity_iota_core::did::IotaDID; -use identity_iota_core::document::IotaDocument; +use identity_iota_core_legacy::did::IotaDID; +use identity_iota_core_legacy::document::IotaDocument; use serde::Deserialize; use serde::Serialize; diff --git a/identity_agent/src/tests/remote_account/tests.rs b/identity_agent/src/tests/remote_account/tests.rs index cc0cd7c71e..5f8d042c91 100644 --- a/identity_agent/src/tests/remote_account/tests.rs +++ b/identity_agent/src/tests/remote_account/tests.rs @@ -1,7 +1,7 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use identity_iota_core::document::IotaDocument; +use identity_iota_core_legacy::document::IotaDocument; use crate::agent::Result as AgentResult; use crate::tests::default_listening_agent; diff --git a/identity_comm/Cargo.toml b/identity_comm/Cargo.toml index 419017930e..cdfcc668ef 100644 --- a/identity_comm/Cargo.toml +++ b/identity_comm/Cargo.toml @@ -14,7 +14,7 @@ description = "An implementation of the DIDComm Messaging Specification." identity_core = { path = "../identity_core", version = "=0.5.0-dev.4" } identity_credential = { path = "../identity_credential", version = "=0.5.0-dev.4" } identity_did = { path = "../identity_did", version = "=0.5.0-dev.4" } -identity_iota_client = { version = "=0.5.0", path = "../identity_iota_client", default-features = false } +identity_iota_client_legacy = { version = "=0.5.0", path = "../identity_iota_client_legacy", default-features = false } # libjose = { path = "../libjose", version = "=0.1.0" } paste = { version = "1.0" } serde = { version = "1.0", features = ["derive"] } diff --git a/identity_comm/src/error.rs b/identity_comm/src/error.rs index 487d632b5c..abc3538e85 100644 --- a/identity_comm/src/error.rs +++ b/identity_comm/src/error.rs @@ -6,7 +6,7 @@ pub type Result = core::result::Result; #[derive(Debug, thiserror::Error, strum::IntoStaticStr)] pub enum Error { #[error(transparent)] - IotaError(#[from] identity_iota_client::Error), + IotaError(#[from] identity_iota_client_legacy::Error), #[error(transparent)] CoreError(#[from] identity_core::Error), #[error(transparent)] diff --git a/identity_did/src/verification/mod.rs b/identity_did/src/verification/mod.rs index 6bb4a93862..37a5783fde 100644 --- a/identity_did/src/verification/mod.rs +++ b/identity_did/src/verification/mod.rs @@ -4,7 +4,7 @@ //! The `verification` module contains code for verifying the correctness of core DID-related types. //! //! This crate DOES NOT verify IOTA-specific invariants, those are exposed by the -//! `identity_iota_core` crate. +//! `identity_iota_core_legacy` crate. mod builder; mod method_data; diff --git a/identity_iota/Cargo.toml b/identity_iota/Cargo.toml index 8d594979da..f642f2d3d5 100644 --- a/identity_iota/Cargo.toml +++ b/identity_iota/Cargo.toml @@ -8,61 +8,38 @@ keywords = ["iota", "tangle", "identity", "did", "ssi"] license = "Apache-2.0" readme = "./README.md" repository = "https://github.com/iotaledger/identity.rs" -rust-version = "1.60" +rust-version = "1.62" description = "Framework for Self-Sovereign Identity with IOTA DID." [dependencies] -# identity_comm = { version = "=0.5.0-dev.4", path = "../identity_comm", optional = true } -identity_account = { version = "=0.6.0", path = "../identity_account", default-features = false, optional = true } -identity_account_storage = { version = "=0.6.0", path = "../identity_account_storage", default-features = false, optional = true } identity_agent = { version = "=0.6.0", path = "../identity_agent", default-features = false, optional = true } identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } -identity_credential = { version = "=0.6.0", path = "../identity_credential", default-features = false } +identity_credential = { version = "=0.6.0", path = "../identity_credential", features = ["validator"], default-features = false } identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } -identity_iota_client = { version = "=0.6.0", path = "../identity_iota_client", default-features = false } identity_iota_core = { version = "=0.6.0", path = "../identity_iota_core", default-features = false } identity_resolver = { version = "=0.6.0", path = "../identity_resolver", default-features = false } [dev-dependencies] -criterion = { version = "0.3" } +anyhow = "1.0.64" +iota-client = { version = "2.0.0-beta.3", default-features = false, features = ["tls", "stronghold"] } tokio = { version = "1.17.0", features = ["full"] } -[[bench]] -name = "benchmark" -harness = false - [features] -default = ["account", "stronghold", "send-sync-storage", "unstable-encryption", "revocation-bitmap"] - -# Enables support for secure storage of DID Documents -account = ["identity_account", "identity_account_storage"] - -# Enables support for stronghold storage. -stronghold = ["identity_account/stronghold", "identity_account_storage/stronghold"] - -# Enables support for DID Communication -# comm = ["identity_comm"] - -# TODO: After merging the refactor (#1000), add "identity_resolver/iota" to the `iota-client` feature. +default = ["revocation-bitmap", "client", "iota-client"] -# Enables `Send` + `Sync` bounds for the Storage trait. -send-sync-storage = ["identity_account_storage/send-sync-storage"] +# Exposes the `IotaIdentityClient` and `IotaIdentityClientExt` traits. +client = ["identity_iota_core/client"] -# Exposes Storage `test_suite` module. -storage-test-suite = ["identity_account_storage/storage-test-suite"] +# Enables the iota-client integration, the client trait implementations for it, and the `IotaClientExt` trait. +iota-client = ["identity_iota_core/iota-client", "identity_resolver/iota"] # Enables revocation with `RevocationBitmap2022`. revocation-bitmap = [ - "identity_account?/revocation-bitmap", - "identity_resolver/revocation-bitmap", - "identity_iota_client/revocation-bitmap", "identity_credential/revocation-bitmap", + "identity_iota_core/revocation-bitmap", + "identity_resolver/revocation-bitmap", ] -# Enables encryption and decryption functionality. -# Breaking changes to types and functions behind this flag are not covered by semver. -unstable-encryption = ["identity_account/encryption"] - # Enables support for the unstable identity agent. # Breaking changes to types and functions behind this flag are not covered by semver. unstable-agent = ["dep:identity_agent"] diff --git a/identity_iota/README.md b/identity_iota/README.md index 00ad3729f3..0ed6ba4a4b 100644 --- a/identity_iota/README.md +++ b/identity_iota/README.md @@ -38,7 +38,7 @@ The individual libraries are developed to be agnostic about the utilized [Distri [Foreign Function Interface (FFI)](https://en.wikipedia.org/wiki/Foreign_function_interface) Bindings of this [Rust](https://www.rust-lang.org/) library to other programming languages are a work in progress (see Roadmap below). Currently available bindings are: -* [Web Assembly](https://github.com/iotaledger/identity.rs/blob/HEAD/bindings/wasm/) (JavaScript/TypeScript) +- [Web Assembly](https://github.com/iotaledger/identity.rs/blob/HEAD/bindings/wasm/) (JavaScript/TypeScript) ## Documentation and Resources @@ -49,12 +49,13 @@ The individual libraries are developed to be agnostic about the utilized [Distri ## Prerequisites -- [Rust](https://www.rust-lang.org/) (>= 1.60) -- [Cargo](https://doc.rust-lang.org/cargo/) (>= 1.60) +- [Rust](https://www.rust-lang.org/) (>= 1.62) +- [Cargo](https://doc.rust-lang.org/cargo/) (>= 1.62) ## Getting Started If you want to include IOTA Identity in your project, simply add it as a dependency in your `Cargo.toml`: + ```toml [dependencies] identity_iota = { version = "0.6" } @@ -68,9 +69,10 @@ To try out the [examples](https://github.com/iotaledger/identity.rs/blob/HEAD/ex ## Example: Creating an Identity -The following code creates and publishes a new IOTA DID Document to the Tangle Mainnet. +The following code creates and publishes a new IOTA DID Document to the Shimmer Testnet. + +_Cargo.toml_ -*Cargo.toml* ```toml [package] name = "iota_identity_example" @@ -78,82 +80,108 @@ version = "1.0.0" edition = "2021" [dependencies] -identity_iota = { version = "0.6" } +identity_iota = { version = "0.7" } +iota-client = { version = "2.0.0-beta.3", default-features = false, features = ["tls", "stronghold"] } tokio = { version = "1", features = ["full"] } ``` -*main.**rs* + +_main.__rs_ + ```rust,no_run -use identity_iota::account::Account; -use identity_iota::account::IdentitySetup; -use identity_iota::account::Result; -use identity_iota::account_storage::Stronghold; use identity_iota::core::ToJson; -use identity_iota::client::ExplorerUrl; -use identity_iota::client::ResolvedIotaDocument; - +use identity_iota::crypto::KeyPair; +use identity_iota::crypto::KeyType; +use identity_iota::did::MethodScope; +use identity_iota::iota::IotaClientExt; +use identity_iota::iota::IotaDocument; +use identity_iota::iota::IotaIdentityClientExt; +use identity_iota::iota::IotaVerificationMethod; +use identity_iota::iota::NetworkName; +use iota_client::block::address::Address; +use iota_client::block::output::AliasOutput; +use iota_client::crypto::keys::bip39; +use iota_client::secret::stronghold::StrongholdSecretManager; +use iota_client::secret::SecretManager; +use iota_client::Client; +use tokio::io::AsyncReadExt; + +// The endpoint of the IOTA node to use. +static API_ENDPOINT: &str = "https://api.testnet.shimmer.network/"; + +/// Demonstrates how to create a DID Document and publish it in a new Alias Output. #[tokio::main] -async fn main() -> Result<()> { - // Stronghold settings. - let stronghold_path: &str = "./example-strong.hodl"; - let password: String = "my-password".into(); - let stronghold: Stronghold = Stronghold::new(stronghold_path, password, None).await?; - - // Create a new identity with default settings and - // Stronghold as the storage. - let account: Account = Account::builder() - .storage(stronghold) - .create_identity(IdentitySetup::default()) - .await?; - - println!("[Example] Local Document = {:#?}", account.document()); - - // Fetch the DID Document from the Tangle - // - // This is an optional step to ensure DID Document consistency. - let resolved: ResolvedIotaDocument = account.resolve_identity().await?; - - println!("[Example] Tangle Document = {}", resolved.to_json_pretty()?); - - // Print the Identity Resolver Explorer URL. - let explorer: &ExplorerUrl = ExplorerUrl::mainnet(); - println!( - "[Example] Explore the DID Document = {}", - explorer.resolver_url(account.did())? - ); +async fn main() -> anyhow::Result<()> { + // Create a new client to interact with the IOTA ledger. + let client: Client = Client::builder().with_primary_node(API_ENDPOINT, None)?.finish()?; + + // Create a new Stronghold. + let mut stronghold = StrongholdSecretManager::builder() + .password("secure_password") + .build("./example-strong.hodl")?; + + // Generate a mnemonic and store it in the Stronghold. + let keypair = KeyPair::new(KeyType::Ed25519)?; + let mnemonic = bip39::wordlist::encode(keypair.private().as_ref(), &bip39::wordlist::ENGLISH) + .map_err(|err| anyhow::anyhow!("{err:?}"))?; + stronghold.store_mnemonic(mnemonic).await?; + + // Create a new secret manager backed by the Stronghold. + let secret_manager: SecretManager = SecretManager::Stronghold(stronghold); + + // Get an address from the secret manager. + let address: Address = client.get_addresses(&secret_manager).with_range(0..1).get_raw().await?[0]; + + // Get the Bech32 human-readable part (HRP) of the network. + let network_name: NetworkName = client.network_name().await?; + + println!("Your wallet address is: {}", address.to_bech32(network_name.as_ref())); + println!("Please request funds from https://faucet.testnet.shimmer.network/, then press Enter."); + tokio::io::stdin().read_u8().await?; + + // Create a new DID document with a placeholder DID. + // The DID will be derived from the Alias Id of the Alias Output after publishing. + let mut document: IotaDocument = IotaDocument::new(&network_name); + + // Insert a new Ed25519 verification method in the DID document. + let keypair: KeyPair = KeyPair::new(KeyType::Ed25519)?; + let method: IotaVerificationMethod = + IotaVerificationMethod::new(document.id().clone(), keypair.type_(), keypair.public(), "#key-1")?; + document.insert_method(method, MethodScope::VerificationMethod)?; + + // Construct an Alias Output containing the DID document, with the wallet address + // set as both the state controller and governor. + let alias_output: AliasOutput = client.new_did_output(address, document, None).await?; + println!("Alias Output: {}", alias_output.to_json()?); + + // Publish the Alias Output and get the published DID document. + let document: IotaDocument = client.publish_did_output(&secret_manager, alias_output).await?; + println!("Published DID document: {:#}", document); Ok(()) } ``` -*Example output* + +_Example output_ + ```json { "doc": { - "id": "did:iota:8nG4d85jnqTYGMWt5DL63FobHF5Ersuw4foQnEo66nbD", - "capabilityInvocation": [ + "id": "did:iota:rms:0x4113f08e360a3c1725bb1f93d94f6e807a1ef88f091d45f93513c3e88dac3248", + "verificationMethod": [ { - "id": "did:iota:8nG4d85jnqTYGMWt5DL63FobHF5Ersuw4foQnEo66nbD#sign-0", - "controller": "did:iota:8nG4d85jnqTYGMWt5DL63FobHF5Ersuw4foQnEo66nbD", + "id": "did:iota:rms:0x4113f08e360a3c1725bb1f93d94f6e807a1ef88f091d45f93513c3e88dac3248#key-1", + "controller": "did:iota:rms:0x4113f08e360a3c1725bb1f93d94f6e807a1ef88f091d45f93513c3e88dac3248", "type": "Ed25519VerificationKey2018", - "publicKeyMultibase": "zHCoXy5XR9BmxMfXK8GrKziPGJLFBnrfeuH3XR4GuQoR2" + "publicKeyMultibase": "z7BoQerJn9NxwcA4KHGK9CP5FJRRZJBmsxrGPvWiyuFGG" } ] }, "meta": { - "created": "2022-06-14T13:16:04Z", - "updated": "2022-06-14T13:16:04Z" - }, - "proof": { - "type": "JcsEd25519Signature2020", - "verificationMethod": "did:iota:8nG4d85jnqTYGMWt5DL63FobHF5Ersuw4foQnEo66nbD#sign-0", - "signatureValue": "2zx5UCTbcbzSRtPmNj12fzPe1fdGAPPEyT3WGjkP8ADb6xx5jj6E6tcGCYPgWi9YvohkwHSjAVPS5sD2Zac5deyW" - }, - "integrationMessageId": "446c1416eda4b40ec793f902fe4ba18e88d8f164637426d9239fc7c1b921c8c3" + "created": "2022-09-06T12:12:11Z", + "updated": "2022-09-06T12:12:11Z" + } } ``` -```text -[Example] Explore the DID Document = https://explorer.iota.org/mainnet/identity-resolver/did:iota:8nG4d85jnqTYGMWt5DL63FobHF5Ersuw4foQnEo66nbD -``` -The output link points to the [Identity Resolver on the IOTA Tangle Explorer](https://explorer.iota.org/mainnet/identity-resolver/did:iota:8jYcEGiNYUWcSdEtjCAcS97G58qq1VrWzW7M57BsHymz). ## Roadmap and Milestones @@ -163,20 +191,19 @@ IOTA Identity is in heavy development, and will naturally change as it matures a #### Basic Framework -| Feature | Not started | In Research | In Development | Done | Notes | -| :------------------------- | :---------: | :------: | :---------------: | :-: | :-------------------------------------------------------------------- | -| [IOTA DID Method](https://wiki.iota.org/identity.rs/specs/did/iota_did_method_spec) | | | | ✔️ | Finished implementation. | -| [Verifiable Credentials](https://www.w3.org/TR/vc-data-model/) | | | | ✔️ | Finished implementation. | -| Account | | | | ✔️ | Finished implementation. | -| Identity Actor | | | 🔶 | | | -| [DIDComm](https://wiki.iota.org/identity.rs/specs/didcomm/overview) | | | 🔶 | | In-progress with Actor | -| Selective Disclosure | | 🔶 | | | | -| Zero Knowledge Proofs | | 🔶 | | | | -| Support Embedded Rust | | 🔶 | | | | -| [WASM Bindings](https://github.com/iotaledger/identity.rs/blob/HEAD/bindings/wasm) | | | | ✔️ | Finished implementation. | -| [Code Examples](https://github.com/iotaledger/identity.rs/blob/HEAD/examples) | | | | ✔️ | | -| [Documentation Portal](https://wiki.iota.org/identity.rs/introduction) | | | 🔶 | | | - +| Feature | Not started | In Research | In Development | Done | Notes | +| :---------------------------------------------------------------------------------: | :---------: | :---------: | :------------: | :--: | :----------------------: | +| [IOTA DID Method](https://wiki.iota.org/identity.rs/specs/did/iota_did_method_spec) | | | | ✔️ | Finished implementation. | +| [Verifiable Credentials](https://www.w3.org/TR/vc-data-model/) | | | | ✔️ | Finished implementation. | +| Account | | | | ✔️ | Finished implementation. | +| [Identity Agent](https://github.com/iotaledger/identity.rs/tree/dev/identity_agent) | | | 🔶 | | | +| [DIDComm](https://wiki.iota.org/identity.rs/specs/didcomm/overview) | | | 🔶 | | In-progress with Agent | +| Selective Disclosure | | 🔶 | | | | +| Zero Knowledge Proofs | | 🔶 | | | | +| Support Embedded Rust | | 🔶 | | | | +| [WASM Bindings](https://github.com/iotaledger/identity.rs/blob/HEAD/bindings/wasm) | | | | ✔️ | Finished implementation. | +| [Code Examples](https://github.com/iotaledger/identity.rs/blob/HEAD/examples) | | | | ✔️ | | +| [Documentation Portal](https://wiki.iota.org/identity.rs/introduction) | | | 🔶 | | | #### Next Milestones diff --git a/identity_iota/benches/benchmark.rs b/identity_iota/benches/benchmark.rs deleted file mode 100644 index ffc957ea09..0000000000 --- a/identity_iota/benches/benchmark.rs +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -#![allow(deprecated)] - -use criterion::criterion_group; -use criterion::criterion_main; -use criterion::BenchmarkId; -use criterion::Criterion; - -use identity_core::crypto::KeyType; -use identity_iota::client::DocumentChain; -use identity_iota::client::IntegrationChain; -use identity_iota::crypto::KeyPair; -use identity_iota::iota_core::IotaDID; -use identity_iota::iota_core::IotaDocument; - -use self::diff_chain::setup_diff_chain_bench; -use self::diff_chain::update_diff_chain; -use self::diff_chain::update_integration_chain; - -mod diff_chain; - -fn generate_signed_document(keypair: &KeyPair) { - let mut document: IotaDocument = IotaDocument::new(keypair).unwrap(); - - document - .sign_self( - keypair.private(), - document.default_signing_method().unwrap().id().clone(), - ) - .unwrap(); -} - -fn generate_did(keypair: &KeyPair) { - let _ = IotaDID::new(keypair.public().as_ref()).unwrap(); -} - -fn bench_generate_signed_document(c: &mut Criterion) { - let keypair = KeyPair::new(KeyType::Ed25519).unwrap(); - - c.bench_function("generate signed document", |b| { - b.iter(|| generate_signed_document(&keypair)) - }); -} - -fn bench_generate_did(c: &mut Criterion) { - let keypair = KeyPair::new(KeyType::Ed25519).unwrap(); - c.bench_function("generate did", |b| b.iter(|| generate_did(&keypair))); -} - -fn bench_diff_chain_updates(c: &mut Criterion) { - static ITERATIONS: &[usize] = &[1, 10, 100, 1000]; - - let (doc, keys) = setup_diff_chain_bench(); - - let mut group = c.benchmark_group("update diff chain"); - for size in ITERATIONS.iter() { - let mut chain: DocumentChain = DocumentChain::new(IntegrationChain::new(doc.clone()).unwrap()); - - update_diff_chain(*size, &mut chain, &keys); - - group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &_| { - b.iter(|| update_diff_chain(1, &mut chain.clone(), &keys)); - }); - } - group.finish(); -} - -fn bench_integration_chain_updates(c: &mut Criterion) { - static ITERATIONS: &[usize] = &[1, 10, 100, 1000]; - - let (doc, keys) = setup_diff_chain_bench(); - - let mut group = c.benchmark_group("update integration chain"); - - for size in ITERATIONS.iter() { - let mut chain: DocumentChain = DocumentChain::new(IntegrationChain::new(doc.clone()).unwrap()); - - update_integration_chain(*size, &mut chain, &keys); - - group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &_| { - b.iter(|| update_integration_chain(1, &mut chain.clone(), &keys)); - }); - } - group.finish(); -} - -criterion_group!( - benches, - bench_generate_signed_document, - bench_generate_did, - bench_diff_chain_updates, - bench_integration_chain_updates, -); -criterion_main!(benches); diff --git a/identity_iota/benches/diff_chain.rs b/identity_iota/benches/diff_chain.rs deleted file mode 100644 index d2bfa0965e..0000000000 --- a/identity_iota/benches/diff_chain.rs +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -#![allow(deprecated)] - -use identity_core::crypto::KeyType; -use identity_core::crypto::ProofOptions; -use identity_iota::client::DocumentChain; -use identity_iota::client::TangleRef; -use identity_iota::core::Timestamp; -use identity_iota::crypto::KeyPair; -use identity_iota::did::MethodBuilder; -use identity_iota::did::MethodData; -use identity_iota::did::MethodRef; -use identity_iota::did::MethodType; -use identity_iota::did::DID; -use identity_iota::iota_core::DiffMessage; -use identity_iota::iota_core::IotaDID; -use identity_iota::iota_core::IotaDocument; -use identity_iota::iota_core::MessageId; -use identity_iota_client::document::ResolvedIotaDocument; - -pub fn setup_diff_chain_bench() -> (ResolvedIotaDocument, KeyPair) { - let keypair: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); - let mut document: IotaDocument = IotaDocument::new(&keypair).unwrap(); - - document - .sign_self( - keypair.private(), - document.default_signing_method().unwrap().id().clone(), - ) - .unwrap(); - - let mut resolved: ResolvedIotaDocument = ResolvedIotaDocument::from(document); - resolved.set_message_id(MessageId::new([8; 32])); - - (resolved, keypair) -} - -/// Creates a diff chain and updates it `n` times -pub fn update_diff_chain(n: usize, chain: &mut DocumentChain, keypair: &KeyPair) { - let current_n = chain.diff().len(); - - for i in current_n..(n + current_n) { - let new: IotaDocument = { - let mut this: IotaDocument = chain.current().clone().document; - this.properties_mut().insert(i.to_string(), 123.into()); - this.metadata.updated = Some(Timestamp::now_utc()); - this - }; - - let message_id = *chain.diff_message_id(); - let mut diff: DiffMessage = chain - .current() - .document - .diff( - &new, - message_id, - keypair.private(), - chain.current().document.default_signing_method().unwrap().id(), - ) - .unwrap(); - - diff.set_message_id(message_id); - assert!(chain.try_push_diff(diff).is_ok()); - } -} - -/// Creates an integration chain and updates it `n` times -pub fn update_integration_chain(n: usize, chain: &mut DocumentChain, keypair: &KeyPair) { - let current_n = chain.diff().len(); - - for i in current_n..(n + current_n) { - let mut new: ResolvedIotaDocument = chain.current().clone(); - - let authentication: MethodRef = MethodBuilder::default() - .id(chain.id().to_url().join(&format!("#key-{}", i)).unwrap()) - .controller(chain.id().clone()) - .type_(MethodType::Ed25519VerificationKey2018) - .data(MethodData::new_multibase(keypair.public())) - .build() - .map(Into::into) - .unwrap(); - - new.document.core_document_mut().authentication_mut().clear(); - new - .document - .core_document_mut() - .authentication_mut() - .append(authentication); - - new.document.metadata.updated = Some(Timestamp::now_utc()); - new.document.metadata.previous_message_id = *chain.integration_message_id(); - - chain - .current() - .document - .sign_data( - &mut new.document, - keypair.private(), - chain.current().document.default_signing_method().unwrap().id(), - ProofOptions::default(), - ) - .unwrap(); - chain.try_push_integration(new).unwrap(); - } -} diff --git a/identity_iota/src/lib.rs b/identity_iota/src/lib.rs index 8aeb22d067..f700d290fb 100644 --- a/identity_iota/src/lib.rs +++ b/identity_iota/src/lib.rs @@ -69,54 +69,10 @@ pub mod did { pub use identity_did::verifiable; } -pub mod client { - //! IOTA DID Tangle client and validators. +pub mod iota { + //! The IOTA DID method implementation for the IOTA ledger. - pub use identity_iota_client::chain::*; - pub use identity_iota_client::document::*; - pub use identity_iota_client::tangle::*; - - pub use identity_iota_client::Error; - pub use identity_iota_client::Result; -} - -pub mod iota_core { - //! IOTA Core Traits and Types definitions - - pub use identity_iota_core::did::*; - pub use identity_iota_core::diff::*; - pub use identity_iota_core::document::*; - pub use identity_iota_core::tangle::*; - - pub use identity_iota_core::Error; - pub use identity_iota_core::Result; - - #[doc(inline)] - pub use identity_iota_core::try_construct_did; -} - -#[cfg(feature = "account")] -#[cfg_attr(docsrs, doc(cfg(feature = "account")))] -pub mod account { - //! Secure storage for Decentralized Identifiers - - pub use identity_account::account::*; - pub use identity_account::error::*; - pub use identity_account::types::*; - pub use identity_account::updates::*; -} - -#[cfg(feature = "account")] -#[cfg_attr(docsrs, doc(cfg(feature = "account")))] -pub mod account_storage { - //! Storage Trait and Types definitions - - pub use identity_account_storage::crypto::*; - pub use identity_account_storage::error::*; - pub use identity_account_storage::identity::*; - pub use identity_account_storage::storage::*; - pub use identity_account_storage::types::*; - pub use identity_account_storage::utils::*; + pub use identity_iota_core::*; } // #[cfg(feature = "comm")] @@ -134,11 +90,11 @@ pub mod account_storage { pub mod prelude { //! Prelude of commonly used types + pub use identity_iota_core::IotaDID; + pub use identity_iota_core::IotaDocument; + pub use identity_core::crypto::KeyPair; pub use identity_core::crypto::KeyType; - pub use identity_iota_client::tangle::Client; - pub use identity_iota_client::Result; - pub use identity_iota_core::document::IotaDocument; pub use identity_resolver::Resolver; } diff --git a/identity_iota_client/Cargo.toml b/identity_iota_client_legacy/Cargo.toml similarity index 88% rename from identity_iota_client/Cargo.toml rename to identity_iota_client_legacy/Cargo.toml index a06276ed90..94a02fff7d 100644 --- a/identity_iota_client/Cargo.toml +++ b/identity_iota_client_legacy/Cargo.toml @@ -1,11 +1,12 @@ [package] -name = "identity_iota_client" +name = "identity_iota_client_legacy" version = "0.6.0" authors = ["IOTA Stiftung"] edition = "2021" homepage = "https://www.iota.org" keywords = ["iota", "tangle", "identity", "did"] license = "Apache-2.0" +publish = false readme = "./README.md" repository = "https://github.com/iotaledger/identity.rs" description = "Tangle Client integration for the IOTA DID Method." @@ -19,7 +20,7 @@ futures = { version = "0.3" } identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } identity_credential = { version = "=0.6.0", path = "../identity_credential", default-features = false, features = ["validator"] } identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } -identity_iota_core = { version = "=0.6.0", path = "../identity_iota_core", default-features = false } +identity_iota_core_legacy = { version = "=0.6.0", path = "../identity_iota_core_legacy", default-features = false } itertools = { version = "0.10" } lazy_static = { version = "1.4", default-features = false } log = { version = "0.4", default-features = false } @@ -51,7 +52,10 @@ tokio = { version = "1.17.0", default-features = false, features = ["macros"] } default = ["revocation-bitmap"] # Enables revocation with `RevocationBitmap2022`. -revocation-bitmap = ["identity_iota_core/revocation-bitmap", "identity_credential/revocation-bitmap"] +revocation-bitmap = [ + "identity_iota_core_legacy/revocation-bitmap", + "identity_credential/revocation-bitmap", +] [package.metadata.docs.rs] # To build locally: diff --git a/identity_iota_client/README.md b/identity_iota_client_legacy/README.md similarity index 100% rename from identity_iota_client/README.md rename to identity_iota_client_legacy/README.md diff --git a/identity_iota_client/src/chain/diff_chain.rs b/identity_iota_client_legacy/src/chain/diff_chain.rs similarity index 96% rename from identity_iota_client/src/chain/diff_chain.rs rename to identity_iota_client_legacy/src/chain/diff_chain.rs index 206bc628c7..ac94ca5e7e 100644 --- a/identity_iota_client/src/chain/diff_chain.rs +++ b/identity_iota_client_legacy/src/chain/diff_chain.rs @@ -10,11 +10,11 @@ use serde::Deserialize; use serde::Serialize; use identity_core::convert::FmtJson; -use identity_iota_core::did::IotaDID; -use identity_iota_core::diff::DiffMessage; -use identity_iota_core::tangle::Message; -use identity_iota_core::tangle::MessageId; -use identity_iota_core::tangle::MessageIdExt; +use identity_iota_core_legacy::did::IotaDID; +use identity_iota_core_legacy::diff::DiffMessage; +use identity_iota_core_legacy::tangle::Message; +use identity_iota_core_legacy::tangle::MessageId; +use identity_iota_core_legacy::tangle::MessageIdExt; use crate::chain::milestone::sort_by_milestone; use crate::chain::IntegrationChain; @@ -276,10 +276,10 @@ mod tests { use identity_core::json; use identity_did::did::DID; use identity_did::service::Service; - use identity_iota_core::diff::DiffMessage; - use identity_iota_core::document::IotaDocument; - use identity_iota_core::document::IotaService; - use identity_iota_core::tangle::MessageId; + use identity_iota_core_legacy::diff::DiffMessage; + use identity_iota_core_legacy::document::IotaDocument; + use identity_iota_core_legacy::document::IotaService; + use identity_iota_core_legacy::tangle::MessageId; use crate::document::ResolvedIotaDocument; use crate::tangle::ClientBuilder; diff --git a/identity_iota_client/src/chain/document_chain.rs b/identity_iota_client_legacy/src/chain/document_chain.rs similarity index 98% rename from identity_iota_client/src/chain/document_chain.rs rename to identity_iota_client_legacy/src/chain/document_chain.rs index 9523be9743..06d89672ba 100644 --- a/identity_iota_client/src/chain/document_chain.rs +++ b/identity_iota_client_legacy/src/chain/document_chain.rs @@ -5,9 +5,9 @@ use core::fmt::Display; use core::fmt::Formatter; use identity_core::convert::FmtJson; -use identity_iota_core::did::IotaDID; -use identity_iota_core::diff::DiffMessage; -use identity_iota_core::tangle::MessageId; +use identity_iota_core_legacy::did::IotaDID; +use identity_iota_core_legacy::diff::DiffMessage; +use identity_iota_core_legacy::tangle::MessageId; use serde::Deserialize; use serde::Serialize; @@ -177,9 +177,9 @@ mod test { use identity_did::verification::MethodRelationship; use identity_did::verification::MethodScope; use identity_did::verification::MethodType; - use identity_iota_core::did::IotaDIDUrl; - use identity_iota_core::document::IotaDocument; - use identity_iota_core::document::IotaVerificationMethod; + use identity_iota_core_legacy::did::IotaDIDUrl; + use identity_iota_core_legacy::document::IotaDocument; + use identity_iota_core_legacy::document::IotaVerificationMethod; use crate::tangle::TangleRef; use crate::Error; diff --git a/identity_iota_client/src/chain/document_history.rs b/identity_iota_client_legacy/src/chain/document_history.rs similarity index 95% rename from identity_iota_client/src/chain/document_history.rs rename to identity_iota_client_legacy/src/chain/document_history.rs index 193354766c..b07be5d1cb 100644 --- a/identity_iota_client/src/chain/document_history.rs +++ b/identity_iota_client_legacy/src/chain/document_history.rs @@ -4,11 +4,11 @@ use std::collections::HashSet; use std::ops::Deref; -use identity_iota_core::did::IotaDID; -use identity_iota_core::diff::DiffMessage; -use identity_iota_core::document::IotaDocument; -use identity_iota_core::tangle::Message; -use identity_iota_core::tangle::MessageId; +use identity_iota_core_legacy::did::IotaDID; +use identity_iota_core_legacy::diff::DiffMessage; +use identity_iota_core_legacy::document::IotaDocument; +use identity_iota_core_legacy::tangle::Message; +use identity_iota_core_legacy::tangle::MessageId; use serde; use serde::Deserialize; use serde::Serialize; diff --git a/identity_iota_client/src/chain/integration_chain.rs b/identity_iota_client_legacy/src/chain/integration_chain.rs similarity index 96% rename from identity_iota_client/src/chain/integration_chain.rs rename to identity_iota_client_legacy/src/chain/integration_chain.rs index d0be119543..5eec5068b7 100644 --- a/identity_iota_client/src/chain/integration_chain.rs +++ b/identity_iota_client_legacy/src/chain/integration_chain.rs @@ -6,11 +6,11 @@ use core::fmt::Formatter; use core::mem; use identity_core::convert::FmtJson; -use identity_iota_core::did::IotaDID; -use identity_iota_core::document::IotaDocument; -use identity_iota_core::tangle::Message; -use identity_iota_core::tangle::MessageId; -use identity_iota_core::tangle::MessageIdExt; +use identity_iota_core_legacy::did::IotaDID; +use identity_iota_core_legacy::document::IotaDocument; +use identity_iota_core_legacy::tangle::Message; +use identity_iota_core_legacy::tangle::MessageId; +use identity_iota_core_legacy::tangle::MessageIdExt; use serde; use serde::Deserialize; use serde::Serialize; diff --git a/identity_iota_client/src/chain/milestone.rs b/identity_iota_client_legacy/src/chain/milestone.rs similarity index 97% rename from identity_iota_client/src/chain/milestone.rs rename to identity_iota_client_legacy/src/chain/milestone.rs index 649d147488..061ea7c822 100644 --- a/identity_iota_client/src/chain/milestone.rs +++ b/identity_iota_client_legacy/src/chain/milestone.rs @@ -67,8 +67,8 @@ fn sort_by_milestone_index(messages_milestones: Vec<(Option, #[cfg(test)] mod tests { - use identity_iota_core::did::IotaDID; - use identity_iota_core::tangle::MessageId; + use identity_iota_core_legacy::did::IotaDID; + use identity_iota_core_legacy::tangle::MessageId; use super::*; diff --git a/identity_iota_client/src/chain/mod.rs b/identity_iota_client_legacy/src/chain/mod.rs similarity index 100% rename from identity_iota_client/src/chain/mod.rs rename to identity_iota_client_legacy/src/chain/mod.rs diff --git a/identity_iota_client/src/document/mod.rs b/identity_iota_client_legacy/src/document/mod.rs similarity index 100% rename from identity_iota_client/src/document/mod.rs rename to identity_iota_client_legacy/src/document/mod.rs diff --git a/identity_iota_client/src/document/resolved_iota_document.rs b/identity_iota_client_legacy/src/document/resolved_iota_document.rs similarity index 93% rename from identity_iota_client/src/document/resolved_iota_document.rs rename to identity_iota_client_legacy/src/document/resolved_iota_document.rs index e6030ae64f..4102f7aa86 100644 --- a/identity_iota_client/src/document/resolved_iota_document.rs +++ b/identity_iota_client_legacy/src/document/resolved_iota_document.rs @@ -10,11 +10,11 @@ use serde::Deserialize; use serde::Serialize; use identity_core::convert::FmtJson; -use identity_iota_core::did::IotaDID; -use identity_iota_core::diff::DiffMessage; -use identity_iota_core::document::IotaDocument; -use identity_iota_core::tangle::MessageId; -use identity_iota_core::tangle::MessageIdExt; +use identity_iota_core_legacy::did::IotaDID; +use identity_iota_core_legacy::diff::DiffMessage; +use identity_iota_core_legacy::document::IotaDocument; +use identity_iota_core_legacy::tangle::MessageId; +use identity_iota_core_legacy::tangle::MessageIdExt; use crate::error::Result; use crate::tangle::TangleRef; diff --git a/identity_iota_client/src/error.rs b/identity_iota_client_legacy/src/error.rs similarity index 96% rename from identity_iota_client/src/error.rs rename to identity_iota_client_legacy/src/error.rs index 4e207a68af..c69f091773 100644 --- a/identity_iota_client/src/error.rs +++ b/identity_iota_client_legacy/src/error.rs @@ -16,7 +16,7 @@ pub enum Error { #[error("{0}")] ClientError(#[from] iota_client::error::Error), #[error("{0}")] - IotaCoreError(#[from] identity_iota_core::Error), + IotaCoreError(#[from] identity_iota_core_legacy::Error), #[error("{0}")] DIDNotFound(String), diff --git a/identity_iota_client/src/lib.rs b/identity_iota_client_legacy/src/lib.rs similarity index 100% rename from identity_iota_client/src/lib.rs rename to identity_iota_client_legacy/src/lib.rs diff --git a/identity_iota_client/src/tangle/client.rs b/identity_iota_client_legacy/src/tangle/client.rs similarity index 97% rename from identity_iota_client/src/tangle/client.rs rename to identity_iota_client_legacy/src/tangle/client.rs index d5e4e479bb..648ebec592 100644 --- a/identity_iota_client/src/tangle/client.rs +++ b/identity_iota_client_legacy/src/tangle/client.rs @@ -5,12 +5,12 @@ use bee_rest_api::types::dtos::LedgerInclusionStateDto; use futures::stream::FuturesUnordered; use futures::stream::TryStreamExt; use identity_core::convert::ToJson; -use identity_iota_core::did::IotaDID; -use identity_iota_core::diff::DiffMessage; -use identity_iota_core::document::IotaDocument; -use identity_iota_core::tangle::Message; -use identity_iota_core::tangle::MessageId; -use identity_iota_core::tangle::Network; +use identity_iota_core_legacy::did::IotaDID; +use identity_iota_core_legacy::diff::DiffMessage; +use identity_iota_core_legacy::document::IotaDocument; +use identity_iota_core_legacy::tangle::Message; +use identity_iota_core_legacy::tangle::MessageId; +use identity_iota_core_legacy::tangle::Network; use iota_client::Client as IotaClient; use iota_client::Error as IotaClientError; diff --git a/identity_iota_client/src/tangle/client_builder.rs b/identity_iota_client_legacy/src/tangle/client_builder.rs similarity index 99% rename from identity_iota_client/src/tangle/client_builder.rs rename to identity_iota_client_legacy/src/tangle/client_builder.rs index 803a265212..53776e3764 100644 --- a/identity_iota_client/src/tangle/client_builder.rs +++ b/identity_iota_client_legacy/src/tangle/client_builder.rs @@ -5,7 +5,7 @@ use core::fmt::Debug; use core::fmt::Formatter; use std::time::Duration; -use identity_iota_core::tangle::Network; +use identity_iota_core_legacy::tangle::Network; use crate::error::Result; use crate::tangle::Client; diff --git a/identity_iota_client/src/tangle/explorer.rs b/identity_iota_client_legacy/src/tangle/explorer.rs similarity index 94% rename from identity_iota_client/src/tangle/explorer.rs rename to identity_iota_client_legacy/src/tangle/explorer.rs index e18e7f1435..a1640d8754 100644 --- a/identity_iota_client/src/tangle/explorer.rs +++ b/identity_iota_client_legacy/src/tangle/explorer.rs @@ -8,7 +8,7 @@ use std::str::FromStr; use identity_core::common::Url; use identity_did::did::DID; -use identity_iota_core::tangle::MessageId; +use identity_iota_core_legacy::tangle::MessageId; use serde; use serde::Deserialize; use serde::Serialize; @@ -29,15 +29,15 @@ lazy_static::lazy_static! { /// # Example /// /// ``` -/// # use identity_iota_core::did::IotaDID; -/// # use identity_iota_client::tangle::ExplorerUrl; +/// # use identity_iota_core_legacy::did::IotaDID; +/// # use identity_iota_client_legacy::tangle::ExplorerUrl; /// let explorer = ExplorerUrl::mainnet(); /// let did = IotaDID::parse("did:iota:H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV")?; /// assert_eq!( /// explorer.resolver_url(&did)?, /// "https://explorer.iota.org/mainnet/identity-resolver/did:iota:H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV" /// ); -/// # Ok::<(), identity_iota_client::Error>(()) +/// # Ok::<(), identity_iota_client_legacy::Error>(()) /// ``` #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] #[repr(transparent)] @@ -66,9 +66,9 @@ impl ExplorerUrl { /// /// Point to a Tangle explorer deployed locally. /// ``` - /// # use identity_iota_client::tangle::ExplorerUrl; + /// # use identity_iota_client_legacy::tangle::ExplorerUrl; /// let explorer = ExplorerUrl::parse("http://127.0.0.1:8082/")?; - /// # Ok::<(), identity_iota_client::Error>(()) + /// # Ok::<(), identity_iota_client_legacy::Error>(()) /// ``` pub fn parse(url: &str) -> Result { let url: Url = Url::parse(url).map_err(|_| Error::InvalidExplorerURL)?; @@ -152,7 +152,7 @@ impl Display for ExplorerUrl { #[cfg(test)] mod tests { - use identity_iota_core::did::IotaDID; + use identity_iota_core_legacy::did::IotaDID; use super::*; diff --git a/identity_iota_client/src/tangle/message/compression_brotli.rs b/identity_iota_client_legacy/src/tangle/message/compression_brotli.rs similarity index 96% rename from identity_iota_client/src/tangle/message/compression_brotli.rs rename to identity_iota_client_legacy/src/tangle/message/compression_brotli.rs index 5e67fb2c51..7f1cbf99c6 100644 --- a/identity_iota_client/src/tangle/message/compression_brotli.rs +++ b/identity_iota_client_legacy/src/tangle/message/compression_brotli.rs @@ -31,7 +31,7 @@ mod test { use identity_core::convert::ToJson; use identity_core::crypto::KeyPair; use identity_core::crypto::KeyType; - use identity_iota_core::document::IotaDocument; + use identity_iota_core_legacy::document::IotaDocument; use super::*; diff --git a/identity_iota_client/src/tangle/message/message_encoding.rs b/identity_iota_client_legacy/src/tangle/message/message_encoding.rs similarity index 100% rename from identity_iota_client/src/tangle/message/message_encoding.rs rename to identity_iota_client_legacy/src/tangle/message/message_encoding.rs diff --git a/identity_iota_client/src/tangle/message/message_ext.rs b/identity_iota_client_legacy/src/tangle/message/message_ext.rs similarity index 94% rename from identity_iota_client/src/tangle/message/message_ext.rs rename to identity_iota_client_legacy/src/tangle/message/message_ext.rs index 6c35186e78..2b2b340ffb 100644 --- a/identity_iota_client/src/tangle/message/message_ext.rs +++ b/identity_iota_client_legacy/src/tangle/message/message_ext.rs @@ -4,10 +4,10 @@ use identity_core::convert::FromJson; use identity_core::convert::ToJson; use identity_did::did::DID; -use identity_iota_core::did::IotaDID; -use identity_iota_core::diff::DiffMessage; -use identity_iota_core::tangle::Message; -use identity_iota_core::tangle::MessageId; +use identity_iota_core_legacy::did::IotaDID; +use identity_iota_core_legacy::diff::DiffMessage; +use identity_iota_core_legacy::tangle::Message; +use identity_iota_core_legacy::tangle::MessageId; use iota_client::bee_message::payload::transaction::Essence; use iota_client::bee_message::payload::Payload; @@ -141,9 +141,9 @@ mod test { use identity_did::service::ServiceBuilder; use identity_did::service::ServiceEndpoint; use identity_did::verification::MethodScope; - use identity_iota_core::document::IotaDocument; - use identity_iota_core::document::IotaVerificationMethod; - use identity_iota_core::tangle::MessageId; + use identity_iota_core_legacy::document::IotaDocument; + use identity_iota_core_legacy::document::IotaVerificationMethod; + use identity_iota_core_legacy::tangle::MessageId; use crate::document::ResolvedIotaDocument; use crate::tangle::message::message_encoding::DIDMessageEncoding; diff --git a/identity_iota_client/src/tangle/message/message_index.rs b/identity_iota_client_legacy/src/tangle/message/message_index.rs similarity index 98% rename from identity_iota_client/src/tangle/message/message_index.rs rename to identity_iota_client_legacy/src/tangle/message/message_index.rs index a2159f6953..58d4f096c0 100644 --- a/identity_iota_client/src/tangle/message/message_index.rs +++ b/identity_iota_client_legacy/src/tangle/message/message_index.rs @@ -8,7 +8,7 @@ use core::ops::Deref; use core::ops::DerefMut; use std::collections::HashMap; -use identity_iota_core::tangle::MessageId; +use identity_iota_core_legacy::tangle::MessageId; use crate::tangle::TangleRef; @@ -115,7 +115,7 @@ where #[cfg(test)] mod tests { - use identity_iota_core::did::IotaDID; + use identity_iota_core_legacy::did::IotaDID; use super::*; diff --git a/identity_iota_client/src/tangle/message/message_version.rs b/identity_iota_client_legacy/src/tangle/message/message_version.rs similarity index 100% rename from identity_iota_client/src/tangle/message/message_version.rs rename to identity_iota_client_legacy/src/tangle/message/message_version.rs diff --git a/identity_iota_client/src/tangle/message/mod.rs b/identity_iota_client_legacy/src/tangle/message/mod.rs similarity index 100% rename from identity_iota_client/src/tangle/message/mod.rs rename to identity_iota_client_legacy/src/tangle/message/mod.rs diff --git a/identity_iota_client/src/tangle/mod.rs b/identity_iota_client_legacy/src/tangle/mod.rs similarity index 100% rename from identity_iota_client/src/tangle/mod.rs rename to identity_iota_client_legacy/src/tangle/mod.rs diff --git a/identity_iota_client/src/tangle/publish.rs b/identity_iota_client_legacy/src/tangle/publish.rs similarity index 96% rename from identity_iota_client/src/tangle/publish.rs rename to identity_iota_client_legacy/src/tangle/publish.rs index d1d38a507a..308a1fff3e 100644 --- a/identity_iota_client/src/tangle/publish.rs +++ b/identity_iota_client_legacy/src/tangle/publish.rs @@ -1,8 +1,8 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use identity_iota_core::document::IotaDocument; -use identity_iota_core::document::IotaVerificationMethod; +use identity_iota_core_legacy::document::IotaDocument; +use identity_iota_core_legacy::document::IotaVerificationMethod; /// Determines whether an updated document needs to be published as an integration or diff message. #[deprecated(since = "0.5.0", note = "diff chain features are slated for removal")] @@ -40,8 +40,8 @@ mod test { use identity_core::crypto::KeyType; use identity_did::did::DID; use identity_did::verification::MethodScope; - use identity_iota_core::did::IotaDIDUrl; - use identity_iota_core::document::IotaVerificationMethod; + use identity_iota_core_legacy::did::IotaDIDUrl; + use identity_iota_core_legacy::document::IotaVerificationMethod; use crate::Result; diff --git a/identity_iota_client/src/tangle/receipt.rs b/identity_iota_client_legacy/src/tangle/receipt.rs similarity index 88% rename from identity_iota_client/src/tangle/receipt.rs rename to identity_iota_client_legacy/src/tangle/receipt.rs index cc00c82825..b414b0ef3e 100644 --- a/identity_iota_client/src/tangle/receipt.rs +++ b/identity_iota_client_legacy/src/tangle/receipt.rs @@ -5,9 +5,9 @@ use serde; use serde::Deserialize; use serde::Serialize; -use identity_iota_core::tangle::Message; -use identity_iota_core::tangle::MessageId; -use identity_iota_core::tangle::Network; +use identity_iota_core_legacy::tangle::Message; +use identity_iota_core_legacy::tangle::MessageId; +use identity_iota_core_legacy::tangle::Network; #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct Receipt { diff --git a/identity_iota_client/src/tangle/resolver.rs b/identity_iota_client_legacy/src/tangle/resolver.rs similarity index 98% rename from identity_iota_client/src/tangle/resolver.rs rename to identity_iota_client_legacy/src/tangle/resolver.rs index db94ebccc5..22331b8951 100644 --- a/identity_iota_client/src/tangle/resolver.rs +++ b/identity_iota_client_legacy/src/tangle/resolver.rs @@ -14,10 +14,10 @@ use identity_credential::validator::FailFast; use identity_credential::validator::PresentationValidationOptions; use identity_credential::validator::PresentationValidator; use identity_credential::validator::ValidatorDocument; -use identity_iota_core::did::IotaDID; -use identity_iota_core::diff::DiffMessage; -use identity_iota_core::document::IotaDocument; -use identity_iota_core::tangle::NetworkName; +use identity_iota_core_legacy::did::IotaDID; +use identity_iota_core_legacy::diff::DiffMessage; +use identity_iota_core_legacy::document::IotaDocument; +use identity_iota_core_legacy::tangle::NetworkName; use crate::chain::ChainHistory; use crate::chain::DocumentHistory; @@ -47,7 +47,7 @@ where C: SharedPtr, { /// Constructs a new [`Resolver`] with a default [`Client`] for - /// the [`Mainnet`](identity_iota_core::tangle::Network::Mainnet). + /// the [`Mainnet`](identity_iota_core_legacy::tangle::Network::Mainnet). /// /// See also [`Resolver::builder`]. pub async fn new() -> Result { @@ -292,8 +292,8 @@ mod tests { use identity_did::document::CoreDocument; use identity_did::verifiable::VerifierOptions; use identity_did::verification::VerificationMethod; - use identity_iota_core::document::IotaDocument; - use identity_iota_core::tangle::Network; + use identity_iota_core_legacy::document::IotaDocument; + use identity_iota_core_legacy::tangle::Network; use super::*; diff --git a/identity_iota_client/src/tangle/traits.rs b/identity_iota_client_legacy/src/tangle/traits.rs similarity index 92% rename from identity_iota_client/src/tangle/traits.rs rename to identity_iota_client_legacy/src/tangle/traits.rs index ae4d820537..c03a1f4a8e 100644 --- a/identity_iota_client/src/tangle/traits.rs +++ b/identity_iota_client_legacy/src/tangle/traits.rs @@ -5,9 +5,9 @@ use std::ops::Deref; use std::rc::Rc; use std::sync::Arc; -use identity_iota_core::did::IotaDID; -use identity_iota_core::diff::DiffMessage; -use identity_iota_core::tangle::MessageId; +use identity_iota_core_legacy::did::IotaDID; +use identity_iota_core_legacy::diff::DiffMessage; +use identity_iota_core_legacy::tangle::MessageId; use crate::document::ResolvedIotaDocument; use crate::error::Result; diff --git a/identity_iota_core/Cargo.toml b/identity_iota_core/Cargo.toml index cc53afa939..223328ce96 100644 --- a/identity_iota_core/Cargo.toml +++ b/identity_iota_core/Cargo.toml @@ -4,25 +4,36 @@ version = "0.6.0" authors = ["IOTA Stiftung"] edition = "2021" homepage = "https://www.iota.org" -keywords = ["iota", "tangle", "identity", "did"] +keywords = ["iota", "tangle", "utxo", "shimmer", "stardust", "identity"] license = "Apache-2.0" -readme = "./README.md" +readme = "../README.md" repository = "https://github.com/iotaledger/identity.rs" -description = "Core data structures for the IOTA DID Method." +rust-version = "1.62" +description = "An IOTA Ledger integration for the IOTA DID Method." [dependencies] -bee-message = { version = "0.1.6", default-features = false, features = ["serde"] } +# Ensure bee-block always matches the version used by iota-client. +bee-block = { version = "1.0.0-beta.7", default-features = false, features = ["std"], optional = true } identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } +identity_credential = { version = "=0.6.0", path = "../identity_credential", default-features = false, features = ["validator"] } identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } -lazy_static = { version = "1.4", default-features = false } + +async-trait = { version = "0.1.56", default-features = false, optional = true } +futures = { version = "0.3" } +iota-client = { version = "2.0.0-beta.3", default-features = false, features = ["tls"], optional = true } +num-derive = { version = "0.3", default-features = false } +num-traits = { version = "0.2", default-features = false, features = ["std"] } +once_cell = { version = "1", default-features = false, features = ["std"] } +prefix-hex = { version = "0.4", default-features = false } serde = { version = "1.0", default-features = false, features = ["std", "derive"] } -strum = { version = "0.24.0", default-features = false, features = ["std", "derive"] } +strum = { version = "0.21", features = ["derive"] } thiserror = { version = "1.0", default-features = false } -[dependencies.iota-crypto] -version = "0.12.1" -default-features = false -features = ["blake2b"] +[dev-dependencies] +anyhow = { version = "1.0.57" } +iota-crypto = { version = "0.12.1", default-features = false, features = ["bip39", "bip39-en"] } +proptest = { version = "1.0.0", default-features = false, features = ["std"] } +tokio = { version = "1.17.0", default-features = false, features = ["rt-multi-thread", "macros"] } [package.metadata.docs.rs] # To build locally: @@ -31,6 +42,12 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [features] -default = ["revocation-bitmap"] +default = ["client", "iota-client", "revocation-bitmap", "send-sync-client-ext"] +# Exposes the `IotaIdentityClient` and `IotaIdentityClientExt` traits. +client = ["dep:async-trait", "dep:bee-block"] +# Enables the iota-client dependency, the client trait implementations for it, and the `IotaClientExt` trait. +iota-client = ["dep:iota-client", "client"] # Enables revocation with `RevocationBitmap2022`. revocation-bitmap = ["identity_did/revocation-bitmap"] +# Adds Send bounds on the futures produces by the client extension traits. +send-sync-client-ext = [] diff --git a/identity_iota_core/README.md b/identity_iota_core/README.md index 229ae49c25..74576bb24e 100644 --- a/identity_iota_core/README.md +++ b/identity_iota_core/README.md @@ -1,4 +1,4 @@ -IOTA Identity Core -=== +IOTA Identity +=== -This crate provides the core data structures for the [IOTA DID Method Specification](https://wiki.iota.org/identity.rs/specs/did/iota_did_method_spec). +This crate provides the core data structures for the [IOTA DID Method Specification](https://wiki.iota.org/identity.rs/specs/did/iota_did_method_spec). It provides interfaces for publishing and resolving DID Documents to and from the Tangle according to the IOTA DID Method Specification. diff --git a/identity_stardust/src/client/identity_client.rs b/identity_iota_core/src/client/identity_client.rs similarity index 86% rename from identity_stardust/src/client/identity_client.rs rename to identity_iota_core/src/client/identity_client.rs index ba88e918f8..b53a2d363f 100644 --- a/identity_stardust/src/client/identity_client.rs +++ b/identity_iota_core/src/client/identity_client.rs @@ -13,15 +13,15 @@ use crate::block::output::OutputId; use crate::block::output::RentStructure; use crate::block::output::UnlockCondition; use crate::Error; +use crate::IotaDID; +use crate::IotaDocument; use crate::NetworkName; use crate::Result; -use crate::StardustDID; -use crate::StardustDocument; -/// Helper functions necessary for the [`StardustIdentityClientExt`] trait. +/// Helper functions necessary for the [`IotaIdentityClientExt`] trait. #[cfg_attr(feature = "send-sync-client-ext", async_trait::async_trait)] #[cfg_attr(not(feature = "send-sync-client-ext"), async_trait::async_trait(?Send))] -pub trait StardustIdentityClient { +pub trait IotaIdentityClient { /// Return the Bech32 human-readable part (HRP) of the network. /// /// E.g. "iota", "atoi", "smr", "rms". @@ -38,11 +38,11 @@ pub trait StardustIdentityClient { /// and resolution of DID documents in Alias Outputs. /// /// This trait is not intended to be implemented directly, a blanket implementation is -/// provided for [`StardustIdentityClient`] implementers. +/// provided for [`IotaIdentityClient`] implementers. #[cfg_attr(feature = "send-sync-client-ext", async_trait::async_trait)] #[cfg_attr(not(feature = "send-sync-client-ext"), async_trait::async_trait(?Send))] -pub trait StardustIdentityClientExt: StardustIdentityClient { +pub trait IotaIdentityClientExt: IotaIdentityClient { /// Create a DID with a new Alias Output containing the given `document`. /// /// The `address` will be set as the state controller and governor unlock conditions. @@ -59,7 +59,7 @@ pub trait StardustIdentityClientExt: StardustIdentityClient { async fn new_did_output( &self, address: Address, - document: StardustDocument, + document: IotaDocument, rent_structure: Option, ) -> Result { let rent_structure: RentStructure = if let Some(rent) = rent_structure { @@ -93,7 +93,7 @@ pub trait StardustIdentityClientExt: StardustIdentityClient { /// # Errors /// /// Returns `Err` when failing to resolve the DID contained in `document`. - async fn update_did_output(&self, document: StardustDocument) -> Result { + async fn update_did_output(&self, document: IotaDocument) -> Result { let id: AliasId = AliasId::from(document.id()); let (_, alias_output) = self.get_alias_output(id).await?; @@ -120,7 +120,7 @@ pub trait StardustIdentityClientExt: StardustIdentityClient { /// # Errors /// /// Returns `Err` when failing to resolve the `did`. - async fn deactivate_did_output(&self, did: &StardustDID) -> Result { + async fn deactivate_did_output(&self, did: &IotaDID) -> Result { let alias_id: AliasId = AliasId::from(did); let (_, alias_output) = self.get_alias_output(alias_id).await?; @@ -135,21 +135,21 @@ pub trait StardustIdentityClientExt: StardustIdentityClient { alias_output_builder.finish().map_err(Error::AliasOutputBuildError) } - /// Resolve a [`StardustDocument`]. Returns an empty, deactivated document if the state metadata + /// Resolve a [`IotaDocument`]. Returns an empty, deactivated document if the state metadata /// of the Alias Output is empty. /// /// # Errors /// /// - [`NetworkMismatch`](Error::NetworkMismatch) if the network of the DID and client differ. /// - [`NotFound`](iota_client::Error::NotFound) if the associated Alias Output was not found. - async fn resolve_did(&self, did: &StardustDID) -> Result { + async fn resolve_did(&self, did: &IotaDID) -> Result { validate_network(self, did).await?; let id: AliasId = AliasId::from(did); let (_, alias_output) = self.get_alias_output(id).await?; let document: &[u8] = alias_output.state_metadata(); - StardustDocument::unpack(did, document, true) + IotaDocument::unpack(did, document, true) } /// Fetches the [`AliasOutput`] associated with the given DID. @@ -158,7 +158,7 @@ pub trait StardustIdentityClientExt: StardustIdentityClient { /// /// - [`NetworkMismatch`](Error::NetworkMismatch) if the network of the DID and client differ. /// - [`NotFound`](iota_client::Error::NotFound) if the associated Alias Output was not found. - async fn resolve_did_output(&self, did: &StardustDID) -> Result { + async fn resolve_did_output(&self, did: &IotaDID) -> Result { validate_network(self, did).await?; let id: AliasId = AliasId::from(did); @@ -174,11 +174,11 @@ pub trait StardustIdentityClientExt: StardustIdentityClient { } } -impl StardustIdentityClientExt for T where T: StardustIdentityClient {} +impl IotaIdentityClientExt for T where T: IotaIdentityClient {} -pub(super) async fn validate_network(client: &T, did: &StardustDID) -> Result<()> +pub(super) async fn validate_network(client: &T, did: &IotaDID) -> Result<()> where - T: StardustIdentityClient + ?Sized, + T: IotaIdentityClient + ?Sized, { let network_hrp: String = client.get_network_hrp().await?; if did.network_str() != network_hrp.as_str() { diff --git a/identity_stardust/src/client/iota_client.rs b/identity_iota_core/src/client/iota_client.rs similarity index 90% rename from identity_stardust/src/client/iota_client.rs rename to identity_iota_core/src/client/iota_client.rs index 1fdf33d881..b273d25e76 100644 --- a/identity_stardust/src/client/iota_client.rs +++ b/identity_iota_core/src/client/iota_client.rs @@ -18,17 +18,17 @@ use crate::block::Block; use crate::client::identity_client::validate_network; use crate::error::Result; use crate::Error; +use crate::IotaDID; +use crate::IotaDocument; +use crate::IotaIdentityClient; +use crate::IotaIdentityClientExt; use crate::NetworkName; -use crate::StardustDID; -use crate::StardustDocument; -use crate::StardustIdentityClient; -use crate::StardustIdentityClientExt; /// An extension trait for [`Client`] that provides helper functions for publication /// and deletion of DID documents in Alias Outputs. #[cfg_attr(feature = "send-sync-client-ext", async_trait::async_trait)] #[cfg_attr(not(feature = "send-sync-client-ext"), async_trait::async_trait(?Send))] -pub trait StardustClientExt: StardustIdentityClient { +pub trait IotaClientExt: IotaIdentityClient { /// Publish the given `alias_output` with the provided `secret_manager`, and returns /// the DID document extracted from the published block. /// @@ -37,11 +37,8 @@ pub trait StardustClientExt: StardustIdentityClient { /// the storage deposit amount specified on `alias_output`. /// /// This method modifies the on-ledger state. - async fn publish_did_output( - &self, - secret_manager: &SecretManager, - alias_output: AliasOutput, - ) -> Result; + async fn publish_did_output(&self, secret_manager: &SecretManager, alias_output: AliasOutput) + -> Result; /// Destroy the Alias Output containing the given `did`, sending its tokens to a new Basic Output /// unlockable by `address`. @@ -51,25 +48,25 @@ pub trait StardustClientExt: StardustIdentityClient { /// # WARNING /// /// This destroys the Alias Output and DID document, rendering them permanently unrecoverable. - async fn delete_did_output(&self, secret_manager: &SecretManager, address: Address, did: &StardustDID) -> Result<()>; + async fn delete_did_output(&self, secret_manager: &SecretManager, address: Address, did: &IotaDID) -> Result<()>; } /// An extension trait for [`Client`] that provides helper functions for publication /// and deletion of DID documents in Alias Outputs. #[cfg_attr(feature = "send-sync-client-ext", async_trait::async_trait)] #[cfg_attr(not(feature = "send-sync-client-ext"), async_trait::async_trait(?Send))] -impl StardustClientExt for Client { +impl IotaClientExt for Client { async fn publish_did_output( &self, secret_manager: &SecretManager, alias_output: AliasOutput, - ) -> Result { + ) -> Result { let block: Block = publish_output(self, secret_manager, alias_output) .await .map_err(|err| Error::DIDUpdateError("publish_did_output: publish failed", Some(err)))?; let network: NetworkName = self.network_name().await?; - StardustDocument::unpack_from_block(&network, &block)? + IotaDocument::unpack_from_block(&network, &block)? .into_iter() .next() .ok_or(Error::DIDUpdateError( @@ -78,7 +75,7 @@ impl StardustClientExt for Client { )) } - async fn delete_did_output(&self, secret_manager: &SecretManager, address: Address, did: &StardustDID) -> Result<()> { + async fn delete_did_output(&self, secret_manager: &SecretManager, address: Address, did: &IotaDID) -> Result<()> { validate_network(self, did).await?; let alias_id: AliasId = AliasId::from(did); @@ -112,7 +109,7 @@ impl StardustClientExt for Client { #[cfg_attr(feature = "send-sync-client-ext", async_trait::async_trait)] #[cfg_attr(not(feature = "send-sync-client-ext"), async_trait::async_trait(?Send))] -impl StardustIdentityClient for Client { +impl IotaIdentityClient for Client { async fn get_network_hrp(&self) -> Result { self.get_bech32_hrp().await.map_err(Error::DIDResolutionError) } diff --git a/identity_stardust/src/client/mod.rs b/identity_iota_core/src/client/mod.rs similarity index 55% rename from identity_stardust/src/client/mod.rs rename to identity_iota_core/src/client/mod.rs index 2565cdfc54..b1cfb4b17f 100644 --- a/identity_stardust/src/client/mod.rs +++ b/identity_iota_core/src/client/mod.rs @@ -1,11 +1,11 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -pub use identity_client::StardustIdentityClient; -pub use identity_client::StardustIdentityClientExt; +pub use identity_client::IotaIdentityClient; +pub use identity_client::IotaIdentityClientExt; #[cfg(feature = "iota-client")] -pub use self::iota_client::StardustClientExt; +pub use self::iota_client::IotaClientExt; mod identity_client; #[cfg(feature = "iota-client")] diff --git a/identity_iota_core/src/did/iota_did.rs b/identity_iota_core/src/did/iota_did.rs index 9f2ccd6b86..8f9c610da4 100644 --- a/identity_iota_core/src/did/iota_did.rs +++ b/identity_iota_core/src/did/iota_did.rs @@ -6,31 +6,19 @@ use core::fmt::Debug; use core::fmt::Display; use core::fmt::Formatter; use core::str::FromStr; -use std::convert::TryInto; - -use crypto::hashes::blake2b::Blake2b256; -use crypto::hashes::Digest; -use serde; -use serde::Deserialize; -use serde::Serialize; use identity_core::common::KeyComparable; -use identity_core::utils::BaseEncoding; use identity_did::did::BaseDIDUrl; use identity_did::did::CoreDID; use identity_did::did::DIDError; use identity_did::did::DIDUrl; use identity_did::did::DID; +use serde::Deserialize; +use serde::Serialize; -use crate::did::Segments; -use crate::error::Error; -use crate::error::Result; -use crate::tangle::Network; -use crate::tangle::NetworkName; -use crate::try_construct_did; +use crate::NetworkName; -// The hash size of BLAKE2b-256 (32-bytes) -const BLAKE2B_256_LEN: usize = 32; +pub type Result = std::result::Result; /// A DID URL conforming to the IOTA DID method specification. /// @@ -41,7 +29,7 @@ pub type IotaDIDUrl = DIDUrl; /// /// This is a thin wrapper around the [`DID`][`CoreDID`] type from the /// [`identity_did`][`identity_did`] crate. -#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)] +#[derive(Clone, Hash, Debug, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)] #[repr(transparent)] #[serde(into = "CoreDID", try_from = "CoreDID")] pub struct IotaDID(CoreDID); @@ -53,135 +41,146 @@ impl IotaDID { /// The IOTA DID method name (`"iota"`). pub const METHOD: &'static str = "iota"; - /// The default Tangle network (`"main"`). - pub const DEFAULT_NETWORK: &'static str = "main"; + /// The default network name (`"iota"`). + pub const DEFAULT_NETWORK: &'static str = "iota"; - /// Converts an owned [`CoreDID`] to an [`IotaDID`]. - /// - /// # Errors - /// - /// Returns `Err` if the input is not a valid [`IotaDID`]. - pub fn try_from_core(did: CoreDID) -> Result { - Self::check_validity(&did)?; + // The length of an Alias ID, which is a BLAKE2b-256 hash (32-bytes). + pub(crate) const TAG_BYTES_LEN: usize = 32; - Ok(Self(Self::normalize(did))) - } + // =========================================================================== + // Constructors + // =========================================================================== - /// Parses an [`IotaDID`] from the given `input`. - /// - /// # Errors + /// Constructs a new [`IotaDID`] from a byte representation of the tag and the given + /// network name. /// - /// Returns `Err` if the input is not a valid [`IotaDID`]. - pub fn parse(input: impl AsRef) -> Result { - CoreDID::parse(input).map_err(Into::into).and_then(Self::try_from_core) - } - - /// Creates a new [`IotaDID`] with a tag derived from the given `public` key. + /// See also [`IotaDID::placeholder`]. /// - /// # Errors + /// # Example /// - /// Returns `Err` if the input does not form a valid [`IotaDID`]. - pub fn new(public: &[u8]) -> Result { - try_construct_did!(public).map_err(Into::into) + /// ``` + /// # use identity_did::did::DID; + /// # use identity_iota_core::NetworkName; + /// # use identity_iota_core::IotaDID; + /// # + /// let did = IotaDID::new(&[1;32], &NetworkName::try_from("smr").unwrap()); + /// assert_eq!(did.as_str(), "did:iota:smr:0x0101010101010101010101010101010101010101010101010101010101010101"); + pub fn new(bytes: &[u8; 32], network_name: &NetworkName) -> Self { + let tag = prefix_hex::encode(bytes); + let did: String = format!("did:{}:{}:{}", Self::METHOD, network_name, tag); + + Self::parse(did).expect("DIDs constructed with new should be valid") } - /// Creates a new [`IotaDID`] from the given `public` key and `network`. + /// Creates a new placeholder [`IotaDID`] with the given network name. /// - /// # Errors + /// # Example /// - /// Returns `Err` if the input does not form a valid [`IotaDID`] or the `network` is invalid. - /// See [`NetworkName`] for validation requirements. - pub fn new_with_network(public: &[u8], network: impl TryInto) -> Result { - let network_name = network.try_into().map_err(|_| Error::InvalidNetworkName)?; - try_construct_did!(public, network_name.as_ref()).map_err(Into::into) + /// ``` + /// # use identity_did::did::DID; + /// # use identity_iota_core::NetworkName; + /// # use identity_iota_core::IotaDID; + /// # + /// let placeholder = IotaDID::placeholder(&NetworkName::try_from("smr").unwrap()); + /// assert_eq!(placeholder.as_str(), "did:iota:smr:0x0000000000000000000000000000000000000000000000000000000000000000"); + pub fn placeholder(network_name: &NetworkName) -> Self { + Self::new(&[0; 32], network_name) } - /// Checks if the given `DID` has a valid IOTA DID `method` (i.e. `"iota"`). + /// Parses an [`IotaDID`] from the given `input`. /// /// # Errors /// - /// Returns `Err` if the input is not a valid [`IotaDID`]. - pub fn check_method(did: &D) -> Result<()> { - if did.method() != Self::METHOD { - Err(Error::InvalidDID(DIDError::InvalidMethodName)) - } else { - Ok(()) - } + /// Returns `Err` if the input does not conform to the [`IotaDID`] specification. + pub fn parse(input: impl AsRef) -> Result { + CoreDID::parse(input).and_then(Self::try_from_core) } - /// Checks if the given `DID` has a valid [`IotaDID`] `method_id`. + /// Converts a [`CoreDID`] to a [`IotaDID`]. /// /// # Errors /// - /// Returns `Err` if the input is not a valid [`IotaDID`]. - pub fn check_method_id(did: &D) -> Result<()> { - let segments: Vec<&str> = did.method_id().split(':').collect(); + /// Returns `Err` if the input does not conform to the [`IotaDID`] specification. + pub fn try_from_core(did: CoreDID) -> Result { + Self::check_validity(&did)?; - if segments.is_empty() || segments.len() > 2 { - return Err(Error::InvalidDID(DIDError::InvalidMethodId)); - } + Ok(Self(Self::normalize(did))) + } - // We checked if `id_segments` was empty so this should not panic - let mid: &str = segments.last().unwrap(); - let len: usize = BaseEncoding::decode_base58(mid) - .map_err(|_| Error::InvalidDID(DIDError::InvalidMethodId))? - .len(); + // =========================================================================== + // Properties + // =========================================================================== - if len == BLAKE2B_256_LEN { - Ok(()) - } else { - Err(Error::InvalidDID(DIDError::InvalidMethodId)) - } + /// Returns the IOTA `network` name of the `DID`. + pub fn network_str(&self) -> &str { + Self::denormalized_components(self.method_id()).0 } - /// Checks if the given `DID` has a valid [`IotaDID`] network name, e.g. "main", "dev". - /// - /// # Errors - /// - /// Returns `Err` if the input is not a valid [`IotaDID`]. - /// See [`NetworkName`] for validation requirements. - pub fn check_network(did: &D) -> Result<()> { - let network_name = Segments(did.method_id()).network(); - NetworkName::validate_network_name(network_name) + /// Returns the tag of the `DID`, which is a hex-encoded Alias ID. + pub fn tag(&self) -> &str { + Self::denormalized_components(self.method_id()).1 } - /// Checks if the given `DID` is valid according to the [`IotaDID`] method specification. + // =========================================================================== + // Validation + // =========================================================================== + + /// Checks if the given `DID` is syntactically valid according to the [`IotaDID`] method specification. /// /// # Errors /// - /// Returns `Err` if the input is not a valid [`IotaDID`]. + /// Returns `Err` if the input is not a syntactically valid [`IotaDID`]. pub fn check_validity(did: &D) -> Result<()> { - Self::check_method(did)?; - Self::check_method_id(did)?; - Self::check_network(did)?; - - Ok(()) + Self::check_method(did) + .and_then(|_| Self::check_tag(did)) + .and_then(|_| Self::check_network(did)) } /// Returns a `bool` indicating if the given `DID` is valid according to the /// [`IotaDID`] method specification. + /// + /// Equivalent to `IotaDID::check_validity(did).is_ok()`. pub fn is_valid(did: &CoreDID) -> bool { Self::check_validity(did).is_ok() } - /// Returns the Tangle `network` of the `DID`, if it is valid. - pub fn network(&self) -> Result { - Network::try_from_name(self.network_str().to_owned()) - } + // =========================================================================== + // Helpers + // =========================================================================== - /// Returns the Tangle `network` name of the `DID`. - pub fn network_str(&self) -> &str { - self.segments().network() + /// Checks if the given `DID` has a valid [`IotaDID`] `method` (i.e. `"iota"`). + /// + /// # Errors + /// + /// Returns `Err` if the input represents another method. + fn check_method(did: &D) -> Result<()> { + (did.method() == Self::METHOD) + .then_some(()) + .ok_or(DIDError::InvalidMethodName) } - /// Returns the unique Tangle tag of the `DID`. - pub fn tag(&self) -> &str { - self.segments().tag() + /// Checks if the given `DID` has a valid [`IotaDID`] `method_id`. + /// + /// # Errors + /// + /// Returns `Err` if the input does not have a [`IotaDID`] compliant method id. + fn check_tag(did: &D) -> Result<()> { + let (_, tag) = Self::denormalized_components(did.method_id()); + + // Implicitly catches if there are too many segments (:) in the DID too. + prefix_hex::decode::<[u8; Self::TAG_BYTES_LEN]>(tag) + .map_err(|_| DIDError::InvalidMethodId) + .map(|_| ()) } - #[doc(hidden)] - pub fn segments(&self) -> Segments<'_> { - Segments(self.method_id()) + /// Checks if the given `DID` has a valid [`IotaDID`] network name. + /// + /// # Errors + /// + /// Returns `Err` if the input is not a valid network name according to the [`IotaDID`] method specification. + fn check_network(did: &D) -> Result<()> { + let (network_name, _) = Self::denormalized_components(did.method_id()); + NetworkName::validate_network_name(network_name).map_err(|_| DIDError::Other("invalid network name")) } /// Normalizes the DID `method_id` by removing the default network segment if present. @@ -189,94 +188,96 @@ impl IotaDID { /// E.g. /// - `"did:iota:main:123" -> "did:iota:123"` is normalized /// - `"did:iota:dev:123" -> "did:iota:dev:123"` is unchanged + // TODO: Remove the lint once this bug in clippy has been fixed. Without to_owned a mutable reference will be aliased. + #[allow(clippy::unnecessary_to_owned)] fn normalize(mut did: CoreDID) -> CoreDID { - let segments: Segments<'_> = Segments(did.method_id()); - - if segments.count() == 2 && segments.network() == Self::DEFAULT_NETWORK { - let method_id: String = segments.tag().to_string(); + let method_id = did.method_id(); + let (network, tag) = Self::denormalized_components(method_id); + if tag.len() == method_id.len() || network != Self::DEFAULT_NETWORK { + did + } else { + did + .set_method_id(tag.to_owned()) + .expect("normalizing a valid CoreDID should be Ok"); did - .set_method_id(method_id) - .expect("this method_id is from a valid did"); } - - did } - // Note: Must be `pub` for the `did` macro. - #[doc(hidden)] - pub fn encode_key(key: &[u8]) -> String { - BaseEncoding::encode_base58(&Blake2b256::digest(key)) + /// foo:bar -> (foo,bar) + /// foo:bar:baz -> (foo, bar:baz) + /// foo -> (IotaDID::DEFAULT_NETWORK.as_ref(), foo) + #[inline(always)] + fn denormalized_components(input: &str) -> (&str, &str) { + input + .find(':') + .map(|idx| input.split_at(idx)) + .map(|(network, tail)| (network, &tail[1..])) + // Self::DEFAULT_NETWORK is built from a static reference so unwrapping is fine + .unwrap_or((Self::DEFAULT_NETWORK, input)) } } -impl Display for IotaDID { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - write!(f, "{}", self.0) - } -} +impl FromStr for IotaDID { + type Err = DIDError; -impl Debug for IotaDID { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - write!(f, "{}", self.0) + fn from_str(s: &str) -> std::result::Result { + Self::parse(s) } } -impl AsRef for IotaDID { - fn as_ref(&self) -> &CoreDID { - &self.0 - } -} +impl TryFrom<&str> for IotaDID { + type Error = DIDError; -impl From for CoreDID { - fn from(other: IotaDID) -> Self { - other.0 + fn try_from(other: &str) -> std::result::Result { + Self::parse(other) } } -impl TryFrom for IotaDID { - type Error = Error; +impl TryFrom for IotaDID { + type Error = DIDError; - fn try_from(other: BaseDIDUrl) -> Result { - let core_did: CoreDID = CoreDID::try_from(other)?; - Self::try_from(core_did) + fn try_from(other: String) -> std::result::Result { + Self::parse(other) } } -impl TryFrom for IotaDID { - type Error = Error; - - fn try_from(other: CoreDID) -> Result { - Self::try_from_core(other) +impl Display for IotaDID { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + write!(f, "{}", self.0) } } -impl FromStr for IotaDID { - type Err = Error; - - fn from_str(string: &str) -> Result { - Self::parse(string) +impl From for CoreDID { + fn from(id: IotaDID) -> Self { + id.0 } } -impl TryFrom<&str> for IotaDID { - type Error = Error; +impl From for String { + fn from(did: IotaDID) -> Self { + did.into_string() + } +} - fn try_from(other: &str) -> Result { - Self::parse(other) +impl TryFrom for IotaDID { + type Error = DIDError; + fn try_from(value: CoreDID) -> std::result::Result { + Self::try_from_core(value) } } -impl TryFrom for IotaDID { - type Error = Error; +impl TryFrom for IotaDID { + type Error = DIDError; - fn try_from(other: String) -> Result { - Self::parse(other) + fn try_from(other: BaseDIDUrl) -> Result { + let core_did: CoreDID = CoreDID::try_from(other)?; + Self::try_from(core_did) } } -impl From for String { - fn from(did: IotaDID) -> Self { - did.into_string() +impl AsRef for IotaDID { + fn as_ref(&self) -> &CoreDID { + &self.0 } } @@ -289,154 +290,560 @@ impl KeyComparable for IotaDID { } } +#[cfg(feature = "client")] +mod __iota_did_client { + use crate::block::output::AliasId; + use crate::IotaDID; + + impl From<&IotaDID> for AliasId { + /// Creates an [`AliasId`] from the DID tag. + fn from(did: &IotaDID) -> Self { + let tag_bytes: [u8; IotaDID::TAG_BYTES_LEN] = prefix_hex::decode(did.tag()) + .expect("being able to successfully decode the tag should be checked during DID creation"); + AliasId::new(tag_bytes) + } + } +} + #[cfg(test)] mod tests { - use identity_core::crypto::KeyPair; - use identity_core::crypto::KeyType; - use identity_did::did::CoreDID; - use identity_did::did::DID; - - use crate::did::IotaDID; - use crate::did::IotaDIDUrl; - - const TAG: &str = "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV"; + use once_cell::sync::Lazy; + use proptest::strategy::Strategy; + use proptest::*; + + use super::*; + + // =========================================================================================================================== + // Reusable constants and statics + // =========================================================================================================================== + + const PLACEHOLDER_TAG: &str = "0x0000000000000000000000000000000000000000000000000000000000000000"; + + // obtained AliasID from a valid OutputID string + // output_id copied from https://github.com/iotaledger/bee/blob/30cab4f02e9f5d72ffe137fd9eb09723b4f0fdb6/bee-block/tests/output_id.rs + // value of AliasID computed from AliasId::from(OutputId).to_string() + const VALID_ALIAS_ID_STR: &str = "0xf29dd16310c2100fd1bf568b345fb1cc14d71caa3bd9b5ad735d2bd6d455ca3b"; + + const LEN_VALID_ALIAS_STR: usize = VALID_ALIAS_ID_STR.len(); + + static VALID_IOTA_DID_STRING: Lazy = Lazy::new(|| format!("did:{}:{}", IotaDID::METHOD, VALID_ALIAS_ID_STR)); + + // Rules are: at least one character, at most six characters and may only contain digits and/or lowercase ascii + // characters. + const VALID_NETWORK_NAMES: [&str; 13] = [ + IotaDID::DEFAULT_NETWORK, + "main", + "dev", + "smr", + "rms", + "test", + "foo", + "foobar", + "123456", + "0", + "foo42", + "bar123", + "42foo", + ]; + + static VALID_IOTA_DID_STRINGS: Lazy> = Lazy::new(|| { + let network_tag_to_did = |network, tag| format!("did:{}:{}:{}", IotaDID::METHOD, network, tag); + + let valid_strings: Vec = VALID_NETWORK_NAMES + .iter() + .flat_map(|network| { + [VALID_ALIAS_ID_STR, PLACEHOLDER_TAG] + .iter() + .map(move |tag| network_tag_to_did(network, tag)) + }) + .collect(); + + // in principle the previous binding is not necessary (we could have just returned the value), + // but let's just ensure that it contains the expected number of elements first. + assert_eq!(valid_strings.len(), 2 * VALID_NETWORK_NAMES.len()); + + valid_strings + }); + + // =========================================================================================================================== + // Test check_* methods + // =========================================================================================================================== #[test] - fn test_parse_did_valid() { - assert!(IotaDID::parse(format!("did:iota:{}", TAG)).is_ok()); - assert!(IotaDID::parse(format!("did:iota:main:{}", TAG)).is_ok()); - assert!(IotaDID::parse(format!("did:iota:dev:{}", TAG)).is_ok()); - assert!(IotaDID::parse(format!("did:iota:custom:{}", TAG)).is_ok()); + fn invalid_check_method() { + let did_key_core: CoreDID = CoreDID::parse("did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK").unwrap(); + assert!(matches!( + IotaDID::check_method(&did_key_core), + Err(DIDError::InvalidMethodName) + )); } #[test] - fn test_parse_did_url_valid() { - assert!(IotaDIDUrl::parse(format!("did:iota:{}", TAG)).is_ok()); - assert!(IotaDIDUrl::parse(format!("did:iota:{}#fragment", TAG)).is_ok()); - assert!(IotaDIDUrl::parse(format!("did:iota:{}?somequery=somevalue", TAG)).is_ok()); - assert!(IotaDIDUrl::parse(format!("did:iota:{}?somequery=somevalue#fragment", TAG)).is_ok()); + fn valid_check_method() { + let did_iota_core: CoreDID = CoreDID::parse(&*VALID_IOTA_DID_STRING).unwrap(); + assert!(IotaDID::check_method(&did_iota_core).is_ok()); + } - assert!(IotaDIDUrl::parse(format!("did:iota:main:{}", TAG)).is_ok()); - assert!(IotaDIDUrl::parse(format!("did:iota:main:{}#fragment", TAG)).is_ok()); - assert!(IotaDIDUrl::parse(format!("did:iota:main:{}?somequery=somevalue", TAG)).is_ok()); - assert!(IotaDIDUrl::parse(format!("did:iota:main:{}?somequery=somevalue#fragment", TAG)).is_ok()); + #[test] + fn valid_check_network() { + let assert_check_network = |input: &str| { + let did_core: CoreDID = + CoreDID::parse(input).unwrap_or_else(|_| panic!("expected {} to parse to a valid CoreDID", input)); + assert!( + IotaDID::check_network(&did_core).is_ok(), + "test: valid_check_network failed with input {}", + input, + ); + }; + + for network_name in VALID_NETWORK_NAMES { + let did_string = format!( + "did:method:{}:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK", + network_name + ); + assert_check_network(&did_string); + } - assert!(IotaDIDUrl::parse(format!("did:iota:dev:{}", TAG)).is_ok()); - assert!(IotaDIDUrl::parse(format!("did:iota:dev:{}#fragment", TAG)).is_ok()); - assert!(IotaDIDUrl::parse(format!("did:iota:dev:{}?somequery=somevalue", TAG)).is_ok()); - assert!(IotaDIDUrl::parse(format!("did:iota:dev:{}?somequery=somevalue#fragment", TAG)).is_ok()); + assert_check_network("did:method:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"); + } - assert!(IotaDIDUrl::parse(format!("did:iota:custom:{}", TAG)).is_ok()); - assert!(IotaDIDUrl::parse(format!("did:iota:custom:{}#fragment", TAG)).is_ok()); - assert!(IotaDIDUrl::parse(format!("did:iota:custom:{}?somequery=somevalue", TAG)).is_ok()); - assert!(IotaDIDUrl::parse(format!("did:iota:custom:{}?somequery=somevalue#fragment", TAG)).is_ok()); + #[test] + fn invalid_check_network() { + // Loop over list of network names known to be invalid, attempt to create a CoreDID containing the given network + // name in the method_id sub-string and ensure that `IotaDID::check_network` fails. If the provided network + // name is in conflict with the DID Core spec itself then proceed to the next network name. + + // Ensure that this test is robust to changes in the supplied list of network names, i.e. fail if none of the + // network names can be contained in a generic CoreDID. + + let mut check_network_executed: bool = false; + + const INVALID_NETWORK_NAMES: [&str; 10] = [ + "Main", "fOo", "deV", "féta", "", " ", "foo ", " foo", "1234567", "foobar0", + ]; + for network_name in INVALID_NETWORK_NAMES { + let did_string: String = format!( + "did:method:{}:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK", + network_name + ); + let did_core: CoreDID = { + match CoreDID::parse(&did_string) { + Ok(did_core) => did_core, + Err(_) => continue, + } + }; + + assert!(matches!(IotaDID::check_network(&did_core), Err(DIDError::Other(_)))); + check_network_executed = true; + } + assert!( + check_network_executed, + "IotaDID::check_network` should have been executed" + ); } #[test] - fn test_parse_did_invalid() { - // A non-"iota" DID method is invalid. - assert!(IotaDID::parse("did:foo::").is_err()); - // An empty DID method is invalid. - assert!(IotaDID::parse("did:::").is_err()); - assert!(IotaDID::parse(format!("did::main:{}", TAG)).is_err()); - // A non-"iota" DID method is invalid. - assert!(IotaDID::parse("did:iota---::").is_err()); - // An empty `iota-specific-idstring` is invalid. - assert!(IotaDID::parse("did:iota:").is_err()); - // Too many components is invalid. - assert!(IotaDID::parse(format!("did:iota:custom:shard-1:random:{}", TAG)).is_err()); - assert!(IotaDID::parse(format!("did:iota:custom:random:{}", TAG)).is_err()); - // Explicit empty network name is invalid (omitting it is still fine) - assert!(IotaDID::parse(format!("did:iota::{}", TAG)).is_err()); - // Invalid network name is invalid. - assert!(IotaDID::parse(format!("did:iota:Invalid-Network:{}", TAG)).is_err()); + fn valid_check_tag() { + for input in VALID_IOTA_DID_STRINGS.iter() { + let did_core: CoreDID = CoreDID::parse(input).unwrap(); + assert!( + IotaDID::check_tag(&did_core).is_ok(), + "test: valid_check_method_id failed on input {}", + input + ); + } + + // Should also work for DID's of the form: did::: + let did_other_string: String = format!("did:method:{}", VALID_ALIAS_ID_STR); + let did_other_with_network: String = format!("did:method:test:{}", VALID_ALIAS_ID_STR); + let did_other_core: CoreDID = CoreDID::parse(&did_other_string).unwrap(); + let did_other_with_network_core: CoreDID = CoreDID::parse(&did_other_with_network).unwrap(); + + assert!(IotaDID::check_tag(&did_other_core).is_ok()); + assert!(IotaDID::check_tag(&did_other_with_network_core).is_ok()); } #[test] - fn test_from_did() { - let key: String = IotaDID::encode_key(b"123"); + fn invalid_check_tag() { + let invalid_method_id_strings = [ + // Too many segments + format!("did:method:main:test:{}", VALID_ALIAS_ID_STR), + // Tag is not prefixed + format!("did:method:{}", &VALID_ALIAS_ID_STR.strip_prefix("0x").unwrap()), + // Tag is too long + format!( + "did:method:{}", + &VALID_ALIAS_ID_STR.chars().chain("a".chars()).collect::() + ), + // Tag is too short (omit last character) + format!("did:method:main:{}", &VALID_ALIAS_ID_STR[..65]), + ]; + + for input in invalid_method_id_strings { + let did_core: CoreDID = CoreDID::parse(input).unwrap(); + assert!( + matches!(IotaDID::check_tag(&did_core), Err(DIDError::InvalidMethodId)), + "{}", + did_core + ); + } + } - let did: CoreDID = format!("did:iota:{}", key).parse().unwrap(); - let iota_did = IotaDID::try_from_core(did).unwrap(); - assert_eq!(iota_did.network_str(), "main"); - assert_eq!(iota_did.tag(), key); + // =========================================================================================================================== + // Test constructors + // =========================================================================================================================== - let did: CoreDID = "did:iota:123".parse().unwrap(); - assert!(IotaDID::try_from_core(did).is_err()); + #[test] + fn placeholder_produces_a_did_with_expected_string_representation() { + assert_eq!( + IotaDID::placeholder(&NetworkName::try_from(IotaDID::DEFAULT_NETWORK).unwrap()).as_str(), + format!("did:{}:{}", IotaDID::METHOD, PLACEHOLDER_TAG) + ); + + for name in VALID_NETWORK_NAMES + .iter() + .filter(|name| *name != &IotaDID::DEFAULT_NETWORK) + { + let network_name: NetworkName = NetworkName::try_from(*name).unwrap(); + let did: IotaDID = IotaDID::placeholder(&network_name); + assert_eq!( + did.as_str(), + format!("did:{}:{}:{}", IotaDID::METHOD, name, PLACEHOLDER_TAG) + ); + } + } - let did: CoreDID = format!("did:web:{}", key).parse().unwrap(); - assert!(IotaDID::try_from_core(did).is_err()); + #[test] + fn normalization_in_constructors() { + let did_with_default_network_string: String = format!( + "did:{}:{}:{}", + IotaDID::METHOD, + IotaDID::DEFAULT_NETWORK, + VALID_ALIAS_ID_STR + ); + let expected_normalization_string_representation: String = + format!("did:{}:{}", IotaDID::METHOD, VALID_ALIAS_ID_STR); + + assert_eq!( + IotaDID::parse(did_with_default_network_string).unwrap().as_str(), + expected_normalization_string_representation + ); } #[test] - fn test_network() { - let key: String = IotaDID::encode_key(b"123"); + fn parse_valid() { + for did_str in VALID_IOTA_DID_STRINGS.iter() { + assert!(IotaDID::parse(&did_str).is_ok()); + } + } - let did: IotaDID = format!("did:iota:{}", key).parse().unwrap(); - assert_eq!(did.network_str(), "main"); + #[test] + fn parse_invalid() { + let execute_assertions = |valid_alias_id: &str| { + assert!(matches!( + IotaDID::parse(format!("dod:{}:{}", IotaDID::METHOD, valid_alias_id)), + Err(DIDError::InvalidScheme) + )); + + assert!(matches!( + IotaDID::parse(format!("did:key:{}", valid_alias_id)), + Err(DIDError::InvalidMethodName) + )); + + // invalid network name (exceeded six characters) + assert!(matches!( + IotaDID::parse(format!("did:{}:1234567:{}", IotaDID::METHOD, valid_alias_id)), + Err(DIDError::Other(_)) + )); + + // invalid network name (contains non ascii character é) + assert!(matches!( + IotaDID::parse(format!("did:{}:féta:{}", IotaDID::METHOD, valid_alias_id)), + Err(DIDError::InvalidMethodId) + )); + + // invalid tag + assert!(matches!( + IotaDID::parse(format!("did:{}:", IotaDID::METHOD)), + Err(DIDError::InvalidMethodId) + )); + + // too many segments in method_id + assert!(matches!( + IotaDID::parse(format!("did:{}:test:foo:{}", IotaDID::METHOD, valid_alias_id)), + Err(DIDError::InvalidMethodId) + )); + }; + + execute_assertions(PLACEHOLDER_TAG); + execute_assertions(VALID_ALIAS_ID_STR); + } + + // =========================================================================================================================== + // Test constructors with randomly generated input + // =========================================================================================================================== + + #[cfg(feature = "iota-client")] + fn arbitrary_alias_id() -> impl Strategy { + ( + proptest::prelude::any::<[u8; 32]>(), + iota_client::block::output::OUTPUT_INDEX_RANGE, + ) + .prop_map(|(bytes, idx)| { + let transaction_id = iota_client::block::payload::transaction::TransactionId::new(bytes); + let output_id = iota_client::block::output::OutputId::new(transaction_id, idx).unwrap(); + iota_client::block::output::AliasId::from(output_id) + }) + } + + #[cfg(feature = "iota-client")] + proptest! { + #[test] + fn property_based_valid_parse(alias_id in arbitrary_alias_id()) { + let did: String = format!("did:{}:{}",IotaDID::METHOD, alias_id); + assert!(IotaDID::parse(&did).is_ok()); + } + } - let did: IotaDID = format!("did:iota:dev:{}", key).parse().unwrap(); - assert_eq!(did.network_str(), "dev"); + #[cfg(feature = "iota-client")] + proptest! { + #[test] + fn property_based_new(bytes in proptest::prelude::any::<[u8;32]>()) { + for network_name in VALID_NETWORK_NAMES.iter().map(|name| NetworkName::try_from(*name).unwrap()) { + // check that this does not panic + IotaDID::new(&bytes, &network_name); + } + } + } - let did: IotaDID = format!("did:iota:test:{}", key).parse().unwrap(); - assert_eq!(did.network_str(), "test"); + #[cfg(feature = "iota-client")] + proptest! { + #[test] + fn property_based_alias_id_string_representation_roundtrip(alias_id in arbitrary_alias_id()) { + for network_name in VALID_NETWORK_NAMES.iter().map(|name| NetworkName::try_from(*name).unwrap()) { + assert_eq!( + iota_client::block::output::AliasId::from_str(IotaDID::new(&alias_id, &network_name).tag()).unwrap(), + alias_id + ); + } + } + } - let did: IotaDID = format!("did:iota:custom:{}", key).parse().unwrap(); - assert_eq!(did.network_str(), "custom"); + fn arbitrary_alias_id_string_replica() -> impl Strategy { + proptest::string::string_regex(&format!("0x([a-f]|[0-9]){{{}}}", (LEN_VALID_ALIAS_STR - 2))) + .expect("regex should be ok") } - #[test] - fn test_tag() { - let did: IotaDID = format!("did:iota:{}", TAG).parse().unwrap(); - assert_eq!(did.tag(), TAG); + proptest! { + #[test] + fn valid_alias_id_string_replicas(tag in arbitrary_alias_id_string_replica()) { + let did : String = format!("did:{}:{}", IotaDID::METHOD, tag); + assert!( + IotaDID::parse(did).is_ok() + ); + } + } - let did: IotaDID = format!("did:iota:main:{}", TAG).parse().unwrap(); - assert_eq!(did.tag(), TAG); + fn arbitrary_invalid_tag() -> impl Strategy { + proptest::string::string_regex("[[:^alpha:]|[a-z]|[1-9]]*") + .expect("regex should be ok") + .prop_map(|arb_string| { + if arb_string + .chars() + .all(|c| c.is_ascii_hexdigit() && c.is_ascii_lowercase()) + && arb_string.len() == LEN_VALID_ALIAS_STR + && arb_string.starts_with("0x") + { + // this means we are in the rare case of generating a valid string hence we replace the last 0 with the non + // ascii character é + let mut counter = 0; + arb_string + .chars() + .rev() + .map(|value| { + if value == '0' && counter == 0 { + counter += 1; + 'é' + } else { + value + } + }) + .collect::() + } else { + arb_string + } + }) + } + + proptest! { + #[test] + fn invalid_tag_property_based_parse(tag in arbitrary_invalid_tag()) { + let did: String = format!("did:{}:{}", IotaDID::METHOD, tag); + assert!( + IotaDID::parse(did).is_err() + ); + } } - #[test] - fn test_new() { - let key: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); - let tag: String = IotaDID::encode_key(key.public().as_ref()); + fn arbitrary_delimiter_mixed_in_prefix_hex() -> impl Strategy { + proptest::string::string_regex("0x([a-f]|[:]|[0-9])*").expect("regex should be ok") + } - let did: IotaDID = IotaDID::new(key.public().as_ref()).unwrap(); - assert_eq!(did.tag(), tag); - assert_eq!(did.network_str(), IotaDID::DEFAULT_NETWORK); + proptest! { + #[test] + fn invalid_hex_mixed_with_delimiter(tag in arbitrary_delimiter_mixed_in_prefix_hex()) { + let did: String = format!("did:{}:{}", IotaDID::METHOD, tag); + assert!(IotaDID::parse(did).is_err()); + } } + // =========================================================================================================================== + // Test getters + // =========================================================================================================================== #[test] - fn test_new_with_network() { - let key: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); - let did: IotaDID = IotaDID::new_with_network(key.public().as_ref(), "foo").unwrap(); - let tag: String = IotaDID::encode_key(key.public().as_ref()); + fn test_network() { + let execute_assertions = |valid_alias_id: &str| { + let did: IotaDID = format!("did:{}:{}", IotaDID::METHOD, valid_alias_id).parse().unwrap(); + assert_eq!(did.network_str(), IotaDID::DEFAULT_NETWORK); + + let did: IotaDID = format!("did:{}:dev:{}", IotaDID::METHOD, valid_alias_id) + .parse() + .unwrap(); + assert_eq!(did.network_str(), "dev"); + + let did: IotaDID = format!("did:{}:test:{}", IotaDID::METHOD, valid_alias_id) + .parse() + .unwrap(); + assert_eq!(did.network_str(), "test"); - assert_eq!(did.tag(), tag); - assert_eq!(did.network_str(), "foo"); + let did: IotaDID = format!("did:{}:custom:{}", IotaDID::METHOD, valid_alias_id) + .parse() + .unwrap(); + assert_eq!(did.network_str(), "custom"); + }; + + execute_assertions(PLACEHOLDER_TAG); + execute_assertions(VALID_ALIAS_ID_STR); } #[test] - fn test_normalize() { - let key: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); - let tag: String = IotaDID::encode_key(key.public().as_ref()); + fn test_tag() { + let execute_assertions = |valid_alias_id: &str| { + let did: IotaDID = format!("did:{}:{}", IotaDID::METHOD, valid_alias_id).parse().unwrap(); + assert_eq!(did.tag(), valid_alias_id); + + let did: IotaDID = format!( + "did:{}:{}:{}", + IotaDID::METHOD, + IotaDID::DEFAULT_NETWORK, + valid_alias_id + ) + .parse() + .unwrap(); + assert_eq!(did.tag(), valid_alias_id); + + let did: IotaDID = format!("did:{}:dev:{}", IotaDID::METHOD, valid_alias_id) + .parse() + .unwrap(); + assert_eq!(did.tag(), valid_alias_id); + + let did: IotaDID = format!("did:{}:custom:{}", IotaDID::METHOD, valid_alias_id) + .parse() + .unwrap(); + assert_eq!(did.tag(), valid_alias_id); + }; + execute_assertions(PLACEHOLDER_TAG); + execute_assertions(VALID_ALIAS_ID_STR); + } + + // =========================================================================================================================== + // Test DIDUrl + // =========================================================================================================================== - // An IotaDID with "main" as the network can be normalized ("main" removed) - let did1: IotaDID = format!("did:iota:{}", tag).parse().unwrap(); - let did2: IotaDID = format!("did:iota:main:{}", tag).parse().unwrap(); - assert_eq!(did1, did2); + #[test] + fn test_parse_did_url_valid() { + let execute_assertions = |valid_alias_id: &str| { + assert!(IotaDIDUrl::parse(format!("did:{}:{}", IotaDID::METHOD, valid_alias_id)).is_ok()); + assert!(IotaDIDUrl::parse(format!("did:{}:{}#fragment", IotaDID::METHOD, valid_alias_id)).is_ok()); + assert!(IotaDIDUrl::parse(format!( + "did:{}:{}?somequery=somevalue", + IotaDID::METHOD, + valid_alias_id + )) + .is_ok()); + assert!(IotaDIDUrl::parse(format!( + "did:{}:{}?somequery=somevalue#fragment", + IotaDID::METHOD, + valid_alias_id + )) + .is_ok()); + + assert!(IotaDIDUrl::parse(format!("did:{}:main:{}", IotaDID::METHOD, valid_alias_id)).is_ok()); + assert!(IotaDIDUrl::parse(format!("did:{}:main:{}#fragment", IotaDID::METHOD, valid_alias_id)).is_ok()); + assert!(IotaDIDUrl::parse(format!( + "did:{}:main:{}?somequery=somevalue", + IotaDID::METHOD, + valid_alias_id + )) + .is_ok()); + assert!(IotaDIDUrl::parse(format!( + "did:{}:main:{}?somequery=somevalue#fragment", + IotaDID::METHOD, + valid_alias_id + )) + .is_ok()); + + assert!(IotaDIDUrl::parse(format!("did:{}:dev:{}", IotaDID::METHOD, valid_alias_id)).is_ok()); + assert!(IotaDIDUrl::parse(format!("did:{}:dev:{}#fragment", IotaDID::METHOD, valid_alias_id)).is_ok()); + assert!(IotaDIDUrl::parse(format!( + "did:{}:dev:{}?somequery=somevalue", + IotaDID::METHOD, + valid_alias_id + )) + .is_ok()); + assert!(IotaDIDUrl::parse(format!( + "did:{}:dev:{}?somequery=somevalue#fragment", + IotaDID::METHOD, + valid_alias_id + )) + .is_ok()); + + assert!(IotaDIDUrl::parse(format!("did:{}:custom:{}", IotaDID::METHOD, valid_alias_id)).is_ok()); + assert!(IotaDIDUrl::parse(format!("did:{}:custom:{}#fragment", IotaDID::METHOD, valid_alias_id)).is_ok()); + assert!(IotaDIDUrl::parse(format!( + "did:{}:custom:{}?somequery=somevalue", + IotaDID::METHOD, + valid_alias_id + )) + .is_ok()); + assert!(IotaDIDUrl::parse(format!( + "did:{}:custom:{}?somequery=somevalue#fragment", + IotaDID::METHOD, + valid_alias_id + )) + .is_ok()); + }; + execute_assertions(PLACEHOLDER_TAG); + execute_assertions(VALID_ALIAS_ID_STR); } #[test] - fn test_setter() { - let key: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); - let did: IotaDID = IotaDID::new(key.public().as_ref()).unwrap(); - let mut did_url: IotaDIDUrl = did.into_url(); - - did_url.set_path(Some("/foo")).unwrap(); - did_url.set_query(Some("diff=true")).unwrap(); - did_url.set_fragment(Some("foo")).unwrap(); - - assert_eq!(did_url.path(), Some("/foo")); - assert_eq!(did_url.query(), Some("diff=true")); - assert_eq!(did_url.fragment(), Some("foo")); + fn valid_url_setters() { + let execute_assertions = |valid_alias_id: &str| { + let mut did_url: IotaDIDUrl = IotaDID::parse(format!("did:{}:{}", IotaDID::METHOD, valid_alias_id)) + .unwrap() + .into_url(); + + did_url.set_path(Some("/foo")).unwrap(); + did_url.set_query(Some("diff=true")).unwrap(); + did_url.set_fragment(Some("foo")).unwrap(); + + assert_eq!(did_url.path(), Some("/foo")); + assert_eq!(did_url.query(), Some("diff=true")); + assert_eq!(did_url.fragment(), Some("foo")); + }; + execute_assertions(PLACEHOLDER_TAG); + execute_assertions(VALID_ALIAS_ID_STR); } } diff --git a/identity_iota_core/src/did/mod.rs b/identity_iota_core/src/did/mod.rs index 6c8c1a4ff0..fe39423378 100644 --- a/identity_iota_core/src/did/mod.rs +++ b/identity_iota_core/src/did/mod.rs @@ -1,12 +1,7 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -//! IOTA DID types. - -pub use self::iota_did::IotaDID; -pub use self::iota_did::IotaDIDUrl; -pub use self::segments::Segments; +pub use iota_did::IotaDID; +pub use iota_did::IotaDIDUrl; mod iota_did; -mod macros; -mod segments; diff --git a/identity_iota_core/src/document/iota_document.rs b/identity_iota_core/src/document/iota_document.rs index 7016a711a2..ab5f5194c9 100644 --- a/identity_iota_core/src/document/iota_document.rs +++ b/identity_iota_core/src/document/iota_document.rs @@ -5,49 +5,36 @@ use core::fmt; use core::fmt::Debug; use core::fmt::Display; -use serde; -use serde::Deserialize; -use serde::Serialize; - use identity_core::common::Object; use identity_core::common::OneOrSet; use identity_core::common::OrderedSet; use identity_core::common::Url; use identity_core::convert::FmtJson; -use identity_core::crypto::Ed25519; use identity_core::crypto::GetSignature; -use identity_core::crypto::GetSignatureMut; -use identity_core::crypto::JcsEd25519; -use identity_core::crypto::KeyPair; use identity_core::crypto::PrivateKey; -use identity_core::crypto::Proof; use identity_core::crypto::ProofOptions; -use identity_core::crypto::PublicKey; use identity_core::crypto::SetSignature; -use identity_core::crypto::Signer; use identity_did::document::CoreDocument; use identity_did::document::Document; use identity_did::service::Service; use identity_did::utils::DIDUrlQuery; use identity_did::verifiable::DocumentSigner; use identity_did::verifiable::VerifierOptions; -use identity_did::verification::MethodRef; use identity_did::verification::MethodRelationship; use identity_did::verification::MethodScope; -use identity_did::verification::MethodType; use identity_did::verification::MethodUriType; use identity_did::verification::TryMethod; use identity_did::verification::VerificationMethod; +use serde::Deserialize; +use serde::Serialize; -use crate::did::IotaDID; -use crate::did::IotaDIDUrl; -use crate::diff::DiffMessage; -use crate::document::IotaDocumentMetadata; -use crate::error::Error; use crate::error::Result; -use crate::tangle::MessageId; -use crate::tangle::MessageIdExt; -use crate::tangle::NetworkName; +use crate::IotaDID; +use crate::IotaDIDUrl; +use crate::IotaDocumentMetadata; +use crate::NetworkName; +use crate::StateMetadataDocument; +use crate::StateMetadataEncoding; /// A [`VerificationMethod`] adhering to the IOTA DID method specification. pub type IotaVerificationMethod = VerificationMethod; @@ -67,168 +54,70 @@ pub struct IotaDocument { pub(crate) document: IotaCoreDocument, #[serde(rename = "meta")] pub metadata: IotaDocumentMetadata, - #[serde(skip_serializing_if = "Option::is_none")] - pub proof: Option, -} - -impl TryMethod for IotaDocument { - const TYPE: MethodUriType = MethodUriType::Absolute; } impl IotaDocument { - // Method types allowed to sign a DID document update. - pub const UPDATE_METHOD_TYPES: &'static [MethodType] = &[MethodType::Ed25519VerificationKey2018]; - pub const DEFAULT_METHOD_FRAGMENT: &'static str = "sign-0"; - - /// Creates a new DID Document from the given [`KeyPair`]. - /// - /// The DID Document will be pre-populated with a single verification method - /// derived from the provided [`KeyPair`] embedded as a capability invocation - /// verification relationship. This method will have the DID URL fragment - /// `#sign-0` and can be easily retrieved with [`IotaDocument::default_signing_method`]. - /// - /// NOTE: the generated document is unsigned, see [`IotaDocument::sign_self`]. - /// - /// Example: - /// - /// ``` - /// # use identity_core::crypto::{KeyPair, KeyType}; - /// # use identity_iota_core::document::IotaDocument; - /// # - /// // Create a DID Document from a new Ed25519 keypair. - /// let keypair = KeyPair::new(KeyType::Ed25519).unwrap(); - /// let document = IotaDocument::new(&keypair).unwrap(); - /// ``` - pub fn new(keypair: &KeyPair) -> Result { - Self::new_with_options(keypair, None, None) - } - - /// Creates a new DID Document from the given [`KeyPair`], network, and verification method - /// fragment name. - /// - /// See [`IotaDocument::new`]. - /// - /// Arguments: - /// - /// * keypair: the initial verification method is derived from the public key of this [`KeyPair`]. - /// * network: Tangle network to use for the DID; default [`Network::Mainnet`](crate::tangle::Network::Mainnet). - /// * fragment: name of the initial verification method; default - /// [`DEFAULT_METHOD_FRAGMENT`](Self::DEFAULT_METHOD_FRAGMENT). - /// - /// Example: - /// - /// ``` - /// # use identity_core::crypto::KeyPair; - /// # use identity_core::crypto::KeyType; - /// # use identity_iota_core::document::IotaDocument; - /// # use identity_iota_core::tangle::Network; - /// # - /// // Create a new DID Document for the devnet from a new Ed25519 keypair. - /// let keypair = KeyPair::new(KeyType::Ed25519).unwrap(); - /// let document = - /// IotaDocument::new_with_options(&keypair, Some(Network::Devnet.name()), Some("auth-key")) - /// .unwrap(); - /// assert_eq!(document.id().network_str(), "dev"); - /// assert_eq!( - /// document - /// .default_signing_method() - /// .unwrap() - /// .id() - /// .fragment() - /// .unwrap(), - /// "auth-key" - /// ); - /// ``` - pub fn new_with_options(keypair: &KeyPair, network: Option, fragment: Option<&str>) -> Result { - let public_key: &PublicKey = keypair.public(); - - let did: IotaDID = if let Some(network_name) = network { - IotaDID::new_with_network(public_key.as_ref(), network_name)? - } else { - IotaDID::new(public_key.as_ref())? - }; - - let method: IotaVerificationMethod = IotaVerificationMethod::new( - did, - keypair.type_(), - keypair.public(), - fragment.unwrap_or(Self::DEFAULT_METHOD_FRAGMENT), - )?; + // =========================================================================== + // Constructors + // =========================================================================== - Self::from_verification_method(method) + /// Constructs an empty DID Document with a [`IotaDID::placeholder`] identifier + /// for the given `network`. + // TODO: always take Option or `new_with_options` for a particular network? + // TODO: store the network in the serialized state metadata? Currently it's lost during packing. + pub fn new(network: &NetworkName) -> Self { + Self::new_with_id(IotaDID::placeholder(network)) } - /// Creates a new DID Document from the given [`IotaVerificationMethod`], inserting it as the - /// default capability invocation method. - /// - /// NOTE: the generated document is unsigned, see [`IotaDocument::sign_self`]. - pub fn from_verification_method(method: IotaVerificationMethod) -> Result { - // Ensure the verification method key type is allowed to sign document updates. - if !Self::is_signing_method_type(method.type_()) { - return Err(Error::InvalidDocumentSigningMethodType); - } - - let document: IotaCoreDocument = IotaCoreDocument::builder(Default::default()) - .id(method.id().did().clone()) - .capability_invocation(MethodRef::Embed(method)) - .build()?; + /// Constructs an empty DID Document with the given identifier. + pub fn new_with_id(id: IotaDID) -> Self { + // PANIC: constructing an empty DID Document is infallible, caught by tests otherwise. + let document: IotaCoreDocument = CoreDocument::builder(Object::default()) + .id(id) + .build() + .expect("empty IotaDocument constructor failed"); let metadata: IotaDocumentMetadata = IotaDocumentMetadata::new(); - Ok(Self::from((document, metadata, None))) - } - - /// Returns whether the given [`MethodType`] can be used to sign document updates. - pub fn is_signing_method_type(method_type: MethodType) -> bool { - Self::UPDATE_METHOD_TYPES.contains(&method_type) - } - - /// Returns a reference to the underlying [`IotaCoreDocument`]. - pub fn core_document(&self) -> &IotaCoreDocument { - &self.document - } - - /// Returns a mutable reference to the underlying [`IotaCoreDocument`]. - pub fn core_document_mut(&mut self) -> &mut IotaCoreDocument { - &mut self.document + Self { document, metadata } } // =========================================================================== // Properties // =========================================================================== - /// Returns the DID document [`id`](IotaDID). + /// Returns the DID document identifier. pub fn id(&self) -> &IotaDID { self.document.id() } - /// Returns a reference to the [`IotaDocument`] controllers. + /// Returns a reference to the DID controllers. + /// + /// NOTE: controllers are determined by the `state_controller` unlock condition of the output + /// during resolution and are omitted when publishing. pub fn controller(&self) -> Option<&OneOrSet> { self.document.controller() } - /// Returns a mutable reference to the [`IotaDocument`] controllers. - pub fn controller_mut(&mut self) -> &mut Option> { - self.document.controller_mut() - } - - /// Returns a reference to the [`IotaDocument`] alsoKnownAs set. + /// Returns a reference to the `alsoKnownAs` set. pub fn also_known_as(&self) -> &OrderedSet { self.document.also_known_as() } - /// Returns a mutable reference to the [`IotaDocument`] alsoKnownAs set. + /// Returns a mutable reference to the `alsoKnownAs` set. pub fn also_known_as_mut(&mut self) -> &mut OrderedSet { self.document.also_known_as_mut() } - /// Returns the first [`IotaVerificationMethod`] with a capability invocation relationship - /// capable of signing this DID document. - pub fn default_signing_method(&self) -> Result<&IotaVerificationMethod> { - self - .core_document() - .capability_invocation() - .head() - .and_then(|method_ref| self.core_document().resolve_method_ref(method_ref)) - .ok_or(Error::MissingSigningKey) + /// Returns a reference to the underlying [`IotaCoreDocument`]. + pub fn core_document(&self) -> &IotaCoreDocument { + &self.document + } + + /// Returns a mutable reference to the underlying [`IotaCoreDocument`]. + /// + /// WARNING: mutating the inner document directly bypasses restrictions and + /// may have undesired consequences. + pub fn core_document_mut(&mut self) -> &mut IotaCoreDocument { + &mut self.document } /// Returns a reference to the custom DID Document properties. @@ -245,7 +134,7 @@ impl IotaDocument { // Services // =========================================================================== - /// Return a set of all [`Service`]s in the document. + /// Return a set of all [`IotaService`]s in the document. pub fn service(&self) -> &OrderedSet { self.document.service() } @@ -257,7 +146,7 @@ impl IotaDocument { if service.id().fragment().is_none() { false } else { - self.document.service_mut().append(service) + self.core_document_mut().service_mut().append(service) } } @@ -265,14 +154,14 @@ impl IotaDocument { /// /// Returns `true` if a service was removed. pub fn remove_service(&mut self, did_url: &IotaDIDUrl) -> bool { - self.document.service_mut().remove(did_url) + self.core_document_mut().service_mut().remove(did_url) } // =========================================================================== // Verification Methods // =========================================================================== - /// Returns an iterator over all [`IotaVerificationMethods`][IotaVerificationMethod] in the DID Document. + /// Returns an iterator over all [`IotaVerificationMethod`] in the DID Document. pub fn methods(&self) -> impl Iterator { self.document.methods() } @@ -283,16 +172,16 @@ impl IotaDocument { /// /// Returns an error if a method with the same fragment already exists. pub fn insert_method(&mut self, method: IotaVerificationMethod, scope: MethodScope) -> Result<()> { - Ok(self.document.insert_method(method, scope)?) + Ok(self.core_document_mut().insert_method(method, scope)?) } - /// Removes all references to the specified [`VerificationMethod`]. + /// Removes all references to the specified [`IotaVerificationMethod`]. /// /// # Errors /// /// Returns an error if the method does not exist. pub fn remove_method(&mut self, did_url: &IotaDIDUrl) -> Result<()> { - Ok(self.document.remove_method(did_url)?) + Ok(self.core_document_mut().remove_method(did_url)?) } /// Attaches the relationship to the given method, if the method exists. @@ -300,27 +189,27 @@ impl IotaDocument { /// Note: The method needs to be in the set of verification methods, /// so it cannot be an embedded one. pub fn attach_method_relationship(&mut self, did_url: &IotaDIDUrl, relationship: MethodRelationship) -> Result { - Ok(self.document.attach_method_relationship(did_url, relationship)?) + Ok( + self + .core_document_mut() + .attach_method_relationship(did_url, relationship)?, + ) } /// Detaches the given relationship from the given method, if the method exists. pub fn detach_method_relationship(&mut self, did_url: &IotaDIDUrl, relationship: MethodRelationship) -> Result { - Ok(self.document.detach_method_relationship(did_url, relationship)?) - } - - /// Returns the first [`IotaVerificationMethod`] with an `id` property matching the - /// provided `query` and the verification relationship specified by `scope` if present. - pub fn resolve_method<'query, Q>(&self, query: Q, scope: Option) -> Option<&IotaVerificationMethod> - where - Q: Into>, - { - self.document.resolve_method(query, scope) + Ok( + self + .core_document_mut() + .detach_method_relationship(did_url, relationship)?, + ) } /// Returns the first [`IotaVerificationMethod`] with an `id` property matching the /// provided `query` and the verification relationship specified by `scope` if present. /// - /// WARNING: improper usage of this allows violating the uniqueness of the verification method sets. + /// WARNING: improper usage of this allows violating the uniqueness of the verification method + /// sets. pub fn resolve_method_mut<'query, Q>( &mut self, query: Q, @@ -332,23 +221,6 @@ impl IotaDocument { self.document.resolve_method_mut(query, scope) } - /// Attempts to resolve the given method query into a method capable of signing a document update. - pub fn resolve_signing_method<'query, Q>(&self, query: Q) -> Result<&IotaVerificationMethod> - where - Q: Into>, - { - self - .resolve_method(query, Some(MethodScope::capability_invocation())) - .ok_or(Error::InvalidDoc(identity_did::Error::MethodNotFound)) - .and_then(|method| { - if Self::is_signing_method_type(method.type_()) { - Ok(method) - } else { - Err(Error::InvalidDocumentSigningMethodType) - } - }) - } - // =========================================================================== // Signatures // =========================================================================== @@ -388,236 +260,92 @@ impl IotaDocument { .map_err(Into::into) } - /// Signs this DID document with the verification method specified by `method_query`. - /// The `method_query` may be the full [`IotaDIDUrl`] of the method or just its fragment, - /// e.g. "#sign-0". The signing method must have a capability invocation verification - /// relationship. - /// - /// NOTE: does not validate whether `private_key` corresponds to the verification method. - /// See [`IotaDocument::verify_document`]. - /// - /// # Errors - /// - /// Fails if an unsupported verification method is used or the signature operation fails. - pub fn sign_self<'query, Q>(&mut self, private_key: &PrivateKey, method_query: Q) -> Result<()> - where - Q: Into>, - { - // Ensure method is permitted to sign document updates. - // TODO: re-map this error - let method: &IotaVerificationMethod = self.resolve_signing_method(method_query.into())?; - - // Specify the full method DID Url if the verification method id does not match the document id. - let method_did: &IotaDID = method.id().did(); - let method_id: String = if method_did == self.id() { - method - .id() - .fragment() - .map(|fragment| core::iter::once('#').chain(fragment.chars()).collect()) - .ok_or(Error::DocumentSignError("method missing id fragment", None))? - } else { - method.id().to_string() - }; - - // Sign document. - match method.type_() { - MethodType::Ed25519VerificationKey2018 => { - JcsEd25519::::create_signature(self, method_id, private_key.as_ref(), ProofOptions::default()) - .map_err(|err| Error::DocumentSignError("Ed25519 signature failed", Some(err)))?; - } - MethodType::X25519KeyAgreementKey2019 => { - // X25519 cannot be used to sign documents. - return Err(Error::DocumentSignError( - "X25519KeyAgreementKey2019 cannot sign documents", - None, - )); - } - } - - Ok(()) - } - // =========================================================================== - // Verification + // Packing // =========================================================================== - /// Verifies the signature of the provided `data` was created using a verification method - /// in this DID Document. - /// - /// # Errors - /// - /// Fails if an unsupported verification method is used, data - /// serialization fails, or the verification operation fails. - pub fn verify_data(&self, data: &X, options: &VerifierOptions) -> Result<()> - where - X: Serialize + GetSignature + ?Sized, - { - self.document.verify_data(data, options).map_err(Into::into) - } - - /// Verifies that the signature on the DID document `signed` was generated by a valid method from - /// this DID document. - /// - /// # Errors - /// - /// Fails if: - /// - The signature proof section is missing in the `signed` document. - /// - The method is not found in this document. - /// - An unsupported verification method is used. - /// - The signature verification operation fails. - pub fn verify_document(&self, signed: &IotaDocument) -> Result<()> { - // Ensure signing method is allowed to sign document updates. - let options = VerifierOptions::default() - .method_scope(MethodScope::capability_invocation()) - .method_type(Self::UPDATE_METHOD_TYPES.to_vec()); - self.verify_data(signed, &options) - } - - /// Verifies whether `document` is a valid root DID document according to the IOTA DID method - /// specification. - /// - /// It must be signed using a verification method with a public key whose BLAKE2b-256 hash matches - /// the DID tag. - pub fn verify_root_document(document: &IotaDocument) -> Result<()> { - // The previous message id must be null. - if !document.metadata.previous_message_id.is_null() { - return Err(Error::InvalidRootDocument("previousMessageId not null")); - } - - // Validate the hash of the public key matches the DID tag. - let signature: &Proof = document - .signature() - .ok_or(Error::InvalidRootDocument("missing signature"))?; - let method: &IotaVerificationMethod = document - .resolve_method(signature, None) - .ok_or(Error::InvalidDoc(identity_did::Error::MethodNotFound))?; - let public: PublicKey = method.data().try_decode()?.into(); - if document.id().tag() != IotaDID::encode_key(public.as_ref()) { - return Err(Error::InvalidRootDocument( - "DID tag does not match any verification method", - )); - } - - // Validate the document is correctly self-signed. - document.verify_document(document) + /// Serializes the document for inclusion in an Alias Output's state metadata + /// with the default [`StateMetadataEncoding`]. + pub fn pack(self) -> Result> { + self.pack_with_encoding(StateMetadataEncoding::Json) } - // =========================================================================== - // Diffs - // =========================================================================== - - /// Creates a `DiffMessage` representing the changes between `self` and `other`. - /// - /// The returned `DiffMessage` will have a digital signature created using the - /// specified `private_key` and `method_query`. - /// - /// NOTE: the method must be a capability invocation method. - /// - /// # Errors - /// - /// Fails if the diff operation or signature operation fails. - #[deprecated(since = "0.5.0", note = "diff chain features are slated for removal")] - pub fn diff<'query, 's: 'query, Q>( - &'query self, - other: &Self, - message_id: MessageId, - private_key: &'query PrivateKey, - method_query: Q, - ) -> Result - where - Q: Into>, - { - let mut diff: DiffMessage = DiffMessage::new(self, other, message_id)?; - - // Ensure the method is allowed to sign document updates. - let method_query: DIDUrlQuery<'_> = method_query.into(); - let _ = self.resolve_signing_method(method_query.clone())?; - - self.sign_data(&mut diff, private_key, method_query, ProofOptions::default())?; - - Ok(diff) + /// Serializes the document for inclusion in an Alias Output's state metadata. + pub fn pack_with_encoding(self, encoding: StateMetadataEncoding) -> Result> { + StateMetadataDocument::from(self).pack(encoding) } - /// Verifies the signature of the `diff` was created using a capability invocation method - /// in this DID Document. + /// Deserializes the document from the state metadata bytes of an Alias Output. /// - /// # Errors + /// If `allow_empty` is true, this will return an empty DID document marked as `deactivated` + /// if `state_metadata` is empty. /// - /// Fails if an unsupported verification method is used or the verification operation fails. - #[deprecated(since = "0.5.0", note = "diff chain features are slated for removal")] - pub fn verify_diff(&self, diff: &DiffMessage) -> Result<()> { - // Ensure signing method is allowed to sign document updates. - let options = VerifierOptions::default() - .method_scope(MethodScope::capability_invocation()) - .method_type(Self::UPDATE_METHOD_TYPES.to_vec()); - self.verify_data(diff, &options).map_err(Into::into) + /// NOTE: `did` is required since it is omitted from the serialized DID Document and + /// cannot be inferred from the state metadata. It also indicates the network, which is not + /// encoded in the `AliasId` alone. + pub fn unpack(did: &IotaDID, state_metadata: &[u8], allow_empty: bool) -> Result { + if state_metadata.is_empty() && allow_empty { + let mut empty_document = IotaDocument::new_with_id(did.clone()); + empty_document.metadata.created = None; + empty_document.metadata.updated = None; + empty_document.metadata.deactivated = Some(true); + return Ok(empty_document); + } + StateMetadataDocument::unpack(state_metadata).and_then(|doc| doc.into_iota_document(did)) } +} - /// Verifies a [`DiffMessage`] signature and merges the changes into `self`. - /// - /// If merging fails `self` remains unmodified, otherwise `self` represents - /// the merged document state. - /// - /// See [`IotaDocument::verify_diff`]. - /// - /// # Errors - /// - /// Fails if the merge operation or signature operation fails. - #[deprecated(since = "0.5.0", note = "diff chain features are slated for removal")] - pub fn merge_diff(&mut self, diff: &DiffMessage) -> Result<()> { - self.verify_diff(diff)?; - - *self = diff.merge(self)?; - - Ok(()) - } +#[cfg(feature = "client")] +mod client_document { + use std::ops::Deref; + + use crate::block::output::AliasId; + use crate::block::output::Output; + use crate::block::output::OutputId; + use crate::block::payload::transaction::TransactionEssence; + use crate::block::payload::Payload; + use crate::block::Block; + use crate::error::Result; + use crate::Error; + use crate::NetworkName; - // =========================================================================== - // Publishing - // =========================================================================== + use super::*; - /// Returns the Tangle index of the integration chain for this DID. - /// - /// This is equivalent to the tag segment of the [`IotaDID`]. - /// - /// E.g. - /// For an [`IotaDocument`] `doc` with `"did:iota:1234567890abcdefghijklmnopqrstuvxyzABCDEFGHI"`, - /// `doc.integration_index() == "1234567890abcdefghijklmnopqrstuvxyzABCDEFGHI"` - pub fn integration_index(&self) -> &str { - self.id().tag() - } + impl IotaDocument { + /// Returns all DID documents of the Alias Outputs contained in the block's transaction payload + /// outputs, if any. + /// + /// Errors if any Alias Output does not contain a valid or empty DID Document. + pub fn unpack_from_block(network: &NetworkName, block: &Block) -> Result> { + let mut documents = Vec::new(); + + if let Some(Payload::Transaction(tx_payload)) = block.payload() { + let TransactionEssence::Regular(regular) = tx_payload.essence(); + + for (index, output) in regular.outputs().iter().enumerate() { + if let Output::Alias(alias_output) = output { + let alias_id = if alias_output.alias_id().is_null() { + AliasId::from( + OutputId::new( + tx_payload.id(), + index + .try_into() + .map_err(|_| Error::OutputIdConversionError(format!("output index {index} must fit into a u16")))?, + ) + .map_err(|err| Error::OutputIdConversionError(err.to_string()))?, + ) + } else { + alias_output.alias_id().to_owned() + }; + + let did: IotaDID = IotaDID::new(alias_id.deref(), network); + documents.push(IotaDocument::unpack(&did, alias_output.state_metadata(), true)?); + } + } + } - /// Returns the Tangle index of the DID diff chain. This should only be called on messages - /// from documents published on the integration chain. - /// - /// This is the Base58-btc encoded SHA-256 digest of the hex-encoded message id. - #[deprecated(since = "0.5.0", note = "diff chain features are slated for removal")] - pub fn diff_index(message_id: &MessageId) -> Result { - if message_id.is_null() { - return Err(Error::InvalidDocumentMessageId); + Ok(documents) } - - Ok(IotaDID::encode_key(message_id.encode_hex().as_bytes())) - } - - /// Returns a vector of all verification methods capable of signing a document update. - pub fn extract_signing_keys(&self) -> Vec> { - self - .core_document() - .capability_invocation() - .iter() - .map(|method_ref| match method_ref { - MethodRef::Embed(method) => Some(method), - MethodRef::Refer(did_url) => self.core_document().resolve_method(did_url, None), - }) - .filter(|method| { - if let Some(method) = method { - IotaDocument::is_signing_method_type(method.type_()) - } else { - true - } - }) - .collect() } } @@ -634,7 +362,7 @@ impl Document for IotaDocument { where Q: Into>, { - self.core_document().resolve_service(query) + self.document.resolve_service(query) } fn resolve_method<'query, 'me, Q>( @@ -645,14 +373,14 @@ impl Document for IotaDocument { where Q: Into>, { - self.core_document().resolve_method(query, scope) + self.document.resolve_method(query, scope) } fn verify_data(&self, data: &X, options: &VerifierOptions) -> identity_did::Result<()> where X: Serialize + GetSignature + ?Sized, { - self.core_document().verify_data(data, options) + self.document.verify_data(data, options) } } @@ -668,120 +396,79 @@ mod iota_document_revocation { impl IotaDocument { /// If the document has a [`RevocationBitmap`](identity_did::revocation::RevocationBitmap) /// service identified by `service_query`, revoke all specified `indices`. - pub fn revoke_credentials<'query, 'me, Q>(&mut self, service_query: Q, credential_indices: &[u32]) -> Result<()> + pub fn revoke_credentials<'query, 'me, Q>(&mut self, service_query: Q, indices: &[u32]) -> Result<()> where Q: Into>, { self .core_document_mut() - .revoke_credentials(service_query, credential_indices) + .revoke_credentials(service_query, indices) .map_err(Error::RevocationError) } /// If the document has a [`RevocationBitmap`](identity_did::revocation::RevocationBitmap) /// service with an id by `service_query`, unrevoke all specified `indices`. - pub fn unrevoke_credentials<'query, 'me, Q>( - &'me mut self, - service_query: Q, - credential_indices: &[u32], - ) -> Result<()> + pub fn unrevoke_credentials<'query, 'me, Q>(&'me mut self, service_query: Q, indices: &[u32]) -> Result<()> where Q: Into>, { self .core_document_mut() - .unrevoke_credentials(service_query, credential_indices) + .unrevoke_credentials(service_query, indices) .map_err(Error::RevocationError) } } } -impl From<(IotaCoreDocument, IotaDocumentMetadata, Option)> for IotaDocument { - fn from((document, metadata, proof): (IotaCoreDocument, IotaDocumentMetadata, Option)) -> Self { - Self { - document, - metadata, - proof, - } - } -} - impl From for IotaCoreDocument { fn from(document: IotaDocument) -> Self { document.document } } -impl Display for IotaDocument { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.fmt_json(f) - } -} - -impl GetSignature for IotaDocument { - fn signature(&self) -> Option<&Proof> { - self.proof.as_ref() +impl From for CoreDocument { + fn from(document: IotaDocument) -> Self { + document.document.map(Into::into, |id| id) } } -impl GetSignatureMut for IotaDocument { - fn signature_mut(&mut self) -> Option<&mut Proof> { - self.proof.as_mut() +impl From<(IotaCoreDocument, IotaDocumentMetadata)> for IotaDocument { + fn from((document, metadata): (IotaCoreDocument, IotaDocumentMetadata)) -> Self { + Self { document, metadata } } } -impl SetSignature for IotaDocument { - fn set_signature(&mut self, signature: Proof) { - self.proof = Some(signature) +impl Display for IotaDocument { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.fmt_json(f) } } -// Workaround to enable using this with the credential and presentation validators. -impl AsRef for IotaDocument { - fn as_ref(&self) -> &IotaDocument { - self - } +impl TryMethod for IotaDocument { + const TYPE: MethodUriType = MethodUriType::Absolute; } #[cfg(test)] mod tests { - use std::str::FromStr; - - use bee_message::MESSAGE_ID_LENGTH; - - use identity_core::common::Object; - use identity_core::common::OneOrSet; use identity_core::common::Timestamp; - use identity_core::common::Value; use identity_core::convert::FromJson; use identity_core::convert::ToJson; + use identity_core::crypto::KeyPair; use identity_core::crypto::KeyType; - use identity_core::utils::BaseEncoding; use identity_did::did::DID; use identity_did::verifiable::VerifiableProperties; use identity_did::verification::MethodData; - - use crate::tangle::Network; + use identity_did::verification::MethodType; use super::*; - const DID_ID: &str = "did:iota:HGE4tecHWL2YiZv5qAGtH7gaeQcaz2Z1CR15GWmMjY1M"; - const DID_METHOD_ID: &str = "did:iota:HGE4tecHWL2YiZv5qAGtH7gaeQcaz2Z1CR15GWmMjY1M#sign-0"; - const DID_DEVNET_ID: &str = "did:iota:dev:HGE4tecHWL2YiZv5qAGtH7gaeQcaz2Z1CR15GWmMjY1M"; - const DID_DEVNET_METHOD_ID: &str = "did:iota:dev:HGE4tecHWL2YiZv5qAGtH7gaeQcaz2Z1CR15GWmMjY1M#sign-0"; - fn valid_did() -> IotaDID { - DID_ID.parse().unwrap() - } - - fn valid_metadata() -> IotaDocumentMetadata { - let mut metadata: IotaDocumentMetadata = IotaDocumentMetadata::new(); - metadata.created = Some(Timestamp::parse("2020-01-02T00:00:00Z").unwrap()); - metadata.updated = Some(Timestamp::parse("2020-01-02T00:00:00Z").unwrap()); - metadata + "did:iota:0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + .parse() + .unwrap() } - fn valid_verification_method(controller: &IotaDID, fragment: &str) -> IotaVerificationMethod { + fn generate_method(controller: &IotaDID, fragment: &str) -> IotaVerificationMethod { VerificationMethod::builder(Default::default()) .id(controller.to_url().join(fragment).unwrap()) .controller(controller.clone()) @@ -791,180 +478,54 @@ mod tests { .unwrap() } - fn valid_iota_document(controller: &IotaDID) -> IotaDocument { - let metadata: IotaDocumentMetadata = valid_metadata(); + fn generate_document(id: &IotaDID) -> IotaDocument { + let mut metadata: IotaDocumentMetadata = IotaDocumentMetadata::new(); + metadata.created = Some(Timestamp::parse("2020-01-02T00:00:00Z").unwrap()); + metadata.updated = Some(Timestamp::parse("2020-01-02T00:00:00Z").unwrap()); let document: IotaCoreDocument = IotaCoreDocument::builder(Object::default()) - .id(controller.clone()) - .controller(controller.clone()) - .verification_method(valid_verification_method(controller, "#key-1")) - .verification_method(valid_verification_method(controller, "#key-2")) - .verification_method(valid_verification_method(controller, "#key-3")) - .authentication(valid_verification_method(controller, "#auth-key")) - .authentication(controller.to_url().join("#key-3").unwrap()) - .key_agreement(controller.to_url().join("#key-4").unwrap()) + .id(id.clone()) + .controller(id.clone()) + .verification_method(generate_method(id, "#key-1")) + .verification_method(generate_method(id, "#key-2")) + .verification_method(generate_method(id, "#key-3")) + .authentication(generate_method(id, "#auth-key")) + .authentication(id.to_url().join("#key-3").unwrap()) .build() .unwrap(); - IotaDocument::from((document, metadata, None)) - } - - fn generate_testkey() -> KeyPair { - let private_key: Vec = vec![ - 40, 185, 109, 70, 134, 119, 123, 37, 190, 254, 232, 186, 106, 48, 213, 63, 133, 223, 167, 126, 159, 43, 178, 4, - 190, 217, 52, 66, 92, 63, 69, 84, - ]; - let public_key: Vec = vec![ - 212, 151, 158, 35, 16, 178, 19, 27, 83, 109, 212, 138, 141, 134, 122, 246, 156, 148, 227, 69, 68, 251, 190, 31, - 25, 101, 230, 20, 130, 188, 121, 196, - ]; - KeyPair::from(( - KeyType::Ed25519, - PublicKey::from(public_key), - PrivateKey::from(private_key), - )) - } - - fn compare_document(document: &IotaDocument) { - assert_eq!(document.id().to_string(), DID_ID); - let default_signing_method: &IotaVerificationMethod = document.default_signing_method().unwrap(); - - assert_eq!(default_signing_method.id().to_string(), DID_METHOD_ID); - assert_eq!( - document.default_signing_method().unwrap().type_(), - MethodType::Ed25519VerificationKey2018 - ); - assert_eq!( - document.default_signing_method().unwrap().data(), - &MethodData::PublicKeyMultibase("zFJsXMk9UqpJf3ZTKnfEQAhvBrVLKMSx9ZeYwQME6c6tT".to_owned()) - ); - } - - fn compare_document_devnet(document: &IotaDocument) { - assert_eq!(document.id().to_string(), DID_DEVNET_ID); - assert_eq!(document.id().network_str(), Network::Devnet.name_str()); - assert_eq!( - document.default_signing_method().unwrap().id().to_string(), - DID_DEVNET_METHOD_ID - ); - assert_eq!( - document.default_signing_method().unwrap().type_(), - MethodType::Ed25519VerificationKey2018 - ); - assert_eq!( - document.default_signing_method().unwrap().data(), - &MethodData::PublicKeyMultibase("zFJsXMk9UqpJf3ZTKnfEQAhvBrVLKMSx9ZeYwQME6c6tT".to_owned()) - ); + IotaDocument::from((document, metadata)) } #[test] fn test_new() { - // VALID new() - let keypair: KeyPair = generate_testkey(); - let document: IotaDocument = IotaDocument::new(&keypair).unwrap(); - compare_document(&document); - - // VALID from_verification_method() - let method: IotaVerificationMethod = document.default_signing_method().unwrap().clone(); - let document: IotaDocument = IotaDocument::from_verification_method(method).unwrap(); - compare_document(&document); - } - - #[test] - fn test_new_with_options_network() { - let keypair: KeyPair = generate_testkey(); - let document: IotaDocument = IotaDocument::new_with_options(&keypair, Some(Network::Devnet.name()), None).unwrap(); - compare_document_devnet(&document); - } - - #[test] - fn test_new_with_options_fragment() { - let keypair: KeyPair = generate_testkey(); - let document: IotaDocument = IotaDocument::new_with_options(&keypair, None, Some("test-key")).unwrap(); - assert_eq!( - document.default_signing_method().unwrap().id().fragment().unwrap(), - "test-key" - ); - } - - #[test] - fn test_new_with_options_empty_fragment() { - let keypair: KeyPair = generate_testkey(); - let result: Result = IotaDocument::new_with_options(&keypair, None, Some("")); - assert!(result.is_err()); - } - - #[test] - fn test_no_controller() { - let keypair: KeyPair = generate_testkey(); - let document: IotaDocument = IotaDocument::new(&keypair).unwrap(); - assert_eq!(document.controller(), None); - } - - #[test] - fn test_controller_from_core() { - // One controller. - { - let controller: IotaDID = valid_did(); - let mut document: IotaDocument = valid_iota_document(&controller); - let expected: IotaDID = IotaDID::new(&[0; 32]).unwrap(); - *document.controller_mut() = Some(OneOrSet::new_one(expected.clone())); - assert_eq!(document.controller().unwrap().as_slice(), &[expected]); - // Unset. - *document.controller_mut() = None; - assert!(document.controller().is_none()); - } - - // Many controllers. - { - let controller: IotaDID = valid_did(); - let mut document: IotaDocument = valid_iota_document(&controller); - let expected_controllers: Vec = vec![ - controller, - IotaDID::new(&[0; 32]).unwrap(), - IotaDID::new(&[1; 32]).unwrap(), - IotaDID::new(&[2; 32]).unwrap(), - ]; - *document.controller_mut() = Some(expected_controllers.clone().try_into().unwrap()); - assert_eq!(document.controller().unwrap().as_slice(), &expected_controllers); - // Unset. - *document.controller_mut() = None; - assert!(document.controller().is_none()); - } - } - - #[test] - fn test_methods_new() { - let keypair: KeyPair = generate_testkey(); - let document: IotaDocument = IotaDocument::new(&keypair).unwrap(); - - // An IotaDocument created from a keypair has a single verification method, namely an - // Ed25519 signature. - let expected = IotaVerificationMethod::builder(Default::default()) - .id(DID_METHOD_ID.parse().unwrap()) - .controller(valid_did()) - .type_(MethodType::Ed25519VerificationKey2018) - .data(MethodData::PublicKeyMultibase( - "zFJsXMk9UqpJf3ZTKnfEQAhvBrVLKMSx9ZeYwQME6c6tT".into(), - )) - .build() - .unwrap(); - - let mut methods = document.methods(); - - assert_eq!(methods.next(), Some(expected).as_ref()); - assert_eq!(methods.next(), None); + // VALID new(). + let network: NetworkName = NetworkName::try_from("test").unwrap(); + let placeholder: IotaDID = IotaDID::placeholder(&network); + let doc1: IotaDocument = IotaDocument::new(&network); + assert_eq!(doc1.id().network_str(), network.as_ref()); + assert_eq!(doc1.id().tag(), placeholder.tag()); + assert_eq!(doc1.id(), &placeholder); + assert_eq!(doc1.methods().count(), 0); + assert!(doc1.service().is_empty()); + + // VALID new_with_id(). + let did: IotaDID = valid_did(); + let doc2: IotaDocument = IotaDocument::new_with_id(did.clone()); + assert_eq!(doc2.id(), &did); + assert_eq!(doc2.methods().count(), 0); + assert!(doc2.service().is_empty()); } #[test] - fn test_methods_from_core() { + fn test_methods() { let controller: IotaDID = valid_did(); - let document: IotaDocument = valid_iota_document(&controller); + let document: IotaDocument = generate_document(&controller); let expected: Vec = vec![ - valid_verification_method(&controller, "#key-1"), - valid_verification_method(&controller, "#key-2"), - valid_verification_method(&controller, "#key-3"), - valid_verification_method(&controller, "#auth-key"), + generate_method(&controller, "#key-1"), + generate_method(&controller, "#key-2"), + generate_method(&controller, "#key-3"), + generate_method(&controller, "#auth-key"), ]; let mut methods = document.methods(); @@ -975,344 +536,6 @@ mod tests { assert_eq!(methods.next(), None); } - #[test] - fn test_sign_self() { - let keypair: KeyPair = generate_testkey(); - let mut document: IotaDocument = IotaDocument::new(&keypair).unwrap(); - assert!(document.verify_document(&document).is_err()); - - // Sign with the default capability invocation method. - document - .sign_self( - keypair.private(), - document.default_signing_method().unwrap().id().clone(), - ) - .unwrap(); - assert!(document.verify_document(&document).is_ok()); - } - - #[test] - fn test_sign_self_new_method() { - let keypair: KeyPair = generate_testkey(); - let mut document: IotaDocument = IotaDocument::new(&keypair).unwrap(); - assert!(document.verify_document(&document).is_err()); - - // Add a new capability invocation method directly - let new_keypair: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); - let new_method: IotaVerificationMethod = IotaVerificationMethod::new( - document.id().clone(), - new_keypair.type_(), - new_keypair.public(), - "new_signer", - ) - .unwrap(); - document - .insert_method(new_method, MethodScope::capability_invocation()) - .unwrap(); - - // INVALID - try sign using the wrong private key - document.sign_self(keypair.private(), "#new_signer").unwrap(); - assert!(document.verify_document(&document).is_err()); - - // VALID - Sign with the new capability invocation method private key - document.sign_self(new_keypair.private(), "#new_signer").unwrap(); - assert!(document.verify_document(&document).is_ok()); - } - - #[test] - fn test_sign_self_embedded_controller_method_with_same_fragment() { - let keypair: KeyPair = generate_testkey(); - let mut document: IotaDocument = IotaDocument::new(&keypair).unwrap(); - assert!(document.verify_document(&document).is_err()); - - // Add a new signing method from a controller DID Document with the SAME FRAGMENT - // as the default signing method. - let controller_keypair: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); - let controller_did: IotaDID = IotaDID::new(controller_keypair.public().as_ref()).unwrap(); - let controller_method: IotaVerificationMethod = IotaVerificationMethod::new( - controller_did, - controller_keypair.type_(), - controller_keypair.public(), - IotaDocument::DEFAULT_METHOD_FRAGMENT, - ) - .unwrap(); - document - .insert_method(controller_method.clone(), MethodScope::capability_invocation()) - .unwrap(); - - // VALID - resolving the fragment alone should return the first matching method in the list. - let default_signing_method: &IotaVerificationMethod = document.default_signing_method().unwrap(); - assert_eq!( - document - .resolve_method(IotaDocument::DEFAULT_METHOD_FRAGMENT, None) - .unwrap(), - default_signing_method - ); - // VALID - resolving the entire id should return the exact method. - assert_eq!( - document.resolve_method(default_signing_method.id(), None).unwrap(), - default_signing_method - ); - assert_eq!( - document.resolve_method(controller_method.id(), None).unwrap(), - &controller_method - ); - - // INVALID - sign with the controller's private key referencing only the fragment. - // Fails since both sign_self and verify_document resolve the wrong method. - document - .sign_self(controller_keypair.private(), IotaDocument::DEFAULT_METHOD_FRAGMENT) - .unwrap(); - assert!(document.verify_document(&document).is_err()); - - // VALID - sign with the controller's private key referencing the full DID-Url of the method. - document - .sign_self(controller_keypair.private(), controller_method.id()) - .unwrap(); - assert!(document.verify_document(&document).is_ok()); - } - - #[test] - fn test_sign_self_fails() { - fn generate_document() -> (IotaDocument, KeyPair) { - let keypair: KeyPair = generate_testkey(); - let document: IotaDocument = IotaDocument::new(&keypair).unwrap(); - (document, keypair) - } - - // INVALID - try sign referencing a non-existent verification method. - { - let (mut document, keypair) = generate_document(); - assert!(document.verify_document(&document).is_err()); - assert!(document.sign_self(keypair.private(), "#doesnotexist").is_err()); - assert!(document.verify_document(&document).is_err()); - } - - // INVALID - try sign using a random private key. - { - let (mut document, _) = generate_document(); - let random_keypair: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); - document - .sign_self( - random_keypair.private(), - document.default_signing_method().unwrap().id().clone(), - ) - .unwrap(); - assert!(document.verify_document(&document).is_err()); - } - - // INVALID - try sign using any verification relationship other than capability invocation. - for method_scope in [ - MethodScope::VerificationMethod, - MethodScope::assertion_method(), - MethodScope::capability_delegation(), - MethodScope::authentication(), - MethodScope::key_agreement(), - ] { - let (mut document, _) = generate_document(); - // Add a new method unable to sign the document. - let keypair_new: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); - let method_new: IotaVerificationMethod = IotaVerificationMethod::new( - document.id().clone(), - keypair_new.type_(), - keypair_new.public(), - "new_signer", - ) - .unwrap(); - document.insert_method(method_new, method_scope).unwrap(); - // Try sign the document using the new key. - assert!(document.sign_self(keypair_new.private(), "#new_signer").is_err()); - assert!(document.verify_document(&document).is_err()); - assert!(IotaDocument::verify_root_document(&document).is_err()); - } - - // INVALID - try sign using a X25519 key. - { - let (mut document, _) = generate_document(); - let x25519: KeyPair = KeyPair::new(KeyType::X25519).unwrap(); - let x25519_method = - IotaVerificationMethod::new(document.id().clone(), x25519.type_(), x25519.public(), "kex-0").unwrap(); - document - .insert_method(x25519_method, MethodScope::capability_invocation()) - .unwrap(); - assert!(document.sign_self(x25519.private(), "kex-0").is_err()); - assert!(document.verify_document(&document).is_err()); - } - } - - #[test] - fn test_diff_signing_methods() { - // Ensure only capability invocation methods are allowed to sign a diff. - for scope in [ - MethodScope::assertion_method(), - MethodScope::authentication(), - MethodScope::capability_delegation(), - MethodScope::capability_invocation(), - MethodScope::key_agreement(), - MethodScope::VerificationMethod, - ] { - let key1: KeyPair = generate_testkey(); - let mut doc1: IotaDocument = IotaDocument::new(&key1).unwrap(); - // Add a new verification relationship. - let key2: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); - let method_fragment = format!("{}-1", scope.as_str().to_ascii_lowercase()); - let method_new: IotaVerificationMethod = - IotaVerificationMethod::new(doc1.id().clone(), key2.type_(), key2.public(), method_fragment.as_str()).unwrap(); - assert!(doc1.insert_method(method_new, scope).is_ok()); - assert!(doc1 - .core_document() - .resolve_method(method_fragment.as_str(), Some(scope)) - .is_some()); - - // Add a service to an updated document. - let mut doc2: IotaDocument = doc1.clone(); - let service: IotaService = Service::from_json( - r#"{ - "id":"did:iota:HGE4tecHWL2YiZv5qAGtH7gaeQcaz2Z1CR15GWmMjY1N#linked-domain", - "type": "LinkedDomains", - "serviceEndpoint": "https://bar.example.com" - }"#, - ) - .unwrap(); - doc2.insert_service(service); - - // Try generate and sign a diff using the specified method. - let diff_result = doc1.diff( - &doc2, - MessageId::new([3_u8; 32]), - key2.private(), - method_fragment.as_str(), - ); - if scope == MethodScope::capability_invocation() { - let diff = diff_result.unwrap(); - assert!(doc1.verify_data(&diff, &VerifierOptions::default()).is_ok()); - assert!(doc1.verify_diff(&diff).is_ok()); - } else { - assert!(diff_result.is_err()); - } - } - } - - #[test] - fn test_diff_properties() { - // Ensure custom fields added to properties are retained by diffs. - let key1: KeyPair = generate_testkey(); - let doc1: IotaDocument = IotaDocument::new(&key1).unwrap(); - let message_id: MessageId = MessageId::new([3_u8; 32]); - - // Add a new property on the document. - let doc2 = { - let mut doc2: IotaDocument = doc1.clone(); - doc2.properties_mut().insert("foo".into(), 123.into()); - let diff2: DiffMessage = doc1 - .diff( - &doc2, - message_id, - key1.private(), - doc1.default_signing_method().unwrap().id(), - ) - .unwrap(); - assert!(doc1.verify_diff(&diff2).is_ok()); - assert_eq!( - diff2.merge(&doc1).unwrap().properties().get("foo").unwrap(), - &Value::from(123) - ); - doc2 - }; - - // Mutate a property on the document. - let doc3 = { - let mut doc3: IotaDocument = doc2.clone(); - *doc3.properties_mut().get_mut("foo").unwrap() = 456.into(); - let diff3: DiffMessage = doc2 - .diff( - &doc3, - message_id, - key1.private(), - doc2.default_signing_method().unwrap().id(), - ) - .unwrap(); - assert!(doc2.verify_diff(&diff3).is_ok()); - assert_eq!( - diff3.merge(&doc2).unwrap().properties().get("foo").unwrap(), - &Value::from(456) - ); - doc3 - }; - - // Remove a property on the document. - { - let mut doc4: IotaDocument = doc3.clone(); - assert_eq!(doc4.properties_mut().remove("foo").unwrap(), Value::from(456)); - let diff4: DiffMessage = doc3 - .diff( - &doc4, - message_id, - key1.private(), - doc3.default_signing_method().unwrap().id(), - ) - .unwrap(); - assert!(doc3.verify_diff(&diff4).is_ok()); - assert!(diff4.merge(&doc3).unwrap().properties().get("foo").is_none()); - } - - // Add a new property on the metadata. - let doc5 = { - let mut doc5: IotaDocument = doc1.clone(); - doc5.metadata.properties.insert("bar".into(), 789.into()); - let diff5: DiffMessage = doc1 - .diff( - &doc5, - message_id, - key1.private(), - doc1.default_signing_method().unwrap().id(), - ) - .unwrap(); - assert!(doc1.verify_diff(&diff5).is_ok()); - assert_eq!( - diff5.merge(&doc1).unwrap().metadata.properties.get("bar").unwrap(), - &Value::from(789) - ); - doc5 - }; - - // Mutate a property on the metadata. - let doc6 = { - let mut doc6: IotaDocument = doc5.clone(); - *doc6.metadata.properties.get_mut("bar").unwrap() = "abc".into(); - let diff6: DiffMessage = doc5 - .diff( - &doc6, - message_id, - key1.private(), - doc5.default_signing_method().unwrap().id(), - ) - .unwrap(); - assert!(doc5.verify_diff(&diff6).is_ok()); - assert_eq!( - diff6.merge(&doc5).unwrap().metadata.properties.get("bar").unwrap(), - &Value::from("abc") - ); - doc6 - }; - - // Remove a property on the metadata. - { - let mut doc7: IotaDocument = doc6.clone(); - assert_eq!(doc7.metadata.properties.remove("bar").unwrap(), Value::from("abc")); - let diff7: DiffMessage = doc6 - .diff( - &doc7, - message_id, - key1.private(), - doc6.default_signing_method().unwrap().id(), - ) - .unwrap(); - assert!(doc6.verify_diff(&diff7).is_ok()); - assert!(diff7.merge(&doc6).unwrap().metadata.properties.get("bar").is_none()); - } - } - #[test] fn test_verify_data_with_scope() { fn generate_data() -> VerifiableProperties { @@ -1326,8 +549,7 @@ mod tests { properties } - let key: KeyPair = generate_testkey(); - let mut document: IotaDocument = IotaDocument::new(&key).unwrap(); + let mut document: IotaDocument = IotaDocument::new_with_id(valid_did()); // Try sign using each type of verification relationship. for scope in [ @@ -1384,273 +606,89 @@ mod tests { } #[test] - fn test_root_document() { - let keypair: KeyPair = generate_testkey(); - let mut document: IotaDocument = IotaDocument::new(&keypair).unwrap(); - assert!(IotaDocument::verify_root_document(&document).is_err()); - - // VALID - root document signed using the default method. - document - .sign_self( - keypair.private(), - document.default_signing_method().unwrap().id().clone(), - ) - .unwrap(); - assert!(document.verify_document(&document).is_ok()); - assert!(IotaDocument::verify_root_document(&document).is_ok()); - } - - #[test] - fn test_root_document_invalid() { - fn generate_root_document() -> (IotaDocument, KeyPair) { - let keypair: KeyPair = generate_testkey(); - (IotaDocument::new(&keypair).unwrap(), keypair) - } - - // INVALID - root document not signed. - { - let (document, _) = generate_root_document(); - assert!(IotaDocument::verify_root_document(&document).is_err()); - } - - // INVALID - root document previousMessageId not null. - { - let (mut document, keypair) = generate_root_document(); - document.metadata.previous_message_id = MessageId::new([3u8; MESSAGE_ID_LENGTH]); - document - .sign_self( - keypair.private(), - document.default_signing_method().unwrap().id().clone(), - ) - .unwrap(); - assert!(document.verify_document(&document).is_ok()); - assert!(IotaDocument::verify_root_document(&document).is_err()); - } - - // INVALID - root document signed with a key not matching the DID tag. - { - let (document, keypair) = generate_root_document(); - // Replace the base58 encoded public key with that of a different key. - let new_keypair: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); - let b58_old = BaseEncoding::encode_base58(keypair.public()); - let b58_new = BaseEncoding::encode_base58(new_keypair.public()); - let doc_json_modified = document.to_string().replace(&b58_old, &b58_new); - // Sign the document using the new key. - let mut new_document: IotaDocument = IotaDocument::from_json(&doc_json_modified).unwrap(); - new_document - .sign_self( - new_keypair.private(), - new_document.default_signing_method().unwrap().id().clone(), - ) - .unwrap(); - assert!(new_document.verify_document(&new_document).is_ok()); - assert!(IotaDocument::verify_root_document(&new_document).is_err()); - } - - // INVALID - root document signed using a different method that does not match the DID tag. - { - let (mut document, _) = generate_root_document(); - // Add a new method able to sign the document. - let keypair_new: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); - let method_new: IotaVerificationMethod = IotaVerificationMethod::new( - document.id().clone(), - keypair_new.type_(), - keypair_new.public(), - "new_signer", - ) - .unwrap(); - document - .insert_method(method_new, MethodScope::capability_invocation()) - .unwrap(); - // Sign the document using the new key. - document.sign_self(keypair_new.private(), "#new_signer").unwrap(); - assert!(document.verify_document(&document).is_ok()); - assert!(IotaDocument::verify_root_document(&document).is_err()); - } - } - - #[test] - fn test_json() { - let keypair: KeyPair = generate_testkey(); - let mut document: IotaDocument = IotaDocument::new(&keypair).unwrap(); - - let json_doc: String = document.to_string(); - let document2: IotaDocument = IotaDocument::from_json(&json_doc).unwrap(); - assert_eq!(document, document2); - - assert!(document - .sign_self( - keypair.private(), - document.default_signing_method().unwrap().id().clone(), - ) - .is_ok()); - - let json_doc: String = document.to_string(); - let document2: IotaDocument = IotaDocument::from_json(&json_doc).unwrap(); - assert_eq!(document, document2); - } - - #[test] - fn test_json_fieldnames() { - let keypair: KeyPair = generate_testkey(); - let document: IotaDocument = IotaDocument::new(&keypair).unwrap(); - let serialization: String = document.to_json().unwrap(); - assert_eq!( - serialization, - format!("{{\"doc\":{},\"meta\":{}}}", document.document, document.metadata) - ); - } - - #[test] - fn test_default_signing_method() { - let keypair: KeyPair = generate_testkey(); - let mut document: IotaDocument = IotaDocument::new(&keypair).unwrap(); - - let signing_method: IotaVerificationMethod = document.default_signing_method().unwrap().clone(); - - // Ensure signing method has an appropriate type. - assert!(IotaDocument::is_signing_method_type(signing_method.type_())); - - // Ensure signing method has a capability invocation relationship. - let capability_invocation: &IotaVerificationMethod = document - .core_document() - .resolve_method(signing_method.id(), Some(MethodScope::capability_invocation())) - .unwrap(); - assert_eq!(&signing_method, capability_invocation); - - // Ensure try_resolve_signing_method resolves it. - assert_eq!( - &signing_method, - document.resolve_signing_method(signing_method.id()).unwrap() - ); - - // Adding a new capability invocation method still returns the original method. - let new_keypair: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); - let new_method: IotaVerificationMethod = IotaVerificationMethod::new( - document.id().clone(), - new_keypair.type_(), - new_keypair.public(), - "new_signer", - ) - .unwrap(); - let new_method_id: IotaDIDUrl = new_method.id().clone(); - document - .insert_method(new_method, MethodScope::capability_invocation()) - .unwrap(); - assert_eq!(document.default_signing_method().unwrap().id(), signing_method.id()); - - // Removing the original signing method returns the next one. - document - .remove_method( - &document - .id() - .to_url() - .join(format!("#{}", IotaDocument::DEFAULT_METHOD_FRAGMENT)) - .unwrap(), - ) - .unwrap(); - assert_eq!(document.default_signing_method().unwrap().id(), &new_method_id); - - // Removing the last signing method causes an error. - document.remove_method(&new_method_id).unwrap(); - assert!(matches!( - document.default_signing_method(), - Err(Error::MissingSigningKey) - )); - } - - #[test] - fn test_document_services() { - let keypair: KeyPair = generate_testkey(); - let mut document: IotaDocument = IotaDocument::new(&keypair).unwrap(); - let service: IotaService = Service::from_json( - r#"{ - "id":"did:iota:HGE4tecHWL2YiZv5qAGtH7gaeQcaz2Z1CR15GWmMjY1N#linked-domain", + fn test_services() { + // VALID: add one service. + let mut document: IotaDocument = IotaDocument::new_with_id(valid_did()); + let url1: IotaDIDUrl = document.id().to_url().join("#linked-domain").unwrap(); + let service1: IotaService = Service::from_json(&format!( + r#"{{ + "id":"{}", "type": "LinkedDomains", "serviceEndpoint": "https://bar.example.com" - }"#, - ) + }}"#, + url1 + )) .unwrap(); - document.insert_service(service); - + document.insert_service(service1.clone()); + assert_eq!(1, document.service().len()); + assert_eq!(document.resolve_service(&url1), Some(&service1)); + assert_eq!(document.resolve_service("#linked-domain"), Some(&service1)); + assert_eq!(document.resolve_service("linked-domain"), Some(&service1)); + assert_eq!(document.resolve_service(""), None); + assert_eq!(document.resolve_service("#other"), None); + + // VALID: add two services. + let url2: IotaDIDUrl = document.id().to_url().join("#revocation").unwrap(); + let service2: IotaService = Service::from_json(&format!( + r#"{{ + "id":"{}", + "type": "RevocationBitmap2022", + "serviceEndpoint": "data:,blah" + }}"#, + url2 + )) + .unwrap(); + document.insert_service(service2.clone()); + assert_eq!(2, document.service().len()); + assert_eq!(document.resolve_service(&url2), Some(&service2)); + assert_eq!(document.resolve_service("#revocation"), Some(&service2)); + assert_eq!(document.resolve_service("revocation"), Some(&service2)); + assert_eq!(document.resolve_service(""), None); + assert_eq!(document.resolve_service("#other"), None); + + // INVALID: insert service with duplicate fragment fails. + let duplicate: IotaService = Service::from_json(&format!( + r#"{{ + "id":"{}", + "type": "DuplicateService", + "serviceEndpoint": "data:,duplicate" + }}"#, + url1 + )) + .unwrap(); + assert!(!document.insert_service(duplicate.clone())); + assert_eq!(2, document.service().len()); + let resolved: &IotaService = document.resolve_service(&url1).unwrap(); + assert_eq!(resolved, &service1); + assert_ne!(resolved, &duplicate); + + // VALID: remove services. + assert!(document.remove_service(&url1)); assert_eq!(1, document.service().len()); + let last_service: &IotaService = document.resolve_service(&url2).unwrap(); + assert_eq!(last_service, &service2); - assert!(document.remove_service( - &IotaDIDUrl::parse("did:iota:HGE4tecHWL2YiZv5qAGtH7gaeQcaz2Z1CR15GWmMjY1N#linked-domain").unwrap(), - )); + assert!(document.remove_service(&url2)); assert_eq!(0, document.service().len()); } - #[test] - fn test_relative_method_uri() { - let keypair: KeyPair = generate_testkey(); - let mut document: IotaDocument = IotaDocument::new(&keypair).unwrap(); - - assert!(document.signature().is_none()); - assert!(document - .sign_self( - keypair.private(), - document.default_signing_method().unwrap().id().clone(), - ) - .is_ok()); - - assert_eq!(document.signature().unwrap().verification_method(), "#sign-0"); - } - - #[test] - fn test_integration_index() { - let keypair: KeyPair = generate_testkey(); - let document: IotaDocument = IotaDocument::new(&keypair).unwrap(); - - // The integration chain index should just be the tag of the DID - let tag = document.id().tag(); - assert_eq!(document.integration_index(), tag); - } - - #[test] - fn test_diff_index() { - let message_id = MessageId::from_str("c38d6c541f98f780ddca6ad648ff0e073cd86c4dee248149c2de789d84d42132").unwrap(); - let diff_index = IotaDocument::diff_index(&message_id).expect("failed to generate diff_index"); - assert_eq!(diff_index, "2g45GsCAmkvQfcrHGUgqwQJLbYY3Gic8f23wf71sGGGP"); - } - - #[test] - fn test_new_document_verification_relationships() { - let keypair: KeyPair = generate_testkey(); - let document: IotaDocument = IotaDocument::new(&keypair).unwrap(); - let verification_method: &IotaVerificationMethod = document.resolve_method("#sign-0", None).unwrap(); - let expected_did_url: IotaDIDUrl = document.id().to_url().join("#sign-0").unwrap(); - - // Ensure capability invocation relationship. - let capability_invocation_method_id: &IotaDIDUrl = - document.core_document().capability_invocation().first().unwrap().id(); - assert_eq!(verification_method.id(), &expected_did_url); - assert_eq!(capability_invocation_method_id, &expected_did_url); - - // Ensure fragment of the capability invocation method reference is `authentication` - match document - .core_document() - .capability_invocation() - .first() - .unwrap() - .clone() - { - MethodRef::Refer(_) => panic!("capability invocation method should be embedded"), - MethodRef::Embed(method) => assert_eq!(method.id(), capability_invocation_method_id), - } - - // `methods` returns all embedded verification methods, so only one is expected. - assert_eq!(document.methods().count(), 1); - } - #[test] fn test_document_equality() { + let mut original_doc: IotaDocument = IotaDocument::new_with_id(valid_did()); let keypair1: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); - let original_doc: IotaDocument = IotaDocument::new_with_options(&keypair1, None, Some("test-0")).unwrap(); + let method1: IotaVerificationMethod = IotaVerificationMethod::new( + original_doc.id().to_owned(), + keypair1.type_(), + keypair1.public(), + "test-0", + ) + .unwrap(); + original_doc + .insert_method(method1, MethodScope::capability_invocation()) + .unwrap(); + // Update the key material of the existing verification method #test-0. let mut doc1 = original_doc.clone(); - - // Update the key material of the existing verification method test-0. let keypair2: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); let method2: IotaVerificationMethod = IotaVerificationMethod::new(doc1.id().to_owned(), keypair2.type_(), keypair2.public(), "test-0").unwrap(); @@ -1677,4 +715,42 @@ mod tests { assert!(insertion_result.is_err()); assert_eq!(doc1, doc2); } + + #[test] + fn test_unpack_empty() { + // VALID: unpack empty, deactivated document. + let did: IotaDID = valid_did(); + let empty: &[u8] = &[]; + let document: IotaDocument = IotaDocument::unpack(&did, empty, true).unwrap(); + assert_eq!(document.id(), &did); + assert_eq!(document.metadata.deactivated, Some(true)); + + // Ensure no other fields are injected. + // TODO: update this when controller/governor fields are added? + let json: String = format!("{{\"doc\":{{\"id\":\"{did}\"}},\"meta\":{{\"deactivated\":true}}}}"); + assert_eq!(document.to_json().unwrap(), json); + + // INVALID: reject empty document. + assert!(IotaDocument::unpack(&did, empty, false).is_err()); + } + + #[test] + fn test_json_roundtrip() { + let document: IotaDocument = generate_document(&valid_did()); + + let ser: String = document.to_json().unwrap(); + let de: IotaDocument = IotaDocument::from_json(&ser).unwrap(); + assert_eq!(document, de); + } + + #[test] + fn test_json_fieldnames() { + // Changing the serialization is a breaking change! + let document: IotaDocument = IotaDocument::new_with_id(valid_did()); + let serialization: String = document.to_json().unwrap(); + assert_eq!( + serialization, + format!("{{\"doc\":{},\"meta\":{}}}", document.document, document.metadata) + ); + } } diff --git a/identity_iota_core/src/document/iota_document_metadata.rs b/identity_iota_core/src/document/iota_document_metadata.rs index 8d4b466e5c..79f3f94902 100644 --- a/identity_iota_core/src/document/iota_document_metadata.rs +++ b/identity_iota_core/src/document/iota_document_metadata.rs @@ -4,44 +4,36 @@ use core::fmt::Debug; use core::fmt::Display; use core::fmt::Formatter; -use core::fmt::Result as FmtResult; use identity_core::common::Object; use identity_core::common::Timestamp; use identity_core::convert::FmtJson; - use serde::Deserialize; use serde::Serialize; -use crate::tangle::MessageId; -use crate::tangle::MessageIdExt; - -/// Additional attributes related to an IOTA DID Document. +/// Additional attributes related to a [`IotaDocument`][crate::IotaDocument]. #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct IotaDocumentMetadata { + // TODO: store created in the immutable metadata, if possible? #[serde(skip_serializing_if = "Option::is_none")] pub created: Option, #[serde(skip_serializing_if = "Option::is_none")] pub updated: Option, - #[serde( - rename = "previousMessageId", - default = "MessageId::null", - skip_serializing_if = "MessageId::is_null" - )] - pub previous_message_id: MessageId, + #[serde(skip_serializing_if = "Option::is_none")] + pub deactivated: Option, #[serde(flatten)] pub properties: Object, } impl IotaDocumentMetadata { - /// Creates a new `IotaDocumentMetadata` with the current system datetime used for `created` and - /// `updated` timestamps. + /// Creates a new `IotaDocumentMetadata` with the current system datetime used for `created` + /// and `updated` timestamps. pub fn new() -> Self { let now: Timestamp = Timestamp::now_utc(); Self { created: Some(now), updated: Some(now), - previous_message_id: MessageId::null(), + deactivated: None, properties: Object::default(), } } @@ -54,7 +46,7 @@ impl Default for IotaDocumentMetadata { } impl Display for IotaDocumentMetadata { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { self.fmt_json(f) } } diff --git a/identity_iota_core/src/document/mod.rs b/identity_iota_core/src/document/mod.rs index f3e2584234..a8dc2e8dea 100644 --- a/identity_iota_core/src/document/mod.rs +++ b/identity_iota_core/src/document/mod.rs @@ -1,13 +1,11 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -//! IOTA DID Document types and wrappers. - -pub use self::iota_document::IotaCoreDocument; -pub use self::iota_document::IotaDocument; -pub use self::iota_document::IotaService; -pub use self::iota_document::IotaVerificationMethod; -pub use self::iota_document_metadata::IotaDocumentMetadata; +pub use iota_document::IotaCoreDocument; +pub use iota_document::IotaDocument; +pub use iota_document::IotaService; +pub use iota_document::IotaVerificationMethod; +pub use iota_document_metadata::IotaDocumentMetadata; mod iota_document; mod iota_document_metadata; diff --git a/identity_iota_core/src/error.rs b/identity_iota_core/src/error.rs index 7a9c8e4cff..08f5542732 100644 --- a/identity_iota_core/src/error.rs +++ b/identity_iota_core/src/error.rs @@ -4,29 +4,43 @@ pub type Result = core::result::Result; #[derive(Debug, thiserror::Error, strum::IntoStaticStr)] +#[non_exhaustive] pub enum Error { - #[deprecated(since = "0.5.0", note = "diff chain features are slated for removal")] + #[error("serialization error")] + SerializationError(#[source] identity_core::Error), #[error("{0}")] - DiffError(#[from] identity_core::diff::Error), - #[error("{0}")] - InvalidDID(#[from] identity_did::did::DIDError), + DIDSyntaxError(#[from] identity_did::did::DIDError), #[error("{0}")] InvalidDoc(#[from] identity_did::Error), - #[error("Invalid Message: {0}")] - InvalidMessage(#[from] bee_message::Error), - - #[error("signing failed: {0}")] - DocumentSignError(&'static str, #[source] Option), - #[error("Invalid Document - Missing Message Id")] - InvalidDocumentMessageId, - #[error("Invalid Document - Signing Verification Method Type Not Supported")] - InvalidDocumentSigningMethodType, - #[error("Invalid Network Name")] - InvalidNetworkName, - #[error("invalid root document: {0}")] - InvalidRootDocument(&'static str), - #[error("Missing Signing Key")] - MissingSigningKey, + #[cfg(feature = "iota-client")] + #[error("DID update: {0}")] + DIDUpdateError(&'static str, #[source] Option), + #[cfg(feature = "iota-client")] + #[error("DID resolution failed")] + DIDResolutionError(#[source] iota_client::error::Error), + #[cfg(feature = "iota-client")] + #[error("{0}")] + BasicOutputBuildError(#[source] iota_client::block::Error), + #[error("\"{0}\" is not a valid network name")] + InvalidNetworkName(String), + #[error("unable to resolve a `{expected}` DID on network `{actual}`")] + NetworkMismatch { expected: String, actual: String }, + #[error("invalid state metadata {0}")] + InvalidStateMetadata(&'static str), #[error("credential revocation error")] RevocationError(#[source] identity_did::Error), + #[cfg(feature = "client")] + #[error("alias output build error")] + AliasOutputBuildError(#[source] crate::block::Error), + #[cfg(feature = "iota-client")] + #[error("output with id `{0}` is not an alias output")] + NotAnAliasOutput(iota_client::block::output::OutputId), + #[cfg(feature = "iota-client")] + #[error("converting a DTO to an output failed")] + OutputConversionError(#[source] iota_client::block::DtoError), + #[error("conversion to an OutputId failed: {0}")] + OutputIdConversionError(String), + #[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] + #[error("JavaScript function threw an exception: {0}")] + JsError(String), } diff --git a/identity_iota_core/src/lib.rs b/identity_iota_core/src/lib.rs index 45b3280037..8ce27a8e30 100644 --- a/identity_iota_core/src/lib.rs +++ b/identity_iota_core/src/lib.rs @@ -2,29 +2,29 @@ // SPDX-License-Identifier: Apache-2.0 #![forbid(unsafe_code)] -#![allow(deprecated)] -#![doc = include_str!("./../README.md")] #![allow(clippy::upper_case_acronyms)] -#![warn( - rust_2018_idioms, - unreachable_pub, - // missing_docs, - rustdoc::missing_crate_level_docs, - rustdoc::broken_intra_doc_links, - rustdoc::private_intra_doc_links, - rustdoc::private_doc_tests, - clippy::missing_safety_doc, - // clippy::missing_errors_doc, -)] -pub use self::error::Error; -pub use self::error::Result; +// Re-export the `bee_block` crate for implementer convenience. +#[cfg(all(feature = "client", not(feature = "iota-client")))] +pub use bee_block as block; +#[cfg(feature = "iota-client")] +pub use iota_client::block; -#[deprecated(since = "0.5.0", note = "diff chain features are slated for removal")] -pub mod diff; +#[cfg(feature = "client")] +pub use client::*; +pub use did::IotaDID; +pub use did::IotaDIDUrl; +pub use document::*; +pub use network::NetworkName; +pub use state_metadata::*; -pub mod did; -pub mod document; -pub mod tangle; +pub use self::error::Error; +pub use self::error::Result; +#[cfg(feature = "client")] +mod client; +mod did; +mod document; mod error; +mod network; +mod state_metadata; diff --git a/identity_stardust/src/network/mod.rs b/identity_iota_core/src/network/mod.rs similarity index 100% rename from identity_stardust/src/network/mod.rs rename to identity_iota_core/src/network/mod.rs diff --git a/identity_stardust/src/network/network_name.rs b/identity_iota_core/src/network/network_name.rs similarity index 94% rename from identity_stardust/src/network/network_name.rs rename to identity_iota_core/src/network/network_name.rs index 1b084b21ab..e1f9b2516c 100644 --- a/identity_stardust/src/network/network_name.rs +++ b/identity_iota_core/src/network/network_name.rs @@ -15,7 +15,7 @@ use serde::Serialize; use crate::error::Error; use crate::error::Result; -/// Network name compliant with the [`crate::StardustDID`] method specification. +/// Network name compliant with the [`crate::IotaDID`] method specification. #[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)] #[repr(transparent)] pub struct NetworkName(Cow<'static, str>); @@ -33,7 +33,7 @@ impl NetworkName { Ok(Self(name_cow)) } - /// Validates whether a string is a spec-compliant IOTA UTXO DID [`NetworkName`]. + /// Validates whether a string is a spec-compliant IOTA DID [`NetworkName`]. pub fn validate_network_name(name: &str) -> Result<()> { Some(()) .filter(|_| { diff --git a/identity_stardust/src/state_metadata/document.rs b/identity_iota_core/src/state_metadata/document.rs similarity index 83% rename from identity_stardust/src/state_metadata/document.rs rename to identity_iota_core/src/state_metadata/document.rs index 27cd1d0ee2..696f7104e0 100644 --- a/identity_stardust/src/state_metadata/document.rs +++ b/identity_iota_core/src/state_metadata/document.rs @@ -11,10 +11,10 @@ use serde::Serialize; use crate::error::Result; use crate::Error; -use crate::StardustCoreDocument; -use crate::StardustDID; -use crate::StardustDocument; -use crate::StardustDocumentMetadata; +use crate::IotaCoreDocument; +use crate::IotaDID; +use crate::IotaDocument; +use crate::IotaDocumentMetadata; use super::StateMetadataEncoding; use super::StateMetadataVersion; @@ -33,27 +33,27 @@ pub(crate) struct StateMetadataDocument { #[serde(rename = "doc")] document: CoreDocument, #[serde(rename = "meta")] - metadata: StardustDocumentMetadata, + metadata: IotaDocumentMetadata, } impl StateMetadataDocument { - /// Transforms the document into a [`StardustDocument`] by replacing all placeholders with `original_did`. - pub fn into_stardust_document(self, original_did: &StardustDID) -> Result { + /// Transforms the document into a [`IotaDocument`] by replacing all placeholders with `original_did`. + pub fn into_iota_document(self, original_did: &IotaDID) -> Result { let Self { document, metadata } = self; - let core_document: StardustCoreDocument = document.try_map( + let core_document: IotaCoreDocument = document.try_map( // Replace placeholder identifiers. |did| { if did == PLACEHOLDER_DID.as_ref() { Ok(original_did.clone()) } else { // TODO: wrap error? - StardustDID::try_from_core(did) + IotaDID::try_from_core(did) } }, // Do not modify properties. Ok, )?; - Ok(StardustDocument::from((core_document, metadata))) + Ok(IotaDocument::from((core_document, metadata))) } /// Pack a [`StateMetadataDocument`] into bytes, suitable for inclusion in @@ -115,12 +115,12 @@ fn add_flags_to_message(mut data: Vec, version: StateMetadataVersion, encodi buffer } -impl From for StateMetadataDocument { - /// Transforms a [`StardustDocument`] into its state metadata representation by replacing all +impl From for StateMetadataDocument { + /// Transforms a [`IotaDocument`] into its state metadata representation by replacing all /// occurrences of its did with a placeholder. - fn from(document: StardustDocument) -> Self { - let StardustDocument { document, metadata } = document; - let id: StardustDID = document.id().clone(); + fn from(document: IotaDocument) -> Self { + let IotaDocument { document, metadata } = document; + let id: IotaDID = document.id().clone(); let core_document: CoreDocument = document.map( // Replace self-referential identifiers with a placeholder, but not others. |did| { @@ -152,31 +152,31 @@ mod tests { use crate::state_metadata::document::DID_MARKER; use crate::state_metadata::PLACEHOLDER_DID; - use crate::StardustDID; - use crate::StardustDocument; - use crate::StardustService; - use crate::StardustVerificationMethod; + use crate::IotaDID; + use crate::IotaDocument; + use crate::IotaService; + use crate::IotaVerificationMethod; use crate::StateMetadataDocument; use crate::StateMetadataEncoding; use crate::StateMetadataVersion; struct TestSetup { - document: StardustDocument, - did_self: StardustDID, - did_foreign: StardustDID, + document: IotaDocument, + did_self: IotaDID, + did_foreign: IotaDID, } fn test_document() -> TestSetup { let did_self = - StardustDID::parse("did:iota:0x8036235b6b5939435a45d68bcea7890eef399209a669c8c263fac7f5089b2ec6").unwrap(); + IotaDID::parse("did:iota:0x8036235b6b5939435a45d68bcea7890eef399209a669c8c263fac7f5089b2ec6").unwrap(); let did_foreign = - StardustDID::parse("did:iota:0x71b709dff439f1ac9dd2b9c2e28db0807156b378e13bfa3605ce665aa0d0fdca").unwrap(); + IotaDID::parse("did:iota:0x71b709dff439f1ac9dd2b9c2e28db0807156b378e13bfa3605ce665aa0d0fdca").unwrap(); - let mut document: StardustDocument = StardustDocument::new_with_id(did_self.clone()); + let mut document: IotaDocument = IotaDocument::new_with_id(did_self.clone()); let keypair: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); document .insert_method( - StardustVerificationMethod::new(document.id().clone(), keypair.type_(), keypair.public(), "did-self").unwrap(), + IotaVerificationMethod::new(document.id().clone(), keypair.type_(), keypair.public(), "did-self").unwrap(), MethodScope::VerificationMethod, ) .unwrap(); @@ -184,7 +184,7 @@ mod tests { let keypair_foreign: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); document .insert_method( - StardustVerificationMethod::new( + IotaVerificationMethod::new( did_foreign.clone(), keypair_foreign.type_(), keypair_foreign.public(), @@ -196,7 +196,7 @@ mod tests { .unwrap(); assert!(document.insert_service( - StardustService::builder(Object::new()) + IotaService::builder(Object::new()) .id(document.id().to_url().join("#my-service").unwrap()) .type_("RevocationList2022") .service_endpoint(Url::parse("https://example.com/xyzabc").unwrap()) @@ -205,7 +205,7 @@ mod tests { )); assert!(document.insert_service( - StardustService::builder(Object::new()) + IotaService::builder(Object::new()) .id(did_foreign.to_url().join("#my-foreign-service").unwrap()) .type_("RevocationList2022") .service_endpoint(Url::parse("https://example.com/0xf4c42e9da").unwrap()) @@ -288,8 +288,8 @@ mod tests { assert_eq!(controllers.get(0).unwrap(), did_foreign.as_ref()); assert_eq!(controllers.get(1).unwrap(), PLACEHOLDER_DID.as_ref()); - let stardust_document = state_metadata_doc.into_stardust_document(&did_self).unwrap(); - assert_eq!(stardust_document, document); + let iota_document = state_metadata_doc.into_iota_document(&did_self).unwrap(); + assert_eq!(iota_document, document); } #[test] diff --git a/identity_stardust/src/state_metadata/encoding.rs b/identity_iota_core/src/state_metadata/encoding.rs similarity index 100% rename from identity_stardust/src/state_metadata/encoding.rs rename to identity_iota_core/src/state_metadata/encoding.rs diff --git a/identity_stardust/src/state_metadata/mod.rs b/identity_iota_core/src/state_metadata/mod.rs similarity index 100% rename from identity_stardust/src/state_metadata/mod.rs rename to identity_iota_core/src/state_metadata/mod.rs diff --git a/identity_stardust/src/state_metadata/version.rs b/identity_iota_core/src/state_metadata/version.rs similarity index 100% rename from identity_stardust/src/state_metadata/version.rs rename to identity_iota_core/src/state_metadata/version.rs diff --git a/identity_iota_core_legacy/Cargo.toml b/identity_iota_core_legacy/Cargo.toml new file mode 100644 index 0000000000..97472094e9 --- /dev/null +++ b/identity_iota_core_legacy/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "identity_iota_core_legacy" +version = "0.6.0" +authors = ["IOTA Stiftung"] +edition = "2021" +homepage = "https://www.iota.org" +keywords = ["iota", "tangle", "identity", "did"] +license = "Apache-2.0" +publish = false +readme = "./README.md" +repository = "https://github.com/iotaledger/identity.rs" +description = "Core data structures for the IOTA DID Method." + +[dependencies] +bee-message = { version = "0.1.6", default-features = false, features = ["serde"] } +identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } +identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } +lazy_static = { version = "1.4", default-features = false } +serde = { version = "1.0", default-features = false, features = ["std", "derive"] } +strum = { version = "0.24.0", default-features = false, features = ["std", "derive"] } +thiserror = { version = "1.0", default-features = false } + +[dependencies.iota-crypto] +version = "0.12.1" +default-features = false +features = ["blake2b"] + +[package.metadata.docs.rs] +# To build locally: +# RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features --no-deps --workspace --open +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[features] +default = ["revocation-bitmap"] +# Enables revocation with `RevocationBitmap2022`. +revocation-bitmap = ["identity_did/revocation-bitmap"] diff --git a/identity_iota_core_legacy/README.md b/identity_iota_core_legacy/README.md new file mode 100644 index 0000000000..229ae49c25 --- /dev/null +++ b/identity_iota_core_legacy/README.md @@ -0,0 +1,4 @@ +IOTA Identity Core +=== + +This crate provides the core data structures for the [IOTA DID Method Specification](https://wiki.iota.org/identity.rs/specs/did/iota_did_method_spec). diff --git a/identity_iota_core_legacy/src/did/iota_did.rs b/identity_iota_core_legacy/src/did/iota_did.rs new file mode 100644 index 0000000000..9f2ccd6b86 --- /dev/null +++ b/identity_iota_core_legacy/src/did/iota_did.rs @@ -0,0 +1,442 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use core::convert::TryFrom; +use core::fmt::Debug; +use core::fmt::Display; +use core::fmt::Formatter; +use core::str::FromStr; +use std::convert::TryInto; + +use crypto::hashes::blake2b::Blake2b256; +use crypto::hashes::Digest; +use serde; +use serde::Deserialize; +use serde::Serialize; + +use identity_core::common::KeyComparable; +use identity_core::utils::BaseEncoding; +use identity_did::did::BaseDIDUrl; +use identity_did::did::CoreDID; +use identity_did::did::DIDError; +use identity_did::did::DIDUrl; +use identity_did::did::DID; + +use crate::did::Segments; +use crate::error::Error; +use crate::error::Result; +use crate::tangle::Network; +use crate::tangle::NetworkName; +use crate::try_construct_did; + +// The hash size of BLAKE2b-256 (32-bytes) +const BLAKE2B_256_LEN: usize = 32; + +/// A DID URL conforming to the IOTA DID method specification. +/// +/// See [`DIDUrl`]. +pub type IotaDIDUrl = DIDUrl; + +/// A DID conforming to the IOTA DID method specification. +/// +/// This is a thin wrapper around the [`DID`][`CoreDID`] type from the +/// [`identity_did`][`identity_did`] crate. +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)] +#[repr(transparent)] +#[serde(into = "CoreDID", try_from = "CoreDID")] +pub struct IotaDID(CoreDID); + +impl IotaDID { + /// The URL scheme for Decentralized Identifiers. + pub const SCHEME: &'static str = CoreDID::SCHEME; + + /// The IOTA DID method name (`"iota"`). + pub const METHOD: &'static str = "iota"; + + /// The default Tangle network (`"main"`). + pub const DEFAULT_NETWORK: &'static str = "main"; + + /// Converts an owned [`CoreDID`] to an [`IotaDID`]. + /// + /// # Errors + /// + /// Returns `Err` if the input is not a valid [`IotaDID`]. + pub fn try_from_core(did: CoreDID) -> Result { + Self::check_validity(&did)?; + + Ok(Self(Self::normalize(did))) + } + + /// Parses an [`IotaDID`] from the given `input`. + /// + /// # Errors + /// + /// Returns `Err` if the input is not a valid [`IotaDID`]. + pub fn parse(input: impl AsRef) -> Result { + CoreDID::parse(input).map_err(Into::into).and_then(Self::try_from_core) + } + + /// Creates a new [`IotaDID`] with a tag derived from the given `public` key. + /// + /// # Errors + /// + /// Returns `Err` if the input does not form a valid [`IotaDID`]. + pub fn new(public: &[u8]) -> Result { + try_construct_did!(public).map_err(Into::into) + } + + /// Creates a new [`IotaDID`] from the given `public` key and `network`. + /// + /// # Errors + /// + /// Returns `Err` if the input does not form a valid [`IotaDID`] or the `network` is invalid. + /// See [`NetworkName`] for validation requirements. + pub fn new_with_network(public: &[u8], network: impl TryInto) -> Result { + let network_name = network.try_into().map_err(|_| Error::InvalidNetworkName)?; + try_construct_did!(public, network_name.as_ref()).map_err(Into::into) + } + + /// Checks if the given `DID` has a valid IOTA DID `method` (i.e. `"iota"`). + /// + /// # Errors + /// + /// Returns `Err` if the input is not a valid [`IotaDID`]. + pub fn check_method(did: &D) -> Result<()> { + if did.method() != Self::METHOD { + Err(Error::InvalidDID(DIDError::InvalidMethodName)) + } else { + Ok(()) + } + } + + /// Checks if the given `DID` has a valid [`IotaDID`] `method_id`. + /// + /// # Errors + /// + /// Returns `Err` if the input is not a valid [`IotaDID`]. + pub fn check_method_id(did: &D) -> Result<()> { + let segments: Vec<&str> = did.method_id().split(':').collect(); + + if segments.is_empty() || segments.len() > 2 { + return Err(Error::InvalidDID(DIDError::InvalidMethodId)); + } + + // We checked if `id_segments` was empty so this should not panic + let mid: &str = segments.last().unwrap(); + let len: usize = BaseEncoding::decode_base58(mid) + .map_err(|_| Error::InvalidDID(DIDError::InvalidMethodId))? + .len(); + + if len == BLAKE2B_256_LEN { + Ok(()) + } else { + Err(Error::InvalidDID(DIDError::InvalidMethodId)) + } + } + + /// Checks if the given `DID` has a valid [`IotaDID`] network name, e.g. "main", "dev". + /// + /// # Errors + /// + /// Returns `Err` if the input is not a valid [`IotaDID`]. + /// See [`NetworkName`] for validation requirements. + pub fn check_network(did: &D) -> Result<()> { + let network_name = Segments(did.method_id()).network(); + NetworkName::validate_network_name(network_name) + } + + /// Checks if the given `DID` is valid according to the [`IotaDID`] method specification. + /// + /// # Errors + /// + /// Returns `Err` if the input is not a valid [`IotaDID`]. + pub fn check_validity(did: &D) -> Result<()> { + Self::check_method(did)?; + Self::check_method_id(did)?; + Self::check_network(did)?; + + Ok(()) + } + + /// Returns a `bool` indicating if the given `DID` is valid according to the + /// [`IotaDID`] method specification. + pub fn is_valid(did: &CoreDID) -> bool { + Self::check_validity(did).is_ok() + } + + /// Returns the Tangle `network` of the `DID`, if it is valid. + pub fn network(&self) -> Result { + Network::try_from_name(self.network_str().to_owned()) + } + + /// Returns the Tangle `network` name of the `DID`. + pub fn network_str(&self) -> &str { + self.segments().network() + } + + /// Returns the unique Tangle tag of the `DID`. + pub fn tag(&self) -> &str { + self.segments().tag() + } + + #[doc(hidden)] + pub fn segments(&self) -> Segments<'_> { + Segments(self.method_id()) + } + + /// Normalizes the DID `method_id` by removing the default network segment if present. + /// + /// E.g. + /// - `"did:iota:main:123" -> "did:iota:123"` is normalized + /// - `"did:iota:dev:123" -> "did:iota:dev:123"` is unchanged + fn normalize(mut did: CoreDID) -> CoreDID { + let segments: Segments<'_> = Segments(did.method_id()); + + if segments.count() == 2 && segments.network() == Self::DEFAULT_NETWORK { + let method_id: String = segments.tag().to_string(); + did + .set_method_id(method_id) + .expect("this method_id is from a valid did"); + } + + did + } + + // Note: Must be `pub` for the `did` macro. + #[doc(hidden)] + pub fn encode_key(key: &[u8]) -> String { + BaseEncoding::encode_base58(&Blake2b256::digest(key)) + } +} + +impl Display for IotaDID { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + write!(f, "{}", self.0) + } +} + +impl Debug for IotaDID { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + write!(f, "{}", self.0) + } +} + +impl AsRef for IotaDID { + fn as_ref(&self) -> &CoreDID { + &self.0 + } +} + +impl From for CoreDID { + fn from(other: IotaDID) -> Self { + other.0 + } +} + +impl TryFrom for IotaDID { + type Error = Error; + + fn try_from(other: BaseDIDUrl) -> Result { + let core_did: CoreDID = CoreDID::try_from(other)?; + Self::try_from(core_did) + } +} + +impl TryFrom for IotaDID { + type Error = Error; + + fn try_from(other: CoreDID) -> Result { + Self::try_from_core(other) + } +} + +impl FromStr for IotaDID { + type Err = Error; + + fn from_str(string: &str) -> Result { + Self::parse(string) + } +} + +impl TryFrom<&str> for IotaDID { + type Error = Error; + + fn try_from(other: &str) -> Result { + Self::parse(other) + } +} + +impl TryFrom for IotaDID { + type Error = Error; + + fn try_from(other: String) -> Result { + Self::parse(other) + } +} + +impl From for String { + fn from(did: IotaDID) -> Self { + did.into_string() + } +} + +impl KeyComparable for IotaDID { + type Key = CoreDID; + + #[inline] + fn key(&self) -> &Self::Key { + self.as_ref() + } +} + +#[cfg(test)] +mod tests { + use identity_core::crypto::KeyPair; + use identity_core::crypto::KeyType; + use identity_did::did::CoreDID; + use identity_did::did::DID; + + use crate::did::IotaDID; + use crate::did::IotaDIDUrl; + + const TAG: &str = "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV"; + + #[test] + fn test_parse_did_valid() { + assert!(IotaDID::parse(format!("did:iota:{}", TAG)).is_ok()); + assert!(IotaDID::parse(format!("did:iota:main:{}", TAG)).is_ok()); + assert!(IotaDID::parse(format!("did:iota:dev:{}", TAG)).is_ok()); + assert!(IotaDID::parse(format!("did:iota:custom:{}", TAG)).is_ok()); + } + + #[test] + fn test_parse_did_url_valid() { + assert!(IotaDIDUrl::parse(format!("did:iota:{}", TAG)).is_ok()); + assert!(IotaDIDUrl::parse(format!("did:iota:{}#fragment", TAG)).is_ok()); + assert!(IotaDIDUrl::parse(format!("did:iota:{}?somequery=somevalue", TAG)).is_ok()); + assert!(IotaDIDUrl::parse(format!("did:iota:{}?somequery=somevalue#fragment", TAG)).is_ok()); + + assert!(IotaDIDUrl::parse(format!("did:iota:main:{}", TAG)).is_ok()); + assert!(IotaDIDUrl::parse(format!("did:iota:main:{}#fragment", TAG)).is_ok()); + assert!(IotaDIDUrl::parse(format!("did:iota:main:{}?somequery=somevalue", TAG)).is_ok()); + assert!(IotaDIDUrl::parse(format!("did:iota:main:{}?somequery=somevalue#fragment", TAG)).is_ok()); + + assert!(IotaDIDUrl::parse(format!("did:iota:dev:{}", TAG)).is_ok()); + assert!(IotaDIDUrl::parse(format!("did:iota:dev:{}#fragment", TAG)).is_ok()); + assert!(IotaDIDUrl::parse(format!("did:iota:dev:{}?somequery=somevalue", TAG)).is_ok()); + assert!(IotaDIDUrl::parse(format!("did:iota:dev:{}?somequery=somevalue#fragment", TAG)).is_ok()); + + assert!(IotaDIDUrl::parse(format!("did:iota:custom:{}", TAG)).is_ok()); + assert!(IotaDIDUrl::parse(format!("did:iota:custom:{}#fragment", TAG)).is_ok()); + assert!(IotaDIDUrl::parse(format!("did:iota:custom:{}?somequery=somevalue", TAG)).is_ok()); + assert!(IotaDIDUrl::parse(format!("did:iota:custom:{}?somequery=somevalue#fragment", TAG)).is_ok()); + } + + #[test] + fn test_parse_did_invalid() { + // A non-"iota" DID method is invalid. + assert!(IotaDID::parse("did:foo::").is_err()); + // An empty DID method is invalid. + assert!(IotaDID::parse("did:::").is_err()); + assert!(IotaDID::parse(format!("did::main:{}", TAG)).is_err()); + // A non-"iota" DID method is invalid. + assert!(IotaDID::parse("did:iota---::").is_err()); + // An empty `iota-specific-idstring` is invalid. + assert!(IotaDID::parse("did:iota:").is_err()); + // Too many components is invalid. + assert!(IotaDID::parse(format!("did:iota:custom:shard-1:random:{}", TAG)).is_err()); + assert!(IotaDID::parse(format!("did:iota:custom:random:{}", TAG)).is_err()); + // Explicit empty network name is invalid (omitting it is still fine) + assert!(IotaDID::parse(format!("did:iota::{}", TAG)).is_err()); + // Invalid network name is invalid. + assert!(IotaDID::parse(format!("did:iota:Invalid-Network:{}", TAG)).is_err()); + } + + #[test] + fn test_from_did() { + let key: String = IotaDID::encode_key(b"123"); + + let did: CoreDID = format!("did:iota:{}", key).parse().unwrap(); + let iota_did = IotaDID::try_from_core(did).unwrap(); + assert_eq!(iota_did.network_str(), "main"); + assert_eq!(iota_did.tag(), key); + + let did: CoreDID = "did:iota:123".parse().unwrap(); + assert!(IotaDID::try_from_core(did).is_err()); + + let did: CoreDID = format!("did:web:{}", key).parse().unwrap(); + assert!(IotaDID::try_from_core(did).is_err()); + } + + #[test] + fn test_network() { + let key: String = IotaDID::encode_key(b"123"); + + let did: IotaDID = format!("did:iota:{}", key).parse().unwrap(); + assert_eq!(did.network_str(), "main"); + + let did: IotaDID = format!("did:iota:dev:{}", key).parse().unwrap(); + assert_eq!(did.network_str(), "dev"); + + let did: IotaDID = format!("did:iota:test:{}", key).parse().unwrap(); + assert_eq!(did.network_str(), "test"); + + let did: IotaDID = format!("did:iota:custom:{}", key).parse().unwrap(); + assert_eq!(did.network_str(), "custom"); + } + + #[test] + fn test_tag() { + let did: IotaDID = format!("did:iota:{}", TAG).parse().unwrap(); + assert_eq!(did.tag(), TAG); + + let did: IotaDID = format!("did:iota:main:{}", TAG).parse().unwrap(); + assert_eq!(did.tag(), TAG); + } + + #[test] + fn test_new() { + let key: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); + let tag: String = IotaDID::encode_key(key.public().as_ref()); + + let did: IotaDID = IotaDID::new(key.public().as_ref()).unwrap(); + assert_eq!(did.tag(), tag); + assert_eq!(did.network_str(), IotaDID::DEFAULT_NETWORK); + } + + #[test] + fn test_new_with_network() { + let key: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); + let did: IotaDID = IotaDID::new_with_network(key.public().as_ref(), "foo").unwrap(); + let tag: String = IotaDID::encode_key(key.public().as_ref()); + + assert_eq!(did.tag(), tag); + assert_eq!(did.network_str(), "foo"); + } + + #[test] + fn test_normalize() { + let key: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); + let tag: String = IotaDID::encode_key(key.public().as_ref()); + + // An IotaDID with "main" as the network can be normalized ("main" removed) + let did1: IotaDID = format!("did:iota:{}", tag).parse().unwrap(); + let did2: IotaDID = format!("did:iota:main:{}", tag).parse().unwrap(); + assert_eq!(did1, did2); + } + + #[test] + fn test_setter() { + let key: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); + let did: IotaDID = IotaDID::new(key.public().as_ref()).unwrap(); + let mut did_url: IotaDIDUrl = did.into_url(); + + did_url.set_path(Some("/foo")).unwrap(); + did_url.set_query(Some("diff=true")).unwrap(); + did_url.set_fragment(Some("foo")).unwrap(); + + assert_eq!(did_url.path(), Some("/foo")); + assert_eq!(did_url.query(), Some("diff=true")); + assert_eq!(did_url.fragment(), Some("foo")); + } +} diff --git a/identity_iota_core/src/did/macros.rs b/identity_iota_core_legacy/src/did/macros.rs similarity index 91% rename from identity_iota_core/src/did/macros.rs rename to identity_iota_core_legacy/src/did/macros.rs index f2c5b4833e..f5082e506e 100644 --- a/identity_iota_core/src/did/macros.rs +++ b/identity_iota_core_legacy/src/did/macros.rs @@ -11,7 +11,7 @@ /// /// ``` /// # use identity_did::did::DID; -/// # use identity_iota_core::try_construct_did; +/// # use identity_iota_core_legacy::try_construct_did; /// # /// let did = try_construct_did!(b"public-key")?; /// assert_eq!( @@ -24,7 +24,7 @@ /// did.as_str(), /// "did:iota:com:2xQiiGHDq5gCi1H7utY1ni7cf65fTay3G11S4xKp1vkS" /// ); -/// # Ok::<(), identity_iota_core::Error>(()) +/// # Ok::<(), identity_iota_core_legacy::Error>(()) /// ``` #[macro_export] macro_rules! try_construct_did { diff --git a/identity_iota_core_legacy/src/did/mod.rs b/identity_iota_core_legacy/src/did/mod.rs new file mode 100644 index 0000000000..6c8c1a4ff0 --- /dev/null +++ b/identity_iota_core_legacy/src/did/mod.rs @@ -0,0 +1,12 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +//! IOTA DID types. + +pub use self::iota_did::IotaDID; +pub use self::iota_did::IotaDIDUrl; +pub use self::segments::Segments; + +mod iota_did; +mod macros; +mod segments; diff --git a/identity_iota_core/src/did/segments.rs b/identity_iota_core_legacy/src/did/segments.rs similarity index 100% rename from identity_iota_core/src/did/segments.rs rename to identity_iota_core_legacy/src/did/segments.rs diff --git a/identity_iota_core/src/diff/diff_iota_did.rs b/identity_iota_core_legacy/src/diff/diff_iota_did.rs similarity index 100% rename from identity_iota_core/src/diff/diff_iota_did.rs rename to identity_iota_core_legacy/src/diff/diff_iota_did.rs diff --git a/identity_iota_core/src/diff/diff_iota_document.rs b/identity_iota_core_legacy/src/diff/diff_iota_document.rs similarity index 100% rename from identity_iota_core/src/diff/diff_iota_document.rs rename to identity_iota_core_legacy/src/diff/diff_iota_document.rs diff --git a/identity_iota_core/src/diff/diff_iota_document_metadata.rs b/identity_iota_core_legacy/src/diff/diff_iota_document_metadata.rs similarity index 100% rename from identity_iota_core/src/diff/diff_iota_document_metadata.rs rename to identity_iota_core_legacy/src/diff/diff_iota_document_metadata.rs diff --git a/identity_iota_core/src/diff/diff_message.rs b/identity_iota_core_legacy/src/diff/diff_message.rs similarity index 100% rename from identity_iota_core/src/diff/diff_message.rs rename to identity_iota_core_legacy/src/diff/diff_message.rs diff --git a/identity_iota_core/src/diff/mod.rs b/identity_iota_core_legacy/src/diff/mod.rs similarity index 100% rename from identity_iota_core/src/diff/mod.rs rename to identity_iota_core_legacy/src/diff/mod.rs diff --git a/identity_iota_core_legacy/src/document/iota_document.rs b/identity_iota_core_legacy/src/document/iota_document.rs new file mode 100644 index 0000000000..eeb750ac32 --- /dev/null +++ b/identity_iota_core_legacy/src/document/iota_document.rs @@ -0,0 +1,1680 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use core::fmt; +use core::fmt::Debug; +use core::fmt::Display; + +use serde; +use serde::Deserialize; +use serde::Serialize; + +use identity_core::common::Object; +use identity_core::common::OneOrSet; +use identity_core::common::OrderedSet; +use identity_core::common::Url; +use identity_core::convert::FmtJson; +use identity_core::crypto::Ed25519; +use identity_core::crypto::GetSignature; +use identity_core::crypto::GetSignatureMut; +use identity_core::crypto::JcsEd25519; +use identity_core::crypto::KeyPair; +use identity_core::crypto::PrivateKey; +use identity_core::crypto::Proof; +use identity_core::crypto::ProofOptions; +use identity_core::crypto::PublicKey; +use identity_core::crypto::SetSignature; +use identity_core::crypto::Signer; +use identity_did::document::CoreDocument; +use identity_did::document::Document; +use identity_did::service::Service; +use identity_did::utils::DIDUrlQuery; +use identity_did::verifiable::DocumentSigner; +use identity_did::verifiable::VerifierOptions; +use identity_did::verification::MethodRef; +use identity_did::verification::MethodRelationship; +use identity_did::verification::MethodScope; +use identity_did::verification::MethodType; +use identity_did::verification::MethodUriType; +use identity_did::verification::TryMethod; +use identity_did::verification::VerificationMethod; + +use crate::did::IotaDID; +use crate::did::IotaDIDUrl; +use crate::diff::DiffMessage; +use crate::document::IotaDocumentMetadata; +use crate::error::Error; +use crate::error::Result; +use crate::tangle::MessageId; +use crate::tangle::MessageIdExt; +use crate::tangle::NetworkName; + +/// A [`VerificationMethod`] adhering to the IOTA DID method specification. +pub type IotaVerificationMethod = VerificationMethod; + +/// A [`Service`] adhering to the IOTA DID method specification. +pub type IotaService = Service; + +/// A [`CoreDocument`] whose fields adhere to the IOTA DID method specification. +pub type IotaCoreDocument = CoreDocument; + +/// A DID Document adhering to the IOTA DID method specification. +/// +/// This extends [`CoreDocument`]. +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +pub struct IotaDocument { + #[serde(rename = "doc")] + pub(crate) document: IotaCoreDocument, + #[serde(rename = "meta")] + pub metadata: IotaDocumentMetadata, + #[serde(skip_serializing_if = "Option::is_none")] + pub proof: Option, +} + +impl TryMethod for IotaDocument { + const TYPE: MethodUriType = MethodUriType::Absolute; +} + +impl IotaDocument { + // Method types allowed to sign a DID document update. + pub const UPDATE_METHOD_TYPES: &'static [MethodType] = &[MethodType::Ed25519VerificationKey2018]; + pub const DEFAULT_METHOD_FRAGMENT: &'static str = "sign-0"; + + /// Creates a new DID Document from the given [`KeyPair`]. + /// + /// The DID Document will be pre-populated with a single verification method + /// derived from the provided [`KeyPair`] embedded as a capability invocation + /// verification relationship. This method will have the DID URL fragment + /// `#sign-0` and can be easily retrieved with [`IotaDocument::default_signing_method`]. + /// + /// NOTE: the generated document is unsigned, see [`IotaDocument::sign_self`]. + /// + /// Example: + /// + /// ``` + /// # use identity_core::crypto::{KeyPair, KeyType}; + /// # use identity_iota_core_legacy::document::IotaDocument; + /// # + /// // Create a DID Document from a new Ed25519 keypair. + /// let keypair = KeyPair::new(KeyType::Ed25519).unwrap(); + /// let document = IotaDocument::new(&keypair).unwrap(); + /// ``` + pub fn new(keypair: &KeyPair) -> Result { + Self::new_with_options(keypair, None, None) + } + + /// Creates a new DID Document from the given [`KeyPair`], network, and verification method + /// fragment name. + /// + /// See [`IotaDocument::new`]. + /// + /// Arguments: + /// + /// * keypair: the initial verification method is derived from the public key of this [`KeyPair`]. + /// * network: Tangle network to use for the DID; default [`Network::Mainnet`](crate::tangle::Network::Mainnet). + /// * fragment: name of the initial verification method; default + /// [`DEFAULT_METHOD_FRAGMENT`](Self::DEFAULT_METHOD_FRAGMENT). + /// + /// Example: + /// + /// ``` + /// # use identity_core::crypto::KeyPair; + /// # use identity_core::crypto::KeyType; + /// # use identity_iota_core_legacy::document::IotaDocument; + /// # use identity_iota_core_legacy::tangle::Network; + /// # + /// // Create a new DID Document for the devnet from a new Ed25519 keypair. + /// let keypair = KeyPair::new(KeyType::Ed25519).unwrap(); + /// let document = + /// IotaDocument::new_with_options(&keypair, Some(Network::Devnet.name()), Some("auth-key")) + /// .unwrap(); + /// assert_eq!(document.id().network_str(), "dev"); + /// assert_eq!( + /// document + /// .default_signing_method() + /// .unwrap() + /// .id() + /// .fragment() + /// .unwrap(), + /// "auth-key" + /// ); + /// ``` + pub fn new_with_options(keypair: &KeyPair, network: Option, fragment: Option<&str>) -> Result { + let public_key: &PublicKey = keypair.public(); + + let did: IotaDID = if let Some(network_name) = network { + IotaDID::new_with_network(public_key.as_ref(), network_name)? + } else { + IotaDID::new(public_key.as_ref())? + }; + + let method: IotaVerificationMethod = IotaVerificationMethod::new( + did, + keypair.type_(), + keypair.public(), + fragment.unwrap_or(Self::DEFAULT_METHOD_FRAGMENT), + )?; + + Self::from_verification_method(method) + } + + /// Creates a new DID Document from the given [`IotaVerificationMethod`], inserting it as the + /// default capability invocation method. + /// + /// NOTE: the generated document is unsigned, see [`IotaDocument::sign_self`]. + pub fn from_verification_method(method: IotaVerificationMethod) -> Result { + // Ensure the verification method key type is allowed to sign document updates. + if !Self::is_signing_method_type(method.type_()) { + return Err(Error::InvalidDocumentSigningMethodType); + } + + let document: IotaCoreDocument = IotaCoreDocument::builder(Default::default()) + .id(method.id().did().clone()) + .capability_invocation(MethodRef::Embed(method)) + .build()?; + let metadata: IotaDocumentMetadata = IotaDocumentMetadata::new(); + Ok(Self::from((document, metadata, None))) + } + + /// Returns whether the given [`MethodType`] can be used to sign document updates. + pub fn is_signing_method_type(method_type: MethodType) -> bool { + Self::UPDATE_METHOD_TYPES.contains(&method_type) + } + + /// Returns a reference to the underlying [`IotaCoreDocument`]. + pub fn core_document(&self) -> &IotaCoreDocument { + &self.document + } + + /// Returns a mutable reference to the underlying [`IotaCoreDocument`]. + pub fn core_document_mut(&mut self) -> &mut IotaCoreDocument { + &mut self.document + } + + // =========================================================================== + // Properties + // =========================================================================== + + /// Returns the DID document [`id`](IotaDID). + pub fn id(&self) -> &IotaDID { + self.document.id() + } + + /// Returns a reference to the [`IotaDocument`] controllers. + pub fn controller(&self) -> Option<&OneOrSet> { + self.document.controller() + } + + /// Returns a mutable reference to the [`IotaDocument`] controllers. + pub fn controller_mut(&mut self) -> &mut Option> { + self.document.controller_mut() + } + + /// Returns a reference to the [`IotaDocument`] alsoKnownAs set. + pub fn also_known_as(&self) -> &OrderedSet { + self.document.also_known_as() + } + + /// Returns a mutable reference to the [`IotaDocument`] alsoKnownAs set. + pub fn also_known_as_mut(&mut self) -> &mut OrderedSet { + self.document.also_known_as_mut() + } + + /// Returns the first [`IotaVerificationMethod`] with a capability invocation relationship + /// capable of signing this DID document. + pub fn default_signing_method(&self) -> Result<&IotaVerificationMethod> { + self + .core_document() + .capability_invocation() + .head() + .and_then(|method_ref| self.core_document().resolve_method_ref(method_ref)) + .ok_or(Error::MissingSigningKey) + } + + /// Returns a reference to the custom DID Document properties. + pub fn properties(&self) -> &Object { + self.document.properties() + } + + /// Returns a mutable reference to the custom DID Document properties. + pub fn properties_mut(&mut self) -> &mut Object { + self.document.properties_mut() + } + + // =========================================================================== + // Services + // =========================================================================== + + /// Return a set of all [`Service`]s in the document. + pub fn service(&self) -> &OrderedSet { + self.document.service() + } + + /// Add a new [`IotaService`] to the document. + /// + /// Returns `true` if the service was added. + pub fn insert_service(&mut self, service: IotaService) -> bool { + if service.id().fragment().is_none() { + false + } else { + self.document.service_mut().append(service) + } + } + + /// Remove a [`IotaService`] identified by the given [`IotaDIDUrl`] from the document. + /// + /// Returns `true` if a service was removed. + pub fn remove_service(&mut self, did_url: &IotaDIDUrl) -> bool { + self.document.service_mut().remove(did_url) + } + + // =========================================================================== + // Verification Methods + // =========================================================================== + + /// Returns an iterator over all [`IotaVerificationMethods`][IotaVerificationMethod] in the DID Document. + pub fn methods(&self) -> impl Iterator { + self.document.methods() + } + + /// Adds a new [`IotaVerificationMethod`] to the document in the given [`MethodScope`]. + /// + /// # Errors + /// + /// Returns an error if a method with the same fragment already exists. + pub fn insert_method(&mut self, method: IotaVerificationMethod, scope: MethodScope) -> Result<()> { + Ok(self.document.insert_method(method, scope)?) + } + + /// Removes all references to the specified [`VerificationMethod`]. + /// + /// # Errors + /// + /// Returns an error if the method does not exist. + pub fn remove_method(&mut self, did_url: &IotaDIDUrl) -> Result<()> { + Ok(self.document.remove_method(did_url)?) + } + + /// Attaches the relationship to the given method, if the method exists. + /// + /// Note: The method needs to be in the set of verification methods, + /// so it cannot be an embedded one. + pub fn attach_method_relationship(&mut self, did_url: &IotaDIDUrl, relationship: MethodRelationship) -> Result { + Ok(self.document.attach_method_relationship(did_url, relationship)?) + } + + /// Detaches the given relationship from the given method, if the method exists. + pub fn detach_method_relationship(&mut self, did_url: &IotaDIDUrl, relationship: MethodRelationship) -> Result { + Ok(self.document.detach_method_relationship(did_url, relationship)?) + } + + /// Returns the first [`IotaVerificationMethod`] with an `id` property matching the + /// provided `query` and the verification relationship specified by `scope` if present. + pub fn resolve_method<'query, Q>(&self, query: Q, scope: Option) -> Option<&IotaVerificationMethod> + where + Q: Into>, + { + self.document.resolve_method(query, scope) + } + + /// Returns the first [`IotaVerificationMethod`] with an `id` property matching the + /// provided `query` and the verification relationship specified by `scope` if present. + /// + /// WARNING: improper usage of this allows violating the uniqueness of the verification method sets. + pub fn resolve_method_mut<'query, Q>( + &mut self, + query: Q, + scope: Option, + ) -> Option<&mut IotaVerificationMethod> + where + Q: Into>, + { + self.document.resolve_method_mut(query, scope) + } + + /// Attempts to resolve the given method query into a method capable of signing a document update. + pub fn resolve_signing_method<'query, Q>(&self, query: Q) -> Result<&IotaVerificationMethod> + where + Q: Into>, + { + self + .resolve_method(query, Some(MethodScope::capability_invocation())) + .ok_or(Error::InvalidDoc(identity_did::Error::MethodNotFound)) + .and_then(|method| { + if Self::is_signing_method_type(method.type_()) { + Ok(method) + } else { + Err(Error::InvalidDocumentSigningMethodType) + } + }) + } + + // =========================================================================== + // Signatures + // =========================================================================== + + /// Creates a new [`DocumentSigner`] that can be used to create digital signatures + /// from verification methods in this DID Document. + pub fn signer<'base>(&'base self, private_key: &'base PrivateKey) -> DocumentSigner<'base, '_, IotaDID> { + self.document.signer(private_key) + } + + /// Signs the provided `data` with the verification method specified by `method_query`. + /// See [`IotaDocument::signer`] for creating signatures with a builder pattern. + /// + /// NOTE: does not validate whether `private_key` corresponds to the verification method. + /// See [`IotaDocument::verify_data`]. + /// + /// # Errors + /// + /// Fails if an unsupported verification method is used, data + /// serialization fails, or the signature operation fails. + pub fn sign_data<'query, 'this: 'query, X, Q>( + &'this self, + data: &mut X, + private_key: &'this PrivateKey, + method_query: Q, + options: ProofOptions, + ) -> Result<()> + where + X: Serialize + SetSignature + TryMethod, + Q: Into>, + { + self + .signer(private_key) + .method(method_query) + .options(options) + .sign(data) + .map_err(Into::into) + } + + /// Signs this DID document with the verification method specified by `method_query`. + /// The `method_query` may be the full [`IotaDIDUrl`] of the method or just its fragment, + /// e.g. "#sign-0". The signing method must have a capability invocation verification + /// relationship. + /// + /// NOTE: does not validate whether `private_key` corresponds to the verification method. + /// See [`IotaDocument::verify_document`]. + /// + /// # Errors + /// + /// Fails if an unsupported verification method is used or the signature operation fails. + pub fn sign_self<'query, Q>(&mut self, private_key: &PrivateKey, method_query: Q) -> Result<()> + where + Q: Into>, + { + // Ensure method is permitted to sign document updates. + // TODO: re-map this error + let method: &IotaVerificationMethod = self.resolve_signing_method(method_query.into())?; + + // Specify the full method DID Url if the verification method id does not match the document id. + let method_did: &IotaDID = method.id().did(); + let method_id: String = if method_did == self.id() { + method + .id() + .fragment() + .map(|fragment| core::iter::once('#').chain(fragment.chars()).collect()) + .ok_or(Error::DocumentSignError("method missing id fragment", None))? + } else { + method.id().to_string() + }; + + // Sign document. + match method.type_() { + MethodType::Ed25519VerificationKey2018 => { + JcsEd25519::::create_signature(self, method_id, private_key.as_ref(), ProofOptions::default()) + .map_err(|err| Error::DocumentSignError("Ed25519 signature failed", Some(err)))?; + } + MethodType::X25519KeyAgreementKey2019 => { + // X25519 cannot be used to sign documents. + return Err(Error::DocumentSignError( + "X25519KeyAgreementKey2019 cannot sign documents", + None, + )); + } + } + + Ok(()) + } + + // =========================================================================== + // Verification + // =========================================================================== + + /// Verifies the signature of the provided `data` was created using a verification method + /// in this DID Document. + /// + /// # Errors + /// + /// Fails if an unsupported verification method is used, data + /// serialization fails, or the verification operation fails. + pub fn verify_data(&self, data: &X, options: &VerifierOptions) -> Result<()> + where + X: Serialize + GetSignature + ?Sized, + { + self.document.verify_data(data, options).map_err(Into::into) + } + + /// Verifies that the signature on the DID document `signed` was generated by a valid method from + /// this DID document. + /// + /// # Errors + /// + /// Fails if: + /// - The signature proof section is missing in the `signed` document. + /// - The method is not found in this document. + /// - An unsupported verification method is used. + /// - The signature verification operation fails. + pub fn verify_document(&self, signed: &IotaDocument) -> Result<()> { + // Ensure signing method is allowed to sign document updates. + let options = VerifierOptions::default() + .method_scope(MethodScope::capability_invocation()) + .method_type(Self::UPDATE_METHOD_TYPES.to_vec()); + self.verify_data(signed, &options) + } + + /// Verifies whether `document` is a valid root DID document according to the IOTA DID method + /// specification. + /// + /// It must be signed using a verification method with a public key whose BLAKE2b-256 hash matches + /// the DID tag. + pub fn verify_root_document(document: &IotaDocument) -> Result<()> { + // The previous message id must be null. + if !document.metadata.previous_message_id.is_null() { + return Err(Error::InvalidRootDocument("previousMessageId not null")); + } + + // Validate the hash of the public key matches the DID tag. + let signature: &Proof = document + .signature() + .ok_or(Error::InvalidRootDocument("missing signature"))?; + let method: &IotaVerificationMethod = document + .resolve_method(signature, None) + .ok_or(Error::InvalidDoc(identity_did::Error::MethodNotFound))?; + let public: PublicKey = method.data().try_decode()?.into(); + if document.id().tag() != IotaDID::encode_key(public.as_ref()) { + return Err(Error::InvalidRootDocument( + "DID tag does not match any verification method", + )); + } + + // Validate the document is correctly self-signed. + document.verify_document(document) + } + + // =========================================================================== + // Diffs + // =========================================================================== + + /// Creates a `DiffMessage` representing the changes between `self` and `other`. + /// + /// The returned `DiffMessage` will have a digital signature created using the + /// specified `private_key` and `method_query`. + /// + /// NOTE: the method must be a capability invocation method. + /// + /// # Errors + /// + /// Fails if the diff operation or signature operation fails. + #[deprecated(since = "0.5.0", note = "diff chain features are slated for removal")] + pub fn diff<'query, 's: 'query, Q>( + &'query self, + other: &Self, + message_id: MessageId, + private_key: &'query PrivateKey, + method_query: Q, + ) -> Result + where + Q: Into>, + { + let mut diff: DiffMessage = DiffMessage::new(self, other, message_id)?; + + // Ensure the method is allowed to sign document updates. + let method_query: DIDUrlQuery<'_> = method_query.into(); + let _ = self.resolve_signing_method(method_query.clone())?; + + self.sign_data(&mut diff, private_key, method_query, ProofOptions::default())?; + + Ok(diff) + } + + /// Verifies the signature of the `diff` was created using a capability invocation method + /// in this DID Document. + /// + /// # Errors + /// + /// Fails if an unsupported verification method is used or the verification operation fails. + #[deprecated(since = "0.5.0", note = "diff chain features are slated for removal")] + pub fn verify_diff(&self, diff: &DiffMessage) -> Result<()> { + // Ensure signing method is allowed to sign document updates. + let options = VerifierOptions::default() + .method_scope(MethodScope::capability_invocation()) + .method_type(Self::UPDATE_METHOD_TYPES.to_vec()); + self.verify_data(diff, &options).map_err(Into::into) + } + + /// Verifies a [`DiffMessage`] signature and merges the changes into `self`. + /// + /// If merging fails `self` remains unmodified, otherwise `self` represents + /// the merged document state. + /// + /// See [`IotaDocument::verify_diff`]. + /// + /// # Errors + /// + /// Fails if the merge operation or signature operation fails. + #[deprecated(since = "0.5.0", note = "diff chain features are slated for removal")] + pub fn merge_diff(&mut self, diff: &DiffMessage) -> Result<()> { + self.verify_diff(diff)?; + + *self = diff.merge(self)?; + + Ok(()) + } + + // =========================================================================== + // Publishing + // =========================================================================== + + /// Returns the Tangle index of the integration chain for this DID. + /// + /// This is equivalent to the tag segment of the [`IotaDID`]. + /// + /// E.g. + /// For an [`IotaDocument`] `doc` with `"did:iota:1234567890abcdefghijklmnopqrstuvxyzABCDEFGHI"`, + /// `doc.integration_index() == "1234567890abcdefghijklmnopqrstuvxyzABCDEFGHI"` + pub fn integration_index(&self) -> &str { + self.id().tag() + } + + /// Returns the Tangle index of the DID diff chain. This should only be called on messages + /// from documents published on the integration chain. + /// + /// This is the Base58-btc encoded SHA-256 digest of the hex-encoded message id. + #[deprecated(since = "0.5.0", note = "diff chain features are slated for removal")] + pub fn diff_index(message_id: &MessageId) -> Result { + if message_id.is_null() { + return Err(Error::InvalidDocumentMessageId); + } + + Ok(IotaDID::encode_key(message_id.encode_hex().as_bytes())) + } + + /// Returns a vector of all verification methods capable of signing a document update. + pub fn extract_signing_keys(&self) -> Vec> { + self + .core_document() + .capability_invocation() + .iter() + .map(|method_ref| match method_ref { + MethodRef::Embed(method) => Some(method), + MethodRef::Refer(did_url) => self.core_document().resolve_method(did_url, None), + }) + .filter(|method| { + if let Some(method) = method { + IotaDocument::is_signing_method_type(method.type_()) + } else { + true + } + }) + .collect() + } +} + +impl Document for IotaDocument { + type D = IotaDID; + type U = Object; + type V = Object; + + fn id(&self) -> &Self::D { + IotaDocument::id(self) + } + + fn resolve_service<'query, 'me, Q>(&'me self, query: Q) -> Option<&Service> + where + Q: Into>, + { + self.core_document().resolve_service(query) + } + + fn resolve_method<'query, 'me, Q>( + &'me self, + query: Q, + scope: Option, + ) -> Option<&VerificationMethod> + where + Q: Into>, + { + self.core_document().resolve_method(query, scope) + } + + fn verify_data(&self, data: &X, options: &VerifierOptions) -> identity_did::Result<()> + where + X: Serialize + GetSignature + ?Sized, + { + self.core_document().verify_data(data, options) + } +} + +#[cfg(feature = "revocation-bitmap")] +mod iota_document_revocation { + use identity_did::utils::DIDUrlQuery; + + use crate::Error; + use crate::Result; + + use super::IotaDocument; + + impl IotaDocument { + /// If the document has a [`RevocationBitmap`](identity_did::revocation::RevocationBitmap) + /// service identified by `service_query`, revoke all specified `indices`. + pub fn revoke_credentials<'query, 'me, Q>(&mut self, service_query: Q, credential_indices: &[u32]) -> Result<()> + where + Q: Into>, + { + self + .core_document_mut() + .revoke_credentials(service_query, credential_indices) + .map_err(Error::RevocationError) + } + + /// If the document has a [`RevocationBitmap`](identity_did::revocation::RevocationBitmap) + /// service with an id by `service_query`, unrevoke all specified `indices`. + pub fn unrevoke_credentials<'query, 'me, Q>( + &'me mut self, + service_query: Q, + credential_indices: &[u32], + ) -> Result<()> + where + Q: Into>, + { + self + .core_document_mut() + .unrevoke_credentials(service_query, credential_indices) + .map_err(Error::RevocationError) + } + } +} + +impl From<(IotaCoreDocument, IotaDocumentMetadata, Option)> for IotaDocument { + fn from((document, metadata, proof): (IotaCoreDocument, IotaDocumentMetadata, Option)) -> Self { + Self { + document, + metadata, + proof, + } + } +} + +impl From for IotaCoreDocument { + fn from(document: IotaDocument) -> Self { + document.document + } +} + +impl Display for IotaDocument { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.fmt_json(f) + } +} + +impl GetSignature for IotaDocument { + fn signature(&self) -> Option<&Proof> { + self.proof.as_ref() + } +} + +impl GetSignatureMut for IotaDocument { + fn signature_mut(&mut self) -> Option<&mut Proof> { + self.proof.as_mut() + } +} + +impl SetSignature for IotaDocument { + fn set_signature(&mut self, signature: Proof) { + self.proof = Some(signature) + } +} + +// Workaround to enable using this with the credential and presentation validators. +impl AsRef for IotaDocument { + fn as_ref(&self) -> &IotaDocument { + self + } +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use bee_message::MESSAGE_ID_LENGTH; + + use identity_core::common::Object; + use identity_core::common::OneOrSet; + use identity_core::common::Timestamp; + use identity_core::common::Value; + use identity_core::convert::FromJson; + use identity_core::convert::ToJson; + use identity_core::crypto::KeyType; + use identity_core::utils::BaseEncoding; + use identity_did::did::DID; + use identity_did::verifiable::VerifiableProperties; + use identity_did::verification::MethodData; + + use crate::tangle::Network; + + use super::*; + + const DID_ID: &str = "did:iota:HGE4tecHWL2YiZv5qAGtH7gaeQcaz2Z1CR15GWmMjY1M"; + const DID_METHOD_ID: &str = "did:iota:HGE4tecHWL2YiZv5qAGtH7gaeQcaz2Z1CR15GWmMjY1M#sign-0"; + const DID_DEVNET_ID: &str = "did:iota:dev:HGE4tecHWL2YiZv5qAGtH7gaeQcaz2Z1CR15GWmMjY1M"; + const DID_DEVNET_METHOD_ID: &str = "did:iota:dev:HGE4tecHWL2YiZv5qAGtH7gaeQcaz2Z1CR15GWmMjY1M#sign-0"; + + fn valid_did() -> IotaDID { + DID_ID.parse().unwrap() + } + + fn valid_metadata() -> IotaDocumentMetadata { + let mut metadata: IotaDocumentMetadata = IotaDocumentMetadata::new(); + metadata.created = Some(Timestamp::parse("2020-01-02T00:00:00Z").unwrap()); + metadata.updated = Some(Timestamp::parse("2020-01-02T00:00:00Z").unwrap()); + metadata + } + + fn valid_verification_method(controller: &IotaDID, fragment: &str) -> IotaVerificationMethod { + VerificationMethod::builder(Default::default()) + .id(controller.to_url().join(fragment).unwrap()) + .controller(controller.clone()) + .type_(MethodType::Ed25519VerificationKey2018) + .data(MethodData::new_multibase(fragment.as_bytes())) + .build() + .unwrap() + } + + fn valid_iota_document(controller: &IotaDID) -> IotaDocument { + let metadata: IotaDocumentMetadata = valid_metadata(); + + let document: IotaCoreDocument = IotaCoreDocument::builder(Object::default()) + .id(controller.clone()) + .controller(controller.clone()) + .verification_method(valid_verification_method(controller, "#key-1")) + .verification_method(valid_verification_method(controller, "#key-2")) + .verification_method(valid_verification_method(controller, "#key-3")) + .authentication(valid_verification_method(controller, "#auth-key")) + .authentication(controller.to_url().join("#key-3").unwrap()) + .key_agreement(controller.to_url().join("#key-4").unwrap()) + .build() + .unwrap(); + + IotaDocument::from((document, metadata, None)) + } + + fn generate_testkey() -> KeyPair { + let private_key: Vec = vec![ + 40, 185, 109, 70, 134, 119, 123, 37, 190, 254, 232, 186, 106, 48, 213, 63, 133, 223, 167, 126, 159, 43, 178, 4, + 190, 217, 52, 66, 92, 63, 69, 84, + ]; + let public_key: Vec = vec![ + 212, 151, 158, 35, 16, 178, 19, 27, 83, 109, 212, 138, 141, 134, 122, 246, 156, 148, 227, 69, 68, 251, 190, 31, + 25, 101, 230, 20, 130, 188, 121, 196, + ]; + KeyPair::from(( + KeyType::Ed25519, + PublicKey::from(public_key), + PrivateKey::from(private_key), + )) + } + + fn compare_document(document: &IotaDocument) { + assert_eq!(document.id().to_string(), DID_ID); + let default_signing_method: &IotaVerificationMethod = document.default_signing_method().unwrap(); + + assert_eq!(default_signing_method.id().to_string(), DID_METHOD_ID); + assert_eq!( + document.default_signing_method().unwrap().type_(), + MethodType::Ed25519VerificationKey2018 + ); + assert_eq!( + document.default_signing_method().unwrap().data(), + &MethodData::PublicKeyMultibase("zFJsXMk9UqpJf3ZTKnfEQAhvBrVLKMSx9ZeYwQME6c6tT".to_owned()) + ); + } + + fn compare_document_devnet(document: &IotaDocument) { + assert_eq!(document.id().to_string(), DID_DEVNET_ID); + assert_eq!(document.id().network_str(), Network::Devnet.name_str()); + assert_eq!( + document.default_signing_method().unwrap().id().to_string(), + DID_DEVNET_METHOD_ID + ); + assert_eq!( + document.default_signing_method().unwrap().type_(), + MethodType::Ed25519VerificationKey2018 + ); + assert_eq!( + document.default_signing_method().unwrap().data(), + &MethodData::PublicKeyMultibase("zFJsXMk9UqpJf3ZTKnfEQAhvBrVLKMSx9ZeYwQME6c6tT".to_owned()) + ); + } + + #[test] + fn test_new() { + // VALID new() + let keypair: KeyPair = generate_testkey(); + let document: IotaDocument = IotaDocument::new(&keypair).unwrap(); + compare_document(&document); + + // VALID from_verification_method() + let method: IotaVerificationMethod = document.default_signing_method().unwrap().clone(); + let document: IotaDocument = IotaDocument::from_verification_method(method).unwrap(); + compare_document(&document); + } + + #[test] + fn test_new_with_options_network() { + let keypair: KeyPair = generate_testkey(); + let document: IotaDocument = IotaDocument::new_with_options(&keypair, Some(Network::Devnet.name()), None).unwrap(); + compare_document_devnet(&document); + } + + #[test] + fn test_new_with_options_fragment() { + let keypair: KeyPair = generate_testkey(); + let document: IotaDocument = IotaDocument::new_with_options(&keypair, None, Some("test-key")).unwrap(); + assert_eq!( + document.default_signing_method().unwrap().id().fragment().unwrap(), + "test-key" + ); + } + + #[test] + fn test_new_with_options_empty_fragment() { + let keypair: KeyPair = generate_testkey(); + let result: Result = IotaDocument::new_with_options(&keypair, None, Some("")); + assert!(result.is_err()); + } + + #[test] + fn test_no_controller() { + let keypair: KeyPair = generate_testkey(); + let document: IotaDocument = IotaDocument::new(&keypair).unwrap(); + assert_eq!(document.controller(), None); + } + + #[test] + fn test_controller_from_core() { + // One controller. + { + let controller: IotaDID = valid_did(); + let mut document: IotaDocument = valid_iota_document(&controller); + let expected: IotaDID = IotaDID::new(&[0; 32]).unwrap(); + *document.controller_mut() = Some(OneOrSet::new_one(expected.clone())); + assert_eq!(document.controller().unwrap().as_slice(), &[expected]); + // Unset. + *document.controller_mut() = None; + assert!(document.controller().is_none()); + } + + // Many controllers. + { + let controller: IotaDID = valid_did(); + let mut document: IotaDocument = valid_iota_document(&controller); + let expected_controllers: Vec = vec![ + controller, + IotaDID::new(&[0; 32]).unwrap(), + IotaDID::new(&[1; 32]).unwrap(), + IotaDID::new(&[2; 32]).unwrap(), + ]; + *document.controller_mut() = Some(expected_controllers.clone().try_into().unwrap()); + assert_eq!(document.controller().unwrap().as_slice(), &expected_controllers); + // Unset. + *document.controller_mut() = None; + assert!(document.controller().is_none()); + } + } + + #[test] + fn test_methods_new() { + let keypair: KeyPair = generate_testkey(); + let document: IotaDocument = IotaDocument::new(&keypair).unwrap(); + + // An IotaDocument created from a keypair has a single verification method, namely an + // Ed25519 signature. + let expected = IotaVerificationMethod::builder(Default::default()) + .id(DID_METHOD_ID.parse().unwrap()) + .controller(valid_did()) + .type_(MethodType::Ed25519VerificationKey2018) + .data(MethodData::PublicKeyMultibase( + "zFJsXMk9UqpJf3ZTKnfEQAhvBrVLKMSx9ZeYwQME6c6tT".into(), + )) + .build() + .unwrap(); + + let mut methods = document.methods(); + + assert_eq!(methods.next(), Some(expected).as_ref()); + assert_eq!(methods.next(), None); + } + + #[test] + fn test_methods_from_core() { + let controller: IotaDID = valid_did(); + let document: IotaDocument = valid_iota_document(&controller); + let expected: Vec = vec![ + valid_verification_method(&controller, "#key-1"), + valid_verification_method(&controller, "#key-2"), + valid_verification_method(&controller, "#key-3"), + valid_verification_method(&controller, "#auth-key"), + ]; + + let mut methods = document.methods(); + assert_eq!(methods.next(), Some(&expected[0])); + assert_eq!(methods.next(), Some(&expected[1])); + assert_eq!(methods.next(), Some(&expected[2])); + assert_eq!(methods.next(), Some(&expected[3])); + assert_eq!(methods.next(), None); + } + + #[test] + fn test_sign_self() { + let keypair: KeyPair = generate_testkey(); + let mut document: IotaDocument = IotaDocument::new(&keypair).unwrap(); + assert!(document.verify_document(&document).is_err()); + + // Sign with the default capability invocation method. + document + .sign_self( + keypair.private(), + document.default_signing_method().unwrap().id().clone(), + ) + .unwrap(); + assert!(document.verify_document(&document).is_ok()); + } + + #[test] + fn test_sign_self_new_method() { + let keypair: KeyPair = generate_testkey(); + let mut document: IotaDocument = IotaDocument::new(&keypair).unwrap(); + assert!(document.verify_document(&document).is_err()); + + // Add a new capability invocation method directly + let new_keypair: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); + let new_method: IotaVerificationMethod = IotaVerificationMethod::new( + document.id().clone(), + new_keypair.type_(), + new_keypair.public(), + "new_signer", + ) + .unwrap(); + document + .insert_method(new_method, MethodScope::capability_invocation()) + .unwrap(); + + // INVALID - try sign using the wrong private key + document.sign_self(keypair.private(), "#new_signer").unwrap(); + assert!(document.verify_document(&document).is_err()); + + // VALID - Sign with the new capability invocation method private key + document.sign_self(new_keypair.private(), "#new_signer").unwrap(); + assert!(document.verify_document(&document).is_ok()); + } + + #[test] + fn test_sign_self_embedded_controller_method_with_same_fragment() { + let keypair: KeyPair = generate_testkey(); + let mut document: IotaDocument = IotaDocument::new(&keypair).unwrap(); + assert!(document.verify_document(&document).is_err()); + + // Add a new signing method from a controller DID Document with the SAME FRAGMENT + // as the default signing method. + let controller_keypair: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); + let controller_did: IotaDID = IotaDID::new(controller_keypair.public().as_ref()).unwrap(); + let controller_method: IotaVerificationMethod = IotaVerificationMethod::new( + controller_did, + controller_keypair.type_(), + controller_keypair.public(), + IotaDocument::DEFAULT_METHOD_FRAGMENT, + ) + .unwrap(); + document + .insert_method(controller_method.clone(), MethodScope::capability_invocation()) + .unwrap(); + + // VALID - resolving the fragment alone should return the first matching method in the list. + let default_signing_method: &IotaVerificationMethod = document.default_signing_method().unwrap(); + assert_eq!( + document + .resolve_method(IotaDocument::DEFAULT_METHOD_FRAGMENT, None) + .unwrap(), + default_signing_method + ); + // VALID - resolving the entire id should return the exact method. + assert_eq!( + document.resolve_method(default_signing_method.id(), None).unwrap(), + default_signing_method + ); + assert_eq!( + document.resolve_method(controller_method.id(), None).unwrap(), + &controller_method + ); + + // INVALID - sign with the controller's private key referencing only the fragment. + // Fails since both sign_self and verify_document resolve the wrong method. + document + .sign_self(controller_keypair.private(), IotaDocument::DEFAULT_METHOD_FRAGMENT) + .unwrap(); + assert!(document.verify_document(&document).is_err()); + + // VALID - sign with the controller's private key referencing the full DID-Url of the method. + document + .sign_self(controller_keypair.private(), controller_method.id()) + .unwrap(); + assert!(document.verify_document(&document).is_ok()); + } + + #[test] + fn test_sign_self_fails() { + fn generate_document() -> (IotaDocument, KeyPair) { + let keypair: KeyPair = generate_testkey(); + let document: IotaDocument = IotaDocument::new(&keypair).unwrap(); + (document, keypair) + } + + // INVALID - try sign referencing a non-existent verification method. + { + let (mut document, keypair) = generate_document(); + assert!(document.verify_document(&document).is_err()); + assert!(document.sign_self(keypair.private(), "#doesnotexist").is_err()); + assert!(document.verify_document(&document).is_err()); + } + + // INVALID - try sign using a random private key. + { + let (mut document, _) = generate_document(); + let random_keypair: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); + document + .sign_self( + random_keypair.private(), + document.default_signing_method().unwrap().id().clone(), + ) + .unwrap(); + assert!(document.verify_document(&document).is_err()); + } + + // INVALID - try sign using any verification relationship other than capability invocation. + for method_scope in [ + MethodScope::VerificationMethod, + MethodScope::assertion_method(), + MethodScope::capability_delegation(), + MethodScope::authentication(), + MethodScope::key_agreement(), + ] { + let (mut document, _) = generate_document(); + // Add a new method unable to sign the document. + let keypair_new: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); + let method_new: IotaVerificationMethod = IotaVerificationMethod::new( + document.id().clone(), + keypair_new.type_(), + keypair_new.public(), + "new_signer", + ) + .unwrap(); + document.insert_method(method_new, method_scope).unwrap(); + // Try sign the document using the new key. + assert!(document.sign_self(keypair_new.private(), "#new_signer").is_err()); + assert!(document.verify_document(&document).is_err()); + assert!(IotaDocument::verify_root_document(&document).is_err()); + } + + // INVALID - try sign using a X25519 key. + { + let (mut document, _) = generate_document(); + let x25519: KeyPair = KeyPair::new(KeyType::X25519).unwrap(); + let x25519_method = + IotaVerificationMethod::new(document.id().clone(), x25519.type_(), x25519.public(), "kex-0").unwrap(); + document + .insert_method(x25519_method, MethodScope::capability_invocation()) + .unwrap(); + assert!(document.sign_self(x25519.private(), "kex-0").is_err()); + assert!(document.verify_document(&document).is_err()); + } + } + + #[test] + fn test_diff_signing_methods() { + // Ensure only capability invocation methods are allowed to sign a diff. + for scope in [ + MethodScope::assertion_method(), + MethodScope::authentication(), + MethodScope::capability_delegation(), + MethodScope::capability_invocation(), + MethodScope::key_agreement(), + MethodScope::VerificationMethod, + ] { + let key1: KeyPair = generate_testkey(); + let mut doc1: IotaDocument = IotaDocument::new(&key1).unwrap(); + // Add a new verification relationship. + let key2: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); + let method_fragment = format!("{}-1", scope.as_str().to_ascii_lowercase()); + let method_new: IotaVerificationMethod = + IotaVerificationMethod::new(doc1.id().clone(), key2.type_(), key2.public(), method_fragment.as_str()).unwrap(); + assert!(doc1.insert_method(method_new, scope).is_ok()); + assert!(doc1 + .core_document() + .resolve_method(method_fragment.as_str(), Some(scope)) + .is_some()); + + // Add a service to an updated document. + let mut doc2: IotaDocument = doc1.clone(); + let service: IotaService = Service::from_json( + r#"{ + "id":"did:iota:HGE4tecHWL2YiZv5qAGtH7gaeQcaz2Z1CR15GWmMjY1N#linked-domain", + "type": "LinkedDomains", + "serviceEndpoint": "https://bar.example.com" + }"#, + ) + .unwrap(); + doc2.insert_service(service); + + // Try generate and sign a diff using the specified method. + let diff_result = doc1.diff( + &doc2, + MessageId::new([3_u8; 32]), + key2.private(), + method_fragment.as_str(), + ); + if scope == MethodScope::capability_invocation() { + let diff = diff_result.unwrap(); + assert!(doc1.verify_data(&diff, &VerifierOptions::default()).is_ok()); + assert!(doc1.verify_diff(&diff).is_ok()); + } else { + assert!(diff_result.is_err()); + } + } + } + + #[test] + fn test_diff_properties() { + // Ensure custom fields added to properties are retained by diffs. + let key1: KeyPair = generate_testkey(); + let doc1: IotaDocument = IotaDocument::new(&key1).unwrap(); + let message_id: MessageId = MessageId::new([3_u8; 32]); + + // Add a new property on the document. + let doc2 = { + let mut doc2: IotaDocument = doc1.clone(); + doc2.properties_mut().insert("foo".into(), 123.into()); + let diff2: DiffMessage = doc1 + .diff( + &doc2, + message_id, + key1.private(), + doc1.default_signing_method().unwrap().id(), + ) + .unwrap(); + assert!(doc1.verify_diff(&diff2).is_ok()); + assert_eq!( + diff2.merge(&doc1).unwrap().properties().get("foo").unwrap(), + &Value::from(123) + ); + doc2 + }; + + // Mutate a property on the document. + let doc3 = { + let mut doc3: IotaDocument = doc2.clone(); + *doc3.properties_mut().get_mut("foo").unwrap() = 456.into(); + let diff3: DiffMessage = doc2 + .diff( + &doc3, + message_id, + key1.private(), + doc2.default_signing_method().unwrap().id(), + ) + .unwrap(); + assert!(doc2.verify_diff(&diff3).is_ok()); + assert_eq!( + diff3.merge(&doc2).unwrap().properties().get("foo").unwrap(), + &Value::from(456) + ); + doc3 + }; + + // Remove a property on the document. + { + let mut doc4: IotaDocument = doc3.clone(); + assert_eq!(doc4.properties_mut().remove("foo").unwrap(), Value::from(456)); + let diff4: DiffMessage = doc3 + .diff( + &doc4, + message_id, + key1.private(), + doc3.default_signing_method().unwrap().id(), + ) + .unwrap(); + assert!(doc3.verify_diff(&diff4).is_ok()); + assert!(diff4.merge(&doc3).unwrap().properties().get("foo").is_none()); + } + + // Add a new property on the metadata. + let doc5 = { + let mut doc5: IotaDocument = doc1.clone(); + doc5.metadata.properties.insert("bar".into(), 789.into()); + let diff5: DiffMessage = doc1 + .diff( + &doc5, + message_id, + key1.private(), + doc1.default_signing_method().unwrap().id(), + ) + .unwrap(); + assert!(doc1.verify_diff(&diff5).is_ok()); + assert_eq!( + diff5.merge(&doc1).unwrap().metadata.properties.get("bar").unwrap(), + &Value::from(789) + ); + doc5 + }; + + // Mutate a property on the metadata. + let doc6 = { + let mut doc6: IotaDocument = doc5.clone(); + *doc6.metadata.properties.get_mut("bar").unwrap() = "abc".into(); + let diff6: DiffMessage = doc5 + .diff( + &doc6, + message_id, + key1.private(), + doc5.default_signing_method().unwrap().id(), + ) + .unwrap(); + assert!(doc5.verify_diff(&diff6).is_ok()); + assert_eq!( + diff6.merge(&doc5).unwrap().metadata.properties.get("bar").unwrap(), + &Value::from("abc") + ); + doc6 + }; + + // Remove a property on the metadata. + { + let mut doc7: IotaDocument = doc6.clone(); + assert_eq!(doc7.metadata.properties.remove("bar").unwrap(), Value::from("abc")); + let diff7: DiffMessage = doc6 + .diff( + &doc7, + message_id, + key1.private(), + doc6.default_signing_method().unwrap().id(), + ) + .unwrap(); + assert!(doc6.verify_diff(&diff7).is_ok()); + assert!(diff7.merge(&doc6).unwrap().metadata.properties.get("bar").is_none()); + } + } + + #[test] + fn test_verify_data_with_scope() { + fn generate_data() -> VerifiableProperties { + use identity_core::json; + let mut properties: VerifiableProperties = VerifiableProperties::default(); + properties.properties.insert("int_key".to_owned(), json!(1)); + properties.properties.insert("str".to_owned(), json!("some value")); + properties + .properties + .insert("object".to_owned(), json!({ "inner": 42 })); + properties + } + + let key: KeyPair = generate_testkey(); + let mut document: IotaDocument = IotaDocument::new(&key).unwrap(); + + // Try sign using each type of verification relationship. + for scope in [ + MethodScope::assertion_method(), + MethodScope::authentication(), + MethodScope::capability_delegation(), + MethodScope::capability_invocation(), + MethodScope::key_agreement(), + MethodScope::VerificationMethod, + ] { + // Add a new method. + let key_new: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); + let method_fragment = format!("{}-1", scope.as_str().to_ascii_lowercase()); + let method_new: IotaVerificationMethod = IotaVerificationMethod::new( + document.id().clone(), + key_new.type_(), + key_new.public(), + method_fragment.as_str(), + ) + .unwrap(); + document.insert_method(method_new, scope).unwrap(); + + // Sign and verify data. + let mut data = generate_data(); + document + .sign_data( + &mut data, + key_new.private(), + method_fragment.as_str(), + ProofOptions::default(), + ) + .unwrap(); + // Signature should still be valid for every scope. + assert!(document.verify_data(&data, &VerifierOptions::default()).is_ok()); + + // Ensure only the correct scope is valid. + for scope_check in [ + MethodScope::assertion_method(), + MethodScope::authentication(), + MethodScope::capability_delegation(), + MethodScope::capability_invocation(), + MethodScope::key_agreement(), + MethodScope::VerificationMethod, + ] { + let result = document.verify_data(&data, &VerifierOptions::new().method_scope(scope_check)); + // Any other scope should fail validation. + if scope_check == scope { + assert!(result.is_ok()); + } else { + assert!(result.is_err()); + } + } + } + } + + #[test] + fn test_root_document() { + let keypair: KeyPair = generate_testkey(); + let mut document: IotaDocument = IotaDocument::new(&keypair).unwrap(); + assert!(IotaDocument::verify_root_document(&document).is_err()); + + // VALID - root document signed using the default method. + document + .sign_self( + keypair.private(), + document.default_signing_method().unwrap().id().clone(), + ) + .unwrap(); + assert!(document.verify_document(&document).is_ok()); + assert!(IotaDocument::verify_root_document(&document).is_ok()); + } + + #[test] + fn test_root_document_invalid() { + fn generate_root_document() -> (IotaDocument, KeyPair) { + let keypair: KeyPair = generate_testkey(); + (IotaDocument::new(&keypair).unwrap(), keypair) + } + + // INVALID - root document not signed. + { + let (document, _) = generate_root_document(); + assert!(IotaDocument::verify_root_document(&document).is_err()); + } + + // INVALID - root document previousMessageId not null. + { + let (mut document, keypair) = generate_root_document(); + document.metadata.previous_message_id = MessageId::new([3u8; MESSAGE_ID_LENGTH]); + document + .sign_self( + keypair.private(), + document.default_signing_method().unwrap().id().clone(), + ) + .unwrap(); + assert!(document.verify_document(&document).is_ok()); + assert!(IotaDocument::verify_root_document(&document).is_err()); + } + + // INVALID - root document signed with a key not matching the DID tag. + { + let (document, keypair) = generate_root_document(); + // Replace the base58 encoded public key with that of a different key. + let new_keypair: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); + let b58_old = BaseEncoding::encode_base58(keypair.public()); + let b58_new = BaseEncoding::encode_base58(new_keypair.public()); + let doc_json_modified = document.to_string().replace(&b58_old, &b58_new); + // Sign the document using the new key. + let mut new_document: IotaDocument = IotaDocument::from_json(&doc_json_modified).unwrap(); + new_document + .sign_self( + new_keypair.private(), + new_document.default_signing_method().unwrap().id().clone(), + ) + .unwrap(); + assert!(new_document.verify_document(&new_document).is_ok()); + assert!(IotaDocument::verify_root_document(&new_document).is_err()); + } + + // INVALID - root document signed using a different method that does not match the DID tag. + { + let (mut document, _) = generate_root_document(); + // Add a new method able to sign the document. + let keypair_new: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); + let method_new: IotaVerificationMethod = IotaVerificationMethod::new( + document.id().clone(), + keypair_new.type_(), + keypair_new.public(), + "new_signer", + ) + .unwrap(); + document + .insert_method(method_new, MethodScope::capability_invocation()) + .unwrap(); + // Sign the document using the new key. + document.sign_self(keypair_new.private(), "#new_signer").unwrap(); + assert!(document.verify_document(&document).is_ok()); + assert!(IotaDocument::verify_root_document(&document).is_err()); + } + } + + #[test] + fn test_json() { + let keypair: KeyPair = generate_testkey(); + let mut document: IotaDocument = IotaDocument::new(&keypair).unwrap(); + + let json_doc: String = document.to_string(); + let document2: IotaDocument = IotaDocument::from_json(&json_doc).unwrap(); + assert_eq!(document, document2); + + assert!(document + .sign_self( + keypair.private(), + document.default_signing_method().unwrap().id().clone(), + ) + .is_ok()); + + let json_doc: String = document.to_string(); + let document2: IotaDocument = IotaDocument::from_json(&json_doc).unwrap(); + assert_eq!(document, document2); + } + + #[test] + fn test_json_fieldnames() { + let keypair: KeyPair = generate_testkey(); + let document: IotaDocument = IotaDocument::new(&keypair).unwrap(); + let serialization: String = document.to_json().unwrap(); + assert_eq!( + serialization, + format!("{{\"doc\":{},\"meta\":{}}}", document.document, document.metadata) + ); + } + + #[test] + fn test_default_signing_method() { + let keypair: KeyPair = generate_testkey(); + let mut document: IotaDocument = IotaDocument::new(&keypair).unwrap(); + + let signing_method: IotaVerificationMethod = document.default_signing_method().unwrap().clone(); + + // Ensure signing method has an appropriate type. + assert!(IotaDocument::is_signing_method_type(signing_method.type_())); + + // Ensure signing method has a capability invocation relationship. + let capability_invocation: &IotaVerificationMethod = document + .core_document() + .resolve_method(signing_method.id(), Some(MethodScope::capability_invocation())) + .unwrap(); + assert_eq!(&signing_method, capability_invocation); + + // Ensure try_resolve_signing_method resolves it. + assert_eq!( + &signing_method, + document.resolve_signing_method(signing_method.id()).unwrap() + ); + + // Adding a new capability invocation method still returns the original method. + let new_keypair: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); + let new_method: IotaVerificationMethod = IotaVerificationMethod::new( + document.id().clone(), + new_keypair.type_(), + new_keypair.public(), + "new_signer", + ) + .unwrap(); + let new_method_id: IotaDIDUrl = new_method.id().clone(); + document + .insert_method(new_method, MethodScope::capability_invocation()) + .unwrap(); + assert_eq!(document.default_signing_method().unwrap().id(), signing_method.id()); + + // Removing the original signing method returns the next one. + document + .remove_method( + &document + .id() + .to_url() + .join(format!("#{}", IotaDocument::DEFAULT_METHOD_FRAGMENT)) + .unwrap(), + ) + .unwrap(); + assert_eq!(document.default_signing_method().unwrap().id(), &new_method_id); + + // Removing the last signing method causes an error. + document.remove_method(&new_method_id).unwrap(); + assert!(matches!( + document.default_signing_method(), + Err(Error::MissingSigningKey) + )); + } + + #[test] + fn test_document_services() { + let keypair: KeyPair = generate_testkey(); + let mut document: IotaDocument = IotaDocument::new(&keypair).unwrap(); + let service: IotaService = Service::from_json( + r#"{ + "id":"did:iota:HGE4tecHWL2YiZv5qAGtH7gaeQcaz2Z1CR15GWmMjY1N#linked-domain", + "type": "LinkedDomains", + "serviceEndpoint": "https://bar.example.com" + }"#, + ) + .unwrap(); + document.insert_service(service); + + assert_eq!(1, document.service().len()); + + assert!(document.remove_service( + &IotaDIDUrl::parse("did:iota:HGE4tecHWL2YiZv5qAGtH7gaeQcaz2Z1CR15GWmMjY1N#linked-domain").unwrap(), + )); + assert_eq!(0, document.service().len()); + } + + #[test] + fn test_relative_method_uri() { + let keypair: KeyPair = generate_testkey(); + let mut document: IotaDocument = IotaDocument::new(&keypair).unwrap(); + + assert!(document.signature().is_none()); + assert!(document + .sign_self( + keypair.private(), + document.default_signing_method().unwrap().id().clone(), + ) + .is_ok()); + + assert_eq!(document.signature().unwrap().verification_method(), "#sign-0"); + } + + #[test] + fn test_integration_index() { + let keypair: KeyPair = generate_testkey(); + let document: IotaDocument = IotaDocument::new(&keypair).unwrap(); + + // The integration chain index should just be the tag of the DID + let tag = document.id().tag(); + assert_eq!(document.integration_index(), tag); + } + + #[test] + fn test_diff_index() { + let message_id = MessageId::from_str("c38d6c541f98f780ddca6ad648ff0e073cd86c4dee248149c2de789d84d42132").unwrap(); + let diff_index = IotaDocument::diff_index(&message_id).expect("failed to generate diff_index"); + assert_eq!(diff_index, "2g45GsCAmkvQfcrHGUgqwQJLbYY3Gic8f23wf71sGGGP"); + } + + #[test] + fn test_new_document_verification_relationships() { + let keypair: KeyPair = generate_testkey(); + let document: IotaDocument = IotaDocument::new(&keypair).unwrap(); + let verification_method: &IotaVerificationMethod = document.resolve_method("#sign-0", None).unwrap(); + let expected_did_url: IotaDIDUrl = document.id().to_url().join("#sign-0").unwrap(); + + // Ensure capability invocation relationship. + let capability_invocation_method_id: &IotaDIDUrl = + document.core_document().capability_invocation().first().unwrap().id(); + assert_eq!(verification_method.id(), &expected_did_url); + assert_eq!(capability_invocation_method_id, &expected_did_url); + + // Ensure fragment of the capability invocation method reference is `authentication` + match document + .core_document() + .capability_invocation() + .first() + .unwrap() + .clone() + { + MethodRef::Refer(_) => panic!("capability invocation method should be embedded"), + MethodRef::Embed(method) => assert_eq!(method.id(), capability_invocation_method_id), + } + + // `methods` returns all embedded verification methods, so only one is expected. + assert_eq!(document.methods().count(), 1); + } + + #[test] + fn test_document_equality() { + let keypair1: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); + let original_doc: IotaDocument = IotaDocument::new_with_options(&keypair1, None, Some("test-0")).unwrap(); + + let mut doc1 = original_doc.clone(); + + // Update the key material of the existing verification method test-0. + let keypair2: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); + let method2: IotaVerificationMethod = + IotaVerificationMethod::new(doc1.id().to_owned(), keypair2.type_(), keypair2.public(), "test-0").unwrap(); + + doc1 + .remove_method(&doc1.id().to_url().join("#test-0").unwrap()) + .unwrap(); + doc1 + .insert_method(method2, MethodScope::capability_invocation()) + .unwrap(); + + // Even though the method fragment is the same, the key material has been updated + // so the two documents are expected to not be equal. + assert_ne!(original_doc, doc1); + + let mut doc2 = doc1.clone(); + let keypair3: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); + let method3: IotaVerificationMethod = + IotaVerificationMethod::new(doc1.id().to_owned(), keypair3.type_(), keypair3.public(), "test-0").unwrap(); + + let insertion_result = doc2.insert_method(method3, MethodScope::capability_invocation()); + + // Nothing was inserted, because a method with the same fragment already existed. + assert!(insertion_result.is_err()); + assert_eq!(doc1, doc2); + } +} diff --git a/identity_stardust/src/document/stardust_document_metadata.rs b/identity_iota_core_legacy/src/document/iota_document_metadata.rs similarity index 55% rename from identity_stardust/src/document/stardust_document_metadata.rs rename to identity_iota_core_legacy/src/document/iota_document_metadata.rs index 52eac6e6c1..8d4b466e5c 100644 --- a/identity_stardust/src/document/stardust_document_metadata.rs +++ b/identity_iota_core_legacy/src/document/iota_document_metadata.rs @@ -4,49 +4,57 @@ use core::fmt::Debug; use core::fmt::Display; use core::fmt::Formatter; +use core::fmt::Result as FmtResult; use identity_core::common::Object; use identity_core::common::Timestamp; use identity_core::convert::FmtJson; + use serde::Deserialize; use serde::Serialize; -/// Additional attributes related to a [`StardustDocument`][crate::StardustDocument]. +use crate::tangle::MessageId; +use crate::tangle::MessageIdExt; + +/// Additional attributes related to an IOTA DID Document. #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] -pub struct StardustDocumentMetadata { - // TODO: store created in the immutable metadata, if possible? +pub struct IotaDocumentMetadata { #[serde(skip_serializing_if = "Option::is_none")] pub created: Option, #[serde(skip_serializing_if = "Option::is_none")] pub updated: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub deactivated: Option, + #[serde( + rename = "previousMessageId", + default = "MessageId::null", + skip_serializing_if = "MessageId::is_null" + )] + pub previous_message_id: MessageId, #[serde(flatten)] pub properties: Object, } -impl StardustDocumentMetadata { - /// Creates a new `StardustDocumentMetadata` with the current system datetime used for `created` - /// and `updated` timestamps. +impl IotaDocumentMetadata { + /// Creates a new `IotaDocumentMetadata` with the current system datetime used for `created` and + /// `updated` timestamps. pub fn new() -> Self { let now: Timestamp = Timestamp::now_utc(); Self { created: Some(now), updated: Some(now), - deactivated: None, + previous_message_id: MessageId::null(), properties: Object::default(), } } } -impl Default for StardustDocumentMetadata { +impl Default for IotaDocumentMetadata { fn default() -> Self { Self::new() } } -impl Display for StardustDocumentMetadata { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { +impl Display for IotaDocumentMetadata { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { self.fmt_json(f) } } diff --git a/identity_iota_core_legacy/src/document/mod.rs b/identity_iota_core_legacy/src/document/mod.rs new file mode 100644 index 0000000000..f3e2584234 --- /dev/null +++ b/identity_iota_core_legacy/src/document/mod.rs @@ -0,0 +1,13 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +//! IOTA DID Document types and wrappers. + +pub use self::iota_document::IotaCoreDocument; +pub use self::iota_document::IotaDocument; +pub use self::iota_document::IotaService; +pub use self::iota_document::IotaVerificationMethod; +pub use self::iota_document_metadata::IotaDocumentMetadata; + +mod iota_document; +mod iota_document_metadata; diff --git a/identity_iota_core_legacy/src/error.rs b/identity_iota_core_legacy/src/error.rs new file mode 100644 index 0000000000..7a9c8e4cff --- /dev/null +++ b/identity_iota_core_legacy/src/error.rs @@ -0,0 +1,32 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +pub type Result = core::result::Result; + +#[derive(Debug, thiserror::Error, strum::IntoStaticStr)] +pub enum Error { + #[deprecated(since = "0.5.0", note = "diff chain features are slated for removal")] + #[error("{0}")] + DiffError(#[from] identity_core::diff::Error), + #[error("{0}")] + InvalidDID(#[from] identity_did::did::DIDError), + #[error("{0}")] + InvalidDoc(#[from] identity_did::Error), + #[error("Invalid Message: {0}")] + InvalidMessage(#[from] bee_message::Error), + + #[error("signing failed: {0}")] + DocumentSignError(&'static str, #[source] Option), + #[error("Invalid Document - Missing Message Id")] + InvalidDocumentMessageId, + #[error("Invalid Document - Signing Verification Method Type Not Supported")] + InvalidDocumentSigningMethodType, + #[error("Invalid Network Name")] + InvalidNetworkName, + #[error("invalid root document: {0}")] + InvalidRootDocument(&'static str), + #[error("Missing Signing Key")] + MissingSigningKey, + #[error("credential revocation error")] + RevocationError(#[source] identity_did::Error), +} diff --git a/identity_iota_core_legacy/src/lib.rs b/identity_iota_core_legacy/src/lib.rs new file mode 100644 index 0000000000..45b3280037 --- /dev/null +++ b/identity_iota_core_legacy/src/lib.rs @@ -0,0 +1,30 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +#![forbid(unsafe_code)] +#![allow(deprecated)] +#![doc = include_str!("./../README.md")] +#![allow(clippy::upper_case_acronyms)] +#![warn( + rust_2018_idioms, + unreachable_pub, + // missing_docs, + rustdoc::missing_crate_level_docs, + rustdoc::broken_intra_doc_links, + rustdoc::private_intra_doc_links, + rustdoc::private_doc_tests, + clippy::missing_safety_doc, + // clippy::missing_errors_doc, +)] + +pub use self::error::Error; +pub use self::error::Result; + +#[deprecated(since = "0.5.0", note = "diff chain features are slated for removal")] +pub mod diff; + +pub mod did; +pub mod document; +pub mod tangle; + +mod error; diff --git a/identity_iota_core/src/tangle/message_id.rs b/identity_iota_core_legacy/src/tangle/message_id.rs similarity index 100% rename from identity_iota_core/src/tangle/message_id.rs rename to identity_iota_core_legacy/src/tangle/message_id.rs diff --git a/identity_iota_core/src/tangle/mod.rs b/identity_iota_core_legacy/src/tangle/mod.rs similarity index 100% rename from identity_iota_core/src/tangle/mod.rs rename to identity_iota_core_legacy/src/tangle/mod.rs diff --git a/identity_iota_core/src/tangle/network.rs b/identity_iota_core_legacy/src/tangle/network.rs similarity index 100% rename from identity_iota_core/src/tangle/network.rs rename to identity_iota_core_legacy/src/tangle/network.rs diff --git a/identity_resolver/Cargo.toml b/identity_resolver/Cargo.toml index ca8f1ca365..3fdcfa4769 100644 --- a/identity_resolver/Cargo.toml +++ b/identity_resolver/Cargo.toml @@ -22,9 +22,9 @@ serde = { version = "1.0", default-features = false, features = ["std", "derive" strum = { version = "0.21", features = ["derive"] } thiserror = { version = "1.0", default-features = false } -[dependencies.identity_stardust] +[dependencies.identity_iota_core] version = "=0.6.0" -path = "../identity_stardust" +path = "../identity_iota_core" default-features = false features = ["send-sync-client-ext", "iota-client"] optional = true @@ -35,6 +35,6 @@ tokio = { version = "1.17.0", default-features = false, features = ["rt-multi-th [features] default = ["revocation-bitmap", "iota"] -revocation-bitmap = ["identity_did/revocation-bitmap", "identity_stardust?/revocation-bitmap"] +revocation-bitmap = ["identity_did/revocation-bitmap", "identity_iota_core?/revocation-bitmap"] # Enables the IOTA integration for the resolver. -iota = ["dep:identity_stardust"] +iota = ["dep:identity_iota_core"] diff --git a/identity_resolver/src/resolution/resolver.rs b/identity_resolver/src/resolution/resolver.rs index 12fb9928fd..937e1c4b57 100644 --- a/identity_resolver/src/resolution/resolver.rs +++ b/identity_resolver/src/resolution/resolver.rs @@ -397,31 +397,31 @@ impl Resolver> mod iota_handler { use super::Resolver; use identity_credential::validator::ValidatorDocument; - use identity_stardust::StardustClientExt; - use identity_stardust::StardustDID; - use identity_stardust::StardustDocument; - use identity_stardust::StardustIdentityClientExt; + use identity_iota_core::IotaClientExt; + use identity_iota_core::IotaDID; + use identity_iota_core::IotaDocument; + use identity_iota_core::IotaIdentityClientExt; use std::sync::Arc; impl Resolver where - DOC: From + ValidatorDocument + 'static, + DOC: From + ValidatorDocument + 'static, { /// Convenience method for attaching a new handler responsible for resolving IOTA DIDs. /// /// See also [`attach_handler`](Self::attach_handler). pub fn attach_iota_handler(&mut self, client: CLI) where - CLI: StardustClientExt + Send + Sync + 'static, + CLI: IotaClientExt + Send + Sync + 'static, { let arc_client: Arc = Arc::new(client); - let handler = move |did: StardustDID| { + let handler = move |did: IotaDID| { let future_client = arc_client.clone(); async move { future_client.resolve_did(&did).await } }; - self.attach_handler(StardustDID::METHOD.to_owned(), handler); + self.attach_handler(IotaDID::METHOD.to_owned(), handler); } } } diff --git a/identity_resolver/src/resolution/tests/presentation_validation_errors.rs b/identity_resolver/src/resolution/tests/presentation_validation_errors.rs index a5ed876530..4bfecf0ad9 100644 --- a/identity_resolver/src/resolution/tests/presentation_validation_errors.rs +++ b/identity_resolver/src/resolution/tests/presentation_validation_errors.rs @@ -10,8 +10,8 @@ use identity_credential::validator::PresentationValidationOptions; use identity_credential::validator::ValidatorDocument; use identity_did::did::CoreDID; use identity_did::document::CoreDocument; -use identity_stardust::StardustDID; -use identity_stardust::StardustDocument; +use identity_iota_core::IotaDID; +use identity_iota_core::IotaDocument; use super::valid_presentation_data::HOLDER_FOO_DOC_JSON; use super::valid_presentation_data::ISSUER_BAR_DOC_JSON; @@ -19,12 +19,12 @@ use super::valid_presentation_data::ISSUER_IOTA_DOC_JSON; use super::valid_presentation_data::PRESENTATION_JSON; type DynamicError = Box; -async fn misconfigured_iota_resolver(_did: StardustDID) -> Result { +async fn misconfigured_iota_resolver(_did: IotaDID) -> Result { Ok(CoreDocument::from_json(HOLDER_FOO_DOC_JSON).unwrap()) } -async fn misconfigured_bar_resolver(_did: CoreDID) -> Result { - Ok(StardustDocument::from_json(ISSUER_IOTA_DOC_JSON).unwrap()) +async fn misconfigured_bar_resolver(_did: CoreDID) -> Result { + Ok(IotaDocument::from_json(ISSUER_IOTA_DOC_JSON).unwrap()) } async fn misconfigured_foo_resolver(_did: CoreDID) -> Result { @@ -34,9 +34,9 @@ async fn misconfigured_foo_resolver(_did: CoreDID) -> Result(mut resolver: Resolver) where - DOC: ValidatorDocument + From + From + Send + Sync, + DOC: ValidatorDocument + From + From + Send + Sync, { - let correct_iota_issuer: StardustDocument = StardustDocument::from_json(ISSUER_IOTA_DOC_JSON).unwrap(); + let correct_iota_issuer: IotaDocument = IotaDocument::from_json(ISSUER_IOTA_DOC_JSON).unwrap(); let correct_bar_issuer: CoreDocument = CoreDocument::from_json(ISSUER_BAR_DOC_JSON).unwrap(); let correct_issuers: [DOC; 2] = [correct_bar_issuer.into(), correct_iota_issuer.into()]; let correct_holder: DOC = CoreDocument::from_json(HOLDER_FOO_DOC_JSON).unwrap().into(); diff --git a/identity_resolver/src/resolution/tests/successful_presentation_validation.rs b/identity_resolver/src/resolution/tests/successful_presentation_validation.rs index 8110f1efdd..80c70c23d9 100644 --- a/identity_resolver/src/resolution/tests/successful_presentation_validation.rs +++ b/identity_resolver/src/resolution/tests/successful_presentation_validation.rs @@ -12,8 +12,8 @@ use identity_did::did::DID; use identity_did::document::CoreDocument; use identity_did::document::Document; use identity_did::verifiable::VerifierOptions; -use identity_stardust::StardustDID; -use identity_stardust::StardustDocument; +use identity_iota_core::IotaDID; +use identity_iota_core::IotaDocument; use serde::de::DeserializeOwned; use crate::Resolver; @@ -42,7 +42,7 @@ async fn resolve_foo(did: CoreDID) -> Result { resolve(did, HOLDER_FOO_DOC_JSON).await } -async fn resolve_iota(did: StardustDID) -> Result { +async fn resolve_iota(did: IotaDID) -> Result { resolve(did, ISSUER_IOTA_DOC_JSON).await } @@ -52,11 +52,11 @@ async fn resolve_bar(did: CoreDID) -> Result { async fn check_verify_presentation(mut resolver: Resolver) where - DOC: ValidatorDocument + From + From + Send + Sync, + DOC: ValidatorDocument + From + From + Send + Sync, { let presentation: Presentation = Presentation::from_json(PRESENTATION_JSON).unwrap(); - resolver.attach_handler(StardustDID::METHOD.to_owned(), resolve_iota); + resolver.attach_handler(IotaDID::METHOD.to_owned(), resolve_iota); resolver.attach_handler("foo".to_owned(), resolve_foo); resolver.attach_handler("bar".to_owned(), resolve_bar); diff --git a/identity_stardust/Cargo.toml b/identity_stardust/Cargo.toml deleted file mode 100644 index 66a591f731..0000000000 --- a/identity_stardust/Cargo.toml +++ /dev/null @@ -1,53 +0,0 @@ -[package] -name = "identity_stardust" -version = "0.6.0" -authors = ["IOTA Stiftung"] -edition = "2021" -homepage = "https://www.iota.org" -keywords = ["iota", "tangle", "stardust", "identity"] -license = "Apache-2.0" -readme = "../README.md" -repository = "https://github.com/iotaledger/identity.rs" -rust-version = "1.62" -description = "An IOTA Ledger integration for the identity.rs library." - -[dependencies] -# Ensure bee-block always matches the version used by iota-client. -bee-block = { version = "1.0.0-beta.7", default-features = false, features = ["std"], optional = true } -identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } -identity_credential = { version = "=0.6.0", path = "../identity_credential", default-features = false, features = ["validator"] } -identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } - -async-trait = { version = "0.1.56", default-features = false, optional = true } -futures = { version = "0.3" } -iota-client = { version = "2.0.0-beta.3", default-features = false, features = ["tls"], optional = true } -num-derive = { version = "0.3", default-features = false } -num-traits = { version = "0.2", default-features = false, features = ["std"] } -once_cell = { version = "1", default-features = false, features = ["std"] } -prefix-hex = { version = "0.4", default-features = false } -serde = { version = "1.0", default-features = false, features = ["std", "derive"] } -strum = { version = "0.21", features = ["derive"] } -thiserror = { version = "1.0", default-features = false } - -[dev-dependencies] -anyhow = { version = "1.0.57" } -iota-crypto = { version = "0.12.1", default-features = false, features = ["bip39", "bip39-en"] } -proptest = { version = "1.0.0", default-features = false, features = ["std"] } -tokio = { version = "1.17.0", default-features = false, features = ["rt-multi-thread", "macros"] } - -[package.metadata.docs.rs] -# To build locally: -# RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features --no-deps --workspace --open -all-features = true -rustdoc-args = ["--cfg", "docsrs"] - -[features] -default = ["client", "iota-client", "revocation-bitmap", "send-sync-client-ext"] -# Exposes the `StardustIdentityClient` and `StardustIdentityClientExt` traits. -client = ["dep:async-trait", "dep:bee-block"] -# Enables the iota-client dependency, the client trait implementations for it, and the `StardustClientExt` trait. -iota-client = ["dep:iota-client", "client"] -# Enables revocation with `RevocationBitmap2022`. -revocation-bitmap = ["identity_did/revocation-bitmap"] -# Adds Send bounds on the futures produces by the client extension traits. -send-sync-client-ext = [] diff --git a/identity_stardust/README.md b/identity_stardust/README.md deleted file mode 100644 index 67eac9593c..0000000000 --- a/identity_stardust/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# IOTA Stardust Identity Library - -This is a work-in-progress intended to replace the `did:iota` DID Method. - -`cargo run --example create_did` \ No newline at end of file diff --git a/identity_stardust/src/did/mod.rs b/identity_stardust/src/did/mod.rs deleted file mode 100644 index 1c1aed99df..0000000000 --- a/identity_stardust/src/did/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -pub use stardust_did::StardustDID; -pub use stardust_did::StardustDIDUrl; - -mod stardust_did; diff --git a/identity_stardust/src/did/stardust_did.rs b/identity_stardust/src/did/stardust_did.rs deleted file mode 100644 index 0e82b45fa8..0000000000 --- a/identity_stardust/src/did/stardust_did.rs +++ /dev/null @@ -1,859 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use core::convert::TryFrom; -use core::fmt::Debug; -use core::fmt::Display; -use core::fmt::Formatter; -use core::str::FromStr; - -use identity_core::common::KeyComparable; -use identity_did::did::BaseDIDUrl; -use identity_did::did::CoreDID; -use identity_did::did::DIDError; -use identity_did::did::DIDUrl; -use identity_did::did::DID; -use serde::Deserialize; -use serde::Serialize; - -use crate::NetworkName; - -pub type Result = std::result::Result; - -/// A DID URL conforming to the IOTA UTXO DID method specification. -/// -/// See [`DIDUrl`]. -pub type StardustDIDUrl = DIDUrl; - -/// A DID conforming to the IOTA UTXO DID method specification. -/// -/// This is a thin wrapper around the [`DID`][`CoreDID`] type from the -/// [`identity_did`][`identity_did`] crate. -#[derive(Clone, Hash, Debug, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)] -#[repr(transparent)] -#[serde(into = "CoreDID", try_from = "CoreDID")] -pub struct StardustDID(CoreDID); - -impl StardustDID { - /// The URL scheme for Decentralized Identifiers. - pub const SCHEME: &'static str = CoreDID::SCHEME; - - /// The IOTA UTXO DID method name (`"iota"`). - pub const METHOD: &'static str = "iota"; - - /// The default Tangle network (`"main"`). - pub const DEFAULT_NETWORK: &'static str = "main"; - - // The length of an Alias ID, which is a BLAKE2b-256 hash (32-bytes). - pub(crate) const TAG_BYTES_LEN: usize = 32; - - // =========================================================================== - // Constructors - // =========================================================================== - - /// Constructs a new [`StardustDID`] from a byte representation of the tag and the given - /// network name. - /// - /// See also [`StardustDID::placeholder`]. - /// - /// # Example - /// - /// ``` - /// # use identity_did::did::DID; - /// # use identity_stardust::NetworkName; - /// # use identity_stardust::StardustDID; - /// # - /// let did = StardustDID::new(&[1;32], &NetworkName::try_from("smr").unwrap()); - /// assert_eq!(did.as_str(), "did:iota:smr:0x0101010101010101010101010101010101010101010101010101010101010101"); - pub fn new(bytes: &[u8; 32], network_name: &NetworkName) -> Self { - let tag = prefix_hex::encode(bytes); - let did: String = format!("did:{}:{}:{}", Self::METHOD, network_name, tag); - - Self::parse(did).expect("DIDs constructed with new should be valid") - } - - /// Creates a new placeholder [`StardustDID`] with the given network name. - /// - /// # Example - /// - /// ``` - /// # use identity_did::did::DID; - /// # use identity_stardust::NetworkName; - /// # use identity_stardust::StardustDID; - /// # - /// let placeholder = StardustDID::placeholder(&NetworkName::try_from("smr").unwrap()); - /// assert_eq!(placeholder.as_str(), "did:iota:smr:0x0000000000000000000000000000000000000000000000000000000000000000"); - pub fn placeholder(network_name: &NetworkName) -> Self { - Self::new(&[0; 32], network_name) - } - - /// Parses an [`StardustDID`] from the given `input`. - /// - /// # Errors - /// - /// Returns `Err` if the input does not conform to the [`StardustDID`] specification. - pub fn parse(input: impl AsRef) -> Result { - CoreDID::parse(input).and_then(Self::try_from_core) - } - - /// Converts a [`CoreDID`] to a [`StardustDID`]. - /// - /// # Errors - /// - /// Returns `Err` if the input does not conform to the [`StardustDID`] specification. - pub fn try_from_core(did: CoreDID) -> Result { - Self::check_validity(&did)?; - - Ok(Self(Self::normalize(did))) - } - - // =========================================================================== - // Properties - // =========================================================================== - - /// Returns the IOTA `network` name of the `DID`. - pub fn network_str(&self) -> &str { - Self::denormalized_components(self.method_id()).0 - } - - /// Returns the tag of the `DID`, which is a hex-encoded Alias ID. - pub fn tag(&self) -> &str { - Self::denormalized_components(self.method_id()).1 - } - - // =========================================================================== - // Validation - // =========================================================================== - - /// Checks if the given `DID` is syntactically valid according to the [`StardustDID`] method specification. - /// - /// # Errors - /// - /// Returns `Err` if the input is not a syntactically valid [`StardustDID`]. - pub fn check_validity(did: &D) -> Result<()> { - Self::check_method(did) - .and_then(|_| Self::check_tag(did)) - .and_then(|_| Self::check_network(did)) - } - - /// Returns a `bool` indicating if the given `DID` is valid according to the - /// [`StardustDID`] method specification. - /// - /// Equivalent to `StardustDID::check_validity(did).is_ok()`. - pub fn is_valid(did: &CoreDID) -> bool { - Self::check_validity(did).is_ok() - } - - // =========================================================================== - // Helpers - // =========================================================================== - - /// Checks if the given `DID` has a valid [`StardustDID`] `method` (i.e. `"iota"`). - /// - /// # Errors - /// - /// Returns `Err` if the input represents another method. - fn check_method(did: &D) -> Result<()> { - (did.method() == Self::METHOD) - .then_some(()) - .ok_or(DIDError::InvalidMethodName) - } - - /// Checks if the given `DID` has a valid [`StardustDID`] `method_id`. - /// - /// # Errors - /// - /// Returns `Err` if the input does not have a [`StardustDID`] compliant method id. - fn check_tag(did: &D) -> Result<()> { - let (_, tag) = Self::denormalized_components(did.method_id()); - - // Implicitly catches if there are too many segments (:) in the DID too. - prefix_hex::decode::<[u8; Self::TAG_BYTES_LEN]>(tag) - .map_err(|_| DIDError::InvalidMethodId) - .map(|_| ()) - } - - /// Checks if the given `DID` has a valid [`StardustDID`] network name. - /// - /// # Errors - /// - /// Returns `Err` if the input is not a valid network name according to the [`StardustDID`] method specification. - fn check_network(did: &D) -> Result<()> { - let (network_name, _) = Self::denormalized_components(did.method_id()); - NetworkName::validate_network_name(network_name).map_err(|_| DIDError::Other("invalid network name")) - } - - /// Normalizes the DID `method_id` by removing the default network segment if present. - /// - /// E.g. - /// - `"did:iota:main:123" -> "did:iota:123"` is normalized - /// - `"did:iota:dev:123" -> "did:iota:dev:123"` is unchanged - // TODO: Remove the lint once this bug in clippy has been fixed. Without to_owned a mutable reference will be aliased. - #[allow(clippy::unnecessary_to_owned)] - fn normalize(mut did: CoreDID) -> CoreDID { - let method_id = did.method_id(); - let (network, tag) = Self::denormalized_components(method_id); - if tag.len() == method_id.len() || network != Self::DEFAULT_NETWORK { - did - } else { - did - .set_method_id(tag.to_owned()) - .expect("normalizing a valid CoreDID should be Ok"); - did - } - } - - /// foo:bar -> (foo,bar) - /// foo:bar:baz -> (foo, bar:baz) - /// foo -> (StardustDID::DEFAULT_NETWORK.as_ref(), foo) - #[inline(always)] - fn denormalized_components(input: &str) -> (&str, &str) { - input - .find(':') - .map(|idx| input.split_at(idx)) - .map(|(network, tail)| (network, &tail[1..])) - // Self::DEFAULT_NETWORK is built from a static reference so unwrapping is fine - .unwrap_or((Self::DEFAULT_NETWORK, input)) - } -} - -impl FromStr for StardustDID { - type Err = DIDError; - - fn from_str(s: &str) -> std::result::Result { - Self::parse(s) - } -} - -impl TryFrom<&str> for StardustDID { - type Error = DIDError; - - fn try_from(other: &str) -> std::result::Result { - Self::parse(other) - } -} - -impl TryFrom for StardustDID { - type Error = DIDError; - - fn try_from(other: String) -> std::result::Result { - Self::parse(other) - } -} - -impl Display for StardustDID { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - write!(f, "{}", self.0) - } -} - -impl From for CoreDID { - fn from(id: StardustDID) -> Self { - id.0 - } -} - -impl From for String { - fn from(did: StardustDID) -> Self { - did.into_string() - } -} - -impl TryFrom for StardustDID { - type Error = DIDError; - fn try_from(value: CoreDID) -> std::result::Result { - Self::try_from_core(value) - } -} - -impl TryFrom for StardustDID { - type Error = DIDError; - - fn try_from(other: BaseDIDUrl) -> Result { - let core_did: CoreDID = CoreDID::try_from(other)?; - Self::try_from(core_did) - } -} - -impl AsRef for StardustDID { - fn as_ref(&self) -> &CoreDID { - &self.0 - } -} - -impl KeyComparable for StardustDID { - type Key = CoreDID; - - #[inline] - fn key(&self) -> &Self::Key { - self.as_ref() - } -} - -#[cfg(feature = "client")] -mod __stardust_did_iota_client { - use crate::block::output::AliasId; - use crate::StardustDID; - - impl From<&StardustDID> for AliasId { - /// Creates an [`AliasId`] from the DID tag. - fn from(did: &StardustDID) -> Self { - let tag_bytes: [u8; StardustDID::TAG_BYTES_LEN] = prefix_hex::decode(did.tag()) - .expect("being able to successfully decode the tag should be checked during DID creation"); - AliasId::new(tag_bytes) - } - } -} - -#[cfg(test)] -mod tests { - use once_cell::sync::Lazy; - use proptest::strategy::Strategy; - use proptest::*; - - use super::*; - - // =========================================================================================================================== - // Reusable constants and statics - // =========================================================================================================================== - - const PLACEHOLDER_TAG: &str = "0x0000000000000000000000000000000000000000000000000000000000000000"; - - // obtained AliasID from a valid OutputID string - // output_id copied from https://github.com/iotaledger/bee/blob/30cab4f02e9f5d72ffe137fd9eb09723b4f0fdb6/bee-block/tests/output_id.rs - // value of AliasID computed from AliasId::from(OutputId).to_string() - const VALID_ALIAS_ID_STR: &str = "0xf29dd16310c2100fd1bf568b345fb1cc14d71caa3bd9b5ad735d2bd6d455ca3b"; - - const LEN_VALID_ALIAS_STR: usize = VALID_ALIAS_ID_STR.len(); - - static VALID_STARDUST_DID_STRING: Lazy = - Lazy::new(|| format!("did:{}:{}", StardustDID::METHOD, VALID_ALIAS_ID_STR)); - - // Rules are: at least one character, at most six characters and may only contain digits and/or lowercase ascii - // characters. - const VALID_NETWORK_NAMES: [&str; 13] = [ - StardustDID::DEFAULT_NETWORK, - "main", - "dev", - "smr", - "rms", - "test", - "foo", - "foobar", - "123456", - "0", - "foo42", - "bar123", - "42foo", - ]; - - static VALID_STARDUST_DID_STRINGS: Lazy> = Lazy::new(|| { - let network_tag_to_did = |network, tag| format!("did:{}:{}:{}", StardustDID::METHOD, network, tag); - - let valid_strings: Vec = VALID_NETWORK_NAMES - .iter() - .flat_map(|network| { - [VALID_ALIAS_ID_STR, PLACEHOLDER_TAG] - .iter() - .map(move |tag| network_tag_to_did(network, tag)) - }) - .collect(); - - // in principle the previous binding is not necessary (we could have just returned the value), - // but let's just ensure that it contains the expected number of elements first. - assert_eq!(valid_strings.len(), 2 * VALID_NETWORK_NAMES.len()); - - valid_strings - }); - - // =========================================================================================================================== - // Test check_* methods - // =========================================================================================================================== - - #[test] - fn invalid_check_method() { - let did_key_core: CoreDID = CoreDID::parse("did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK").unwrap(); - assert!(matches!( - StardustDID::check_method(&did_key_core), - Err(DIDError::InvalidMethodName) - )); - } - - #[test] - fn valid_check_method() { - let did_stardust_core: CoreDID = CoreDID::parse(&*VALID_STARDUST_DID_STRING).unwrap(); - assert!(StardustDID::check_method(&did_stardust_core).is_ok()); - } - - #[test] - fn valid_check_network() { - let assert_check_network = |input: &str| { - let did_core: CoreDID = - CoreDID::parse(input).unwrap_or_else(|_| panic!("expected {} to parse to a valid CoreDID", input)); - assert!( - StardustDID::check_network(&did_core).is_ok(), - "test: valid_check_network failed with input {}", - input, - ); - }; - - for network_name in VALID_NETWORK_NAMES { - let did_string = format!( - "did:method:{}:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK", - network_name - ); - assert_check_network(&did_string); - } - - assert_check_network("did:method:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"); - } - - #[test] - fn invalid_check_network() { - // Loop over list of network names known to be invalid, attempt to create a CoreDID containing the given network - // name in the method_id sub-string and ensure that `StardustDID::check_network` fails. If the provided network - // name is in conflict with the DID Core spec itself then proceed to the next network name. - - // Ensure that this test is robust to changes in the supplied list of network names, i.e. fail if none of the - // network names can be contained in a generic CoreDID. - - let mut check_network_executed: bool = false; - - const INVALID_NETWORK_NAMES: [&str; 10] = [ - "Main", "fOo", "deV", "féta", "", " ", "foo ", " foo", "1234567", "foobar0", - ]; - for network_name in INVALID_NETWORK_NAMES { - let did_string: String = format!( - "did:method:{}:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK", - network_name - ); - let did_core: CoreDID = { - match CoreDID::parse(&did_string) { - Ok(did_core) => did_core, - Err(_) => continue, - } - }; - - assert!(matches!(StardustDID::check_network(&did_core), Err(DIDError::Other(_)))); - check_network_executed = true; - } - assert!( - check_network_executed, - "StardustDID::check_network` should have been executed" - ); - } - - #[test] - fn valid_check_tag() { - for input in VALID_STARDUST_DID_STRINGS.iter() { - let did_core: CoreDID = CoreDID::parse(input).unwrap(); - assert!( - StardustDID::check_tag(&did_core).is_ok(), - "test: valid_check_method_id failed on input {}", - input - ); - } - - // Should also work for DID's of the form: did::: - let did_other_string: String = format!("did:method:{}", VALID_ALIAS_ID_STR); - let did_other_with_network: String = format!("did:method:test:{}", VALID_ALIAS_ID_STR); - let did_other_core: CoreDID = CoreDID::parse(&did_other_string).unwrap(); - let did_other_with_network_core: CoreDID = CoreDID::parse(&did_other_with_network).unwrap(); - - assert!(StardustDID::check_tag(&did_other_core).is_ok()); - assert!(StardustDID::check_tag(&did_other_with_network_core).is_ok()); - } - - #[test] - fn invalid_check_tag() { - let invalid_method_id_strings = [ - // Too many segments - format!("did:method:main:test:{}", VALID_ALIAS_ID_STR), - // Tag is not prefixed - format!("did:method:{}", &VALID_ALIAS_ID_STR.strip_prefix("0x").unwrap()), - // Tag is too long - format!( - "did:method:{}", - &VALID_ALIAS_ID_STR.chars().chain("a".chars()).collect::() - ), - // Tag is too short (omit last character) - format!("did:method:main:{}", &VALID_ALIAS_ID_STR[..65]), - ]; - - for input in invalid_method_id_strings { - let did_core: CoreDID = CoreDID::parse(input).unwrap(); - assert!( - matches!(StardustDID::check_tag(&did_core), Err(DIDError::InvalidMethodId)), - "{}", - did_core - ); - } - } - - // =========================================================================================================================== - // Test constructors - // =========================================================================================================================== - - #[test] - fn placeholder_produces_a_did_with_expected_string_representation() { - assert_eq!( - StardustDID::placeholder(&NetworkName::try_from(StardustDID::DEFAULT_NETWORK).unwrap()).as_str(), - format!("did:{}:{}", StardustDID::METHOD, PLACEHOLDER_TAG) - ); - - for name in VALID_NETWORK_NAMES - .iter() - .filter(|name| *name != &StardustDID::DEFAULT_NETWORK) - { - let network_name: NetworkName = NetworkName::try_from(*name).unwrap(); - let did: StardustDID = StardustDID::placeholder(&network_name); - assert_eq!( - did.as_str(), - format!("did:{}:{}:{}", StardustDID::METHOD, name, PLACEHOLDER_TAG) - ); - } - } - - #[test] - fn normalization_in_constructors() { - let did_with_default_network_string: String = format!( - "did:{}:{}:{}", - StardustDID::METHOD, - StardustDID::DEFAULT_NETWORK, - VALID_ALIAS_ID_STR - ); - let expected_normalization_string_representation: String = - format!("did:{}:{}", StardustDID::METHOD, VALID_ALIAS_ID_STR); - - assert_eq!( - StardustDID::parse(did_with_default_network_string).unwrap().as_str(), - expected_normalization_string_representation - ); - } - - #[test] - fn parse_valid() { - for did_str in VALID_STARDUST_DID_STRINGS.iter() { - assert!(StardustDID::parse(&did_str).is_ok()); - } - } - - #[test] - fn parse_invalid() { - let execute_assertions = |valid_alias_id: &str| { - assert!(matches!( - StardustDID::parse(format!("dod:{}:{}", StardustDID::METHOD, valid_alias_id)), - Err(DIDError::InvalidScheme) - )); - - assert!(matches!( - StardustDID::parse(format!("did:key:{}", valid_alias_id)), - Err(DIDError::InvalidMethodName) - )); - - // invalid network name (exceeded six characters) - assert!(matches!( - StardustDID::parse(format!("did:{}:1234567:{}", StardustDID::METHOD, valid_alias_id)), - Err(DIDError::Other(_)) - )); - - // invalid network name (contains non ascii character é) - assert!(matches!( - StardustDID::parse(format!("did:{}:féta:{}", StardustDID::METHOD, valid_alias_id)), - Err(DIDError::InvalidMethodId) - )); - - // invalid tag - assert!(matches!( - StardustDID::parse(format!("did:{}:", StardustDID::METHOD)), - Err(DIDError::InvalidMethodId) - )); - - // too many segments in method_id - assert!(matches!( - StardustDID::parse(format!("did:{}:test:foo:{}", StardustDID::METHOD, valid_alias_id)), - Err(DIDError::InvalidMethodId) - )); - }; - - execute_assertions(PLACEHOLDER_TAG); - execute_assertions(VALID_ALIAS_ID_STR); - } - - // =========================================================================================================================== - // Test constructors with randomly generated input - // =========================================================================================================================== - - #[cfg(feature = "iota-client")] - fn arbitrary_alias_id() -> impl Strategy { - ( - proptest::prelude::any::<[u8; 32]>(), - iota_client::block::output::OUTPUT_INDEX_RANGE, - ) - .prop_map(|(bytes, idx)| { - let transaction_id = iota_client::block::payload::transaction::TransactionId::new(bytes); - let output_id = iota_client::block::output::OutputId::new(transaction_id, idx).unwrap(); - iota_client::block::output::AliasId::from(output_id) - }) - } - - #[cfg(feature = "iota-client")] - proptest! { - #[test] - fn property_based_valid_parse(alias_id in arbitrary_alias_id()) { - let did: String = format!("did:{}:{}",StardustDID::METHOD, alias_id); - assert!(StardustDID::parse(&did).is_ok()); - } - } - - #[cfg(feature = "iota-client")] - proptest! { - #[test] - fn property_based_new(bytes in proptest::prelude::any::<[u8;32]>()) { - for network_name in VALID_NETWORK_NAMES.iter().map(|name| NetworkName::try_from(*name).unwrap()) { - // check that this does not panic - StardustDID::new(&bytes, &network_name); - } - } - } - - #[cfg(feature = "iota-client")] - proptest! { - #[test] - fn property_based_alias_id_string_representation_roundtrip(alias_id in arbitrary_alias_id()) { - for network_name in VALID_NETWORK_NAMES.iter().map(|name| NetworkName::try_from(*name).unwrap()) { - assert_eq!( - iota_client::block::output::AliasId::from_str(StardustDID::new(&alias_id, &network_name).tag()).unwrap(), - alias_id - ); - } - } - } - - fn arbitrary_alias_id_string_replica() -> impl Strategy { - proptest::string::string_regex(&format!("0x([a-f]|[0-9]){{{}}}", (LEN_VALID_ALIAS_STR - 2))) - .expect("regex should be ok") - } - - proptest! { - #[test] - fn valid_alias_id_string_replicas(tag in arbitrary_alias_id_string_replica()) { - let did : String = format!("did:{}:{}", StardustDID::METHOD, tag); - assert!( - StardustDID::parse(did).is_ok() - ); - } - } - - fn arbitrary_invalid_tag() -> impl Strategy { - proptest::string::string_regex("[[:^alpha:]|[a-z]|[1-9]]*") - .expect("regex should be ok") - .prop_map(|arb_string| { - if arb_string - .chars() - .all(|c| c.is_ascii_hexdigit() && c.is_ascii_lowercase()) - && arb_string.len() == LEN_VALID_ALIAS_STR - && arb_string.starts_with("0x") - { - // this means we are in the rare case of generating a valid string hence we replace the last 0 with the non - // ascii character é - let mut counter = 0; - arb_string - .chars() - .rev() - .map(|value| { - if value == '0' && counter == 0 { - counter += 1; - 'é' - } else { - value - } - }) - .collect::() - } else { - arb_string - } - }) - } - - proptest! { - #[test] - fn invalid_tag_property_based_parse(tag in arbitrary_invalid_tag()) { - let did: String = format!("did:{}:{}", StardustDID::METHOD, tag); - assert!( - StardustDID::parse(did).is_err() - ); - } - } - - fn arbitrary_delimiter_mixed_in_prefix_hex() -> impl Strategy { - proptest::string::string_regex("0x([a-f]|[:]|[0-9])*").expect("regex should be ok") - } - - proptest! { - #[test] - fn invalid_hex_mixed_with_delimiter(tag in arbitrary_delimiter_mixed_in_prefix_hex()) { - let did: String = format!("did:{}:{}", StardustDID::METHOD, tag); - assert!(StardustDID::parse(did).is_err()); - } - } - - // =========================================================================================================================== - // Test getters - // =========================================================================================================================== - #[test] - fn test_network() { - let execute_assertions = |valid_alias_id: &str| { - let did: StardustDID = format!("did:{}:{}", StardustDID::METHOD, valid_alias_id) - .parse() - .unwrap(); - assert_eq!(did.network_str(), StardustDID::DEFAULT_NETWORK); - - let did: StardustDID = format!("did:{}:dev:{}", StardustDID::METHOD, valid_alias_id) - .parse() - .unwrap(); - assert_eq!(did.network_str(), "dev"); - - let did: StardustDID = format!("did:{}:test:{}", StardustDID::METHOD, valid_alias_id) - .parse() - .unwrap(); - assert_eq!(did.network_str(), "test"); - - let did: StardustDID = format!("did:{}:custom:{}", StardustDID::METHOD, valid_alias_id) - .parse() - .unwrap(); - assert_eq!(did.network_str(), "custom"); - }; - - execute_assertions(PLACEHOLDER_TAG); - execute_assertions(VALID_ALIAS_ID_STR); - } - - #[test] - fn test_tag() { - let execute_assertions = |valid_alias_id: &str| { - let did: StardustDID = format!("did:{}:{}", StardustDID::METHOD, valid_alias_id) - .parse() - .unwrap(); - assert_eq!(did.tag(), valid_alias_id); - - let did: StardustDID = format!( - "did:{}:{}:{}", - StardustDID::METHOD, - StardustDID::DEFAULT_NETWORK, - valid_alias_id - ) - .parse() - .unwrap(); - assert_eq!(did.tag(), valid_alias_id); - - let did: StardustDID = format!("did:{}:dev:{}", StardustDID::METHOD, valid_alias_id) - .parse() - .unwrap(); - assert_eq!(did.tag(), valid_alias_id); - - let did: StardustDID = format!("did:{}:custom:{}", StardustDID::METHOD, valid_alias_id) - .parse() - .unwrap(); - assert_eq!(did.tag(), valid_alias_id); - }; - execute_assertions(PLACEHOLDER_TAG); - execute_assertions(VALID_ALIAS_ID_STR); - } - - // =========================================================================================================================== - // Test DIDUrl - // =========================================================================================================================== - - #[test] - fn test_parse_did_url_valid() { - let execute_assertions = |valid_alias_id: &str| { - assert!(StardustDIDUrl::parse(format!("did:{}:{}", StardustDID::METHOD, valid_alias_id)).is_ok()); - assert!(StardustDIDUrl::parse(format!("did:{}:{}#fragment", StardustDID::METHOD, valid_alias_id)).is_ok()); - assert!(StardustDIDUrl::parse(format!( - "did:{}:{}?somequery=somevalue", - StardustDID::METHOD, - valid_alias_id - )) - .is_ok()); - assert!(StardustDIDUrl::parse(format!( - "did:{}:{}?somequery=somevalue#fragment", - StardustDID::METHOD, - valid_alias_id - )) - .is_ok()); - - assert!(StardustDIDUrl::parse(format!("did:{}:main:{}", StardustDID::METHOD, valid_alias_id)).is_ok()); - assert!(StardustDIDUrl::parse(format!("did:{}:main:{}#fragment", StardustDID::METHOD, valid_alias_id)).is_ok()); - assert!(StardustDIDUrl::parse(format!( - "did:{}:main:{}?somequery=somevalue", - StardustDID::METHOD, - valid_alias_id - )) - .is_ok()); - assert!(StardustDIDUrl::parse(format!( - "did:{}:main:{}?somequery=somevalue#fragment", - StardustDID::METHOD, - valid_alias_id - )) - .is_ok()); - - assert!(StardustDIDUrl::parse(format!("did:{}:dev:{}", StardustDID::METHOD, valid_alias_id)).is_ok()); - assert!(StardustDIDUrl::parse(format!("did:{}:dev:{}#fragment", StardustDID::METHOD, valid_alias_id)).is_ok()); - assert!(StardustDIDUrl::parse(format!( - "did:{}:dev:{}?somequery=somevalue", - StardustDID::METHOD, - valid_alias_id - )) - .is_ok()); - assert!(StardustDIDUrl::parse(format!( - "did:{}:dev:{}?somequery=somevalue#fragment", - StardustDID::METHOD, - valid_alias_id - )) - .is_ok()); - - assert!(StardustDIDUrl::parse(format!("did:{}:custom:{}", StardustDID::METHOD, valid_alias_id)).is_ok()); - assert!(StardustDIDUrl::parse(format!( - "did:{}:custom:{}#fragment", - StardustDID::METHOD, - valid_alias_id - )) - .is_ok()); - assert!(StardustDIDUrl::parse(format!( - "did:{}:custom:{}?somequery=somevalue", - StardustDID::METHOD, - valid_alias_id - )) - .is_ok()); - assert!(StardustDIDUrl::parse(format!( - "did:{}:custom:{}?somequery=somevalue#fragment", - StardustDID::METHOD, - valid_alias_id - )) - .is_ok()); - }; - execute_assertions(PLACEHOLDER_TAG); - execute_assertions(VALID_ALIAS_ID_STR); - } - - #[test] - fn valid_url_setters() { - let execute_assertions = |valid_alias_id: &str| { - let mut did_url: StardustDIDUrl = StardustDID::parse(format!("did:{}:{}", StardustDID::METHOD, valid_alias_id)) - .unwrap() - .into_url(); - - did_url.set_path(Some("/foo")).unwrap(); - did_url.set_query(Some("diff=true")).unwrap(); - did_url.set_fragment(Some("foo")).unwrap(); - - assert_eq!(did_url.path(), Some("/foo")); - assert_eq!(did_url.query(), Some("diff=true")); - assert_eq!(did_url.fragment(), Some("foo")); - }; - execute_assertions(PLACEHOLDER_TAG); - execute_assertions(VALID_ALIAS_ID_STR); - } -} diff --git a/identity_stardust/src/document/mod.rs b/identity_stardust/src/document/mod.rs deleted file mode 100644 index 208dd3670a..0000000000 --- a/identity_stardust/src/document/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -pub use stardust_document::StardustCoreDocument; -pub use stardust_document::StardustDocument; -pub use stardust_document::StardustService; -pub use stardust_document::StardustVerificationMethod; -pub use stardust_document_metadata::StardustDocumentMetadata; - -mod stardust_document; -mod stardust_document_metadata; diff --git a/identity_stardust/src/document/stardust_document.rs b/identity_stardust/src/document/stardust_document.rs deleted file mode 100644 index 8dc1ce1405..0000000000 --- a/identity_stardust/src/document/stardust_document.rs +++ /dev/null @@ -1,764 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use core::fmt; -use core::fmt::Debug; -use core::fmt::Display; - -use identity_core::common::Object; -use identity_core::common::OneOrSet; -use identity_core::common::OrderedSet; -use identity_core::common::Url; -use identity_core::convert::FmtJson; -use identity_core::crypto::GetSignature; -use identity_core::crypto::PrivateKey; -use identity_core::crypto::ProofOptions; -use identity_core::crypto::SetSignature; -use identity_did::document::CoreDocument; -use identity_did::document::Document; -use identity_did::service::Service; -use identity_did::utils::DIDUrlQuery; -use identity_did::verifiable::DocumentSigner; -use identity_did::verifiable::VerifierOptions; -use identity_did::verification::MethodRelationship; -use identity_did::verification::MethodScope; -use identity_did::verification::MethodUriType; -use identity_did::verification::TryMethod; -use identity_did::verification::VerificationMethod; -use serde::Deserialize; -use serde::Serialize; - -use crate::error::Result; -use crate::NetworkName; -use crate::StardustDID; -use crate::StardustDIDUrl; -use crate::StardustDocumentMetadata; -use crate::StateMetadataDocument; -use crate::StateMetadataEncoding; - -/// A [`VerificationMethod`] adhering to the IOTA UTXO DID method specification. -pub type StardustVerificationMethod = VerificationMethod; - -/// A [`Service`] adhering to the IOTA UTXO DID method specification. -pub type StardustService = Service; - -/// A [`CoreDocument`] whose fields adhere to the IOTA UTXO DID method specification. -pub type StardustCoreDocument = CoreDocument; - -/// A DID Document adhering to the IOTA UTXO DID method specification. -/// -/// This extends [`CoreDocument`]. -#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] -pub struct StardustDocument { - #[serde(rename = "doc")] - pub(crate) document: StardustCoreDocument, - #[serde(rename = "meta")] - pub metadata: StardustDocumentMetadata, -} - -impl StardustDocument { - // =========================================================================== - // Constructors - // =========================================================================== - - /// Constructs an empty DID Document with a [`StardustDID::placeholder`] identifier - /// for the given `network`. - // TODO: always take Option or `new_with_options` for a particular network? - // TODO: store the network in the serialized state metadata? Currently it's lost during packing. - pub fn new(network: &NetworkName) -> Self { - Self::new_with_id(StardustDID::placeholder(network)) - } - - /// Constructs an empty DID Document with the given identifier. - pub fn new_with_id(id: StardustDID) -> Self { - // PANIC: constructing an empty DID Document is infallible, caught by tests otherwise. - let document: StardustCoreDocument = CoreDocument::builder(Object::default()) - .id(id) - .build() - .expect("empty StardustDocument constructor failed"); - let metadata: StardustDocumentMetadata = StardustDocumentMetadata::new(); - Self { document, metadata } - } - - // =========================================================================== - // Properties - // =========================================================================== - - /// Returns the DID document identifier. - pub fn id(&self) -> &StardustDID { - self.document.id() - } - - /// Returns a reference to the DID controllers. - /// - /// NOTE: controllers are determined by the `state_controller` unlock condition of the output - /// during resolution and are omitted when publishing. - pub fn controller(&self) -> Option<&OneOrSet> { - self.document.controller() - } - - /// Returns a reference to the `alsoKnownAs` set. - pub fn also_known_as(&self) -> &OrderedSet { - self.document.also_known_as() - } - - /// Returns a mutable reference to the `alsoKnownAs` set. - pub fn also_known_as_mut(&mut self) -> &mut OrderedSet { - self.document.also_known_as_mut() - } - - /// Returns a reference to the underlying [`StardustCoreDocument`]. - pub fn core_document(&self) -> &StardustCoreDocument { - &self.document - } - - /// Returns a mutable reference to the underlying [`StardustCoreDocument`]. - /// - /// WARNING: mutating the inner document directly bypasses restrictions and - /// may have undesired consequences. - pub fn core_document_mut(&mut self) -> &mut StardustCoreDocument { - &mut self.document - } - - /// Returns a reference to the custom DID Document properties. - pub fn properties(&self) -> &Object { - self.document.properties() - } - - /// Returns a mutable reference to the custom DID Document properties. - pub fn properties_mut(&mut self) -> &mut Object { - self.document.properties_mut() - } - - // =========================================================================== - // Services - // =========================================================================== - - /// Return a set of all [`StardustService`]s in the document. - pub fn service(&self) -> &OrderedSet { - self.document.service() - } - - /// Add a new [`StardustService`] to the document. - /// - /// Returns `true` if the service was added. - pub fn insert_service(&mut self, service: StardustService) -> bool { - if service.id().fragment().is_none() { - false - } else { - self.core_document_mut().service_mut().append(service) - } - } - - /// Remove a [`StardustService`] identified by the given [`StardustDIDUrl`] from the document. - /// - /// Returns `true` if a service was removed. - pub fn remove_service(&mut self, did_url: &StardustDIDUrl) -> bool { - self.core_document_mut().service_mut().remove(did_url) - } - - // =========================================================================== - // Verification Methods - // =========================================================================== - - /// Returns an iterator over all [`StardustVerificationMethod`] in the DID Document. - pub fn methods(&self) -> impl Iterator { - self.document.methods() - } - - /// Adds a new [`StardustVerificationMethod`] to the document in the given [`MethodScope`]. - /// - /// # Errors - /// - /// Returns an error if a method with the same fragment already exists. - pub fn insert_method(&mut self, method: StardustVerificationMethod, scope: MethodScope) -> Result<()> { - Ok(self.core_document_mut().insert_method(method, scope)?) - } - - /// Removes all references to the specified [`StardustVerificationMethod`]. - /// - /// # Errors - /// - /// Returns an error if the method does not exist. - pub fn remove_method(&mut self, did_url: &StardustDIDUrl) -> Result<()> { - Ok(self.core_document_mut().remove_method(did_url)?) - } - - /// Attaches the relationship to the given method, if the method exists. - /// - /// Note: The method needs to be in the set of verification methods, - /// so it cannot be an embedded one. - pub fn attach_method_relationship( - &mut self, - did_url: &StardustDIDUrl, - relationship: MethodRelationship, - ) -> Result { - Ok( - self - .core_document_mut() - .attach_method_relationship(did_url, relationship)?, - ) - } - - /// Detaches the given relationship from the given method, if the method exists. - pub fn detach_method_relationship( - &mut self, - did_url: &StardustDIDUrl, - relationship: MethodRelationship, - ) -> Result { - Ok( - self - .core_document_mut() - .detach_method_relationship(did_url, relationship)?, - ) - } - - /// Returns the first [`StardustVerificationMethod`] with an `id` property matching the - /// provided `query` and the verification relationship specified by `scope` if present. - /// - /// WARNING: improper usage of this allows violating the uniqueness of the verification method - /// sets. - pub fn resolve_method_mut<'query, Q>( - &mut self, - query: Q, - scope: Option, - ) -> Option<&mut StardustVerificationMethod> - where - Q: Into>, - { - self.document.resolve_method_mut(query, scope) - } - - // =========================================================================== - // Signatures - // =========================================================================== - - /// Creates a new [`DocumentSigner`] that can be used to create digital signatures - /// from verification methods in this DID Document. - pub fn signer<'base>(&'base self, private_key: &'base PrivateKey) -> DocumentSigner<'base, '_, StardustDID> { - self.document.signer(private_key) - } - - /// Signs the provided `data` with the verification method specified by `method_query`. - /// See [`StardustDocument::signer`] for creating signatures with a builder pattern. - /// - /// NOTE: does not validate whether `private_key` corresponds to the verification method. - /// See [`StardustDocument::verify_data`]. - /// - /// # Errors - /// - /// Fails if an unsupported verification method is used, data - /// serialization fails, or the signature operation fails. - pub fn sign_data<'query, 'this: 'query, X, Q>( - &'this self, - data: &mut X, - private_key: &'this PrivateKey, - method_query: Q, - options: ProofOptions, - ) -> Result<()> - where - X: Serialize + SetSignature + TryMethod, - Q: Into>, - { - self - .signer(private_key) - .method(method_query) - .options(options) - .sign(data) - .map_err(Into::into) - } - - // =========================================================================== - // Packing - // =========================================================================== - - /// Serializes the document for inclusion in an Alias Output's state metadata - /// with the default [`StateMetadataEncoding`]. - pub fn pack(self) -> Result> { - self.pack_with_encoding(StateMetadataEncoding::Json) - } - - /// Serializes the document for inclusion in an Alias Output's state metadata. - pub fn pack_with_encoding(self, encoding: StateMetadataEncoding) -> Result> { - StateMetadataDocument::from(self).pack(encoding) - } - - /// Deserializes the document from the state metadata bytes of an Alias Output. - /// - /// If `allow_empty` is true, this will return an empty DID document marked as `deactivated` - /// if `state_metadata` is empty. - /// - /// NOTE: `did` is required since it is omitted from the serialized DID Document and - /// cannot be inferred from the state metadata. It also indicates the network, which is not - /// encoded in the `AliasId` alone. - pub fn unpack(did: &StardustDID, state_metadata: &[u8], allow_empty: bool) -> Result { - if state_metadata.is_empty() && allow_empty { - let mut empty_document = StardustDocument::new_with_id(did.clone()); - empty_document.metadata.created = None; - empty_document.metadata.updated = None; - empty_document.metadata.deactivated = Some(true); - return Ok(empty_document); - } - StateMetadataDocument::unpack(state_metadata).and_then(|doc| doc.into_stardust_document(did)) - } -} - -#[cfg(feature = "client")] -mod client_document { - use std::ops::Deref; - - use crate::block::output::AliasId; - use crate::block::output::Output; - use crate::block::output::OutputId; - use crate::block::payload::transaction::TransactionEssence; - use crate::block::payload::Payload; - use crate::block::Block; - use crate::error::Result; - use crate::Error; - use crate::NetworkName; - - use super::*; - - impl StardustDocument { - /// Returns all DID documents of the Alias Outputs contained in the block's transaction payload - /// outputs, if any. - /// - /// Errors if any Alias Output does not contain a valid or empty DID Document. - pub fn unpack_from_block(network: &NetworkName, block: &Block) -> Result> { - let mut documents = Vec::new(); - - if let Some(Payload::Transaction(tx_payload)) = block.payload() { - let TransactionEssence::Regular(regular) = tx_payload.essence(); - - for (index, output) in regular.outputs().iter().enumerate() { - if let Output::Alias(alias_output) = output { - let alias_id = if alias_output.alias_id().is_null() { - AliasId::from( - OutputId::new( - tx_payload.id(), - index - .try_into() - .map_err(|_| Error::OutputIdConversionError(format!("output index {index} must fit into a u16")))?, - ) - .map_err(|err| Error::OutputIdConversionError(err.to_string()))?, - ) - } else { - alias_output.alias_id().to_owned() - }; - - let did: StardustDID = StardustDID::new(alias_id.deref(), network); - documents.push(StardustDocument::unpack(&did, alias_output.state_metadata(), true)?); - } - } - } - - Ok(documents) - } - } -} - -impl Document for StardustDocument { - type D = StardustDID; - type U = Object; - type V = Object; - - fn id(&self) -> &Self::D { - StardustDocument::id(self) - } - - fn resolve_service<'query, 'me, Q>(&'me self, query: Q) -> Option<&Service> - where - Q: Into>, - { - self.document.resolve_service(query) - } - - fn resolve_method<'query, 'me, Q>( - &'me self, - query: Q, - scope: Option, - ) -> Option<&VerificationMethod> - where - Q: Into>, - { - self.document.resolve_method(query, scope) - } - - fn verify_data(&self, data: &X, options: &VerifierOptions) -> identity_did::Result<()> - where - X: Serialize + GetSignature + ?Sized, - { - self.document.verify_data(data, options) - } -} - -#[cfg(feature = "revocation-bitmap")] -mod iota_document_revocation { - use identity_did::utils::DIDUrlQuery; - - use crate::Error; - use crate::Result; - - use super::StardustDocument; - - impl StardustDocument { - /// If the document has a [`RevocationBitmap`](identity_did::revocation::RevocationBitmap) - /// service identified by `service_query`, revoke all specified `indices`. - pub fn revoke_credentials<'query, 'me, Q>(&mut self, service_query: Q, indices: &[u32]) -> Result<()> - where - Q: Into>, - { - self - .core_document_mut() - .revoke_credentials(service_query, indices) - .map_err(Error::RevocationError) - } - - /// If the document has a [`RevocationBitmap`](identity_did::revocation::RevocationBitmap) - /// service with an id by `service_query`, unrevoke all specified `indices`. - pub fn unrevoke_credentials<'query, 'me, Q>(&'me mut self, service_query: Q, indices: &[u32]) -> Result<()> - where - Q: Into>, - { - self - .core_document_mut() - .unrevoke_credentials(service_query, indices) - .map_err(Error::RevocationError) - } - } -} - -impl From for StardustCoreDocument { - fn from(document: StardustDocument) -> Self { - document.document - } -} - -impl From for CoreDocument { - fn from(document: StardustDocument) -> Self { - document.document.map(Into::into, |id| id) - } -} - -impl From<(StardustCoreDocument, StardustDocumentMetadata)> for StardustDocument { - fn from((document, metadata): (StardustCoreDocument, StardustDocumentMetadata)) -> Self { - Self { document, metadata } - } -} - -impl Display for StardustDocument { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.fmt_json(f) - } -} - -impl TryMethod for StardustDocument { - const TYPE: MethodUriType = MethodUriType::Absolute; -} - -#[cfg(test)] -mod tests { - use identity_core::common::Timestamp; - use identity_core::convert::FromJson; - use identity_core::convert::ToJson; - use identity_core::crypto::KeyPair; - use identity_core::crypto::KeyType; - use identity_did::did::DID; - use identity_did::verifiable::VerifiableProperties; - use identity_did::verification::MethodData; - use identity_did::verification::MethodType; - - use super::*; - - fn valid_did() -> StardustDID { - "did:iota:0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - .parse() - .unwrap() - } - - fn generate_method(controller: &StardustDID, fragment: &str) -> StardustVerificationMethod { - VerificationMethod::builder(Default::default()) - .id(controller.to_url().join(fragment).unwrap()) - .controller(controller.clone()) - .type_(MethodType::Ed25519VerificationKey2018) - .data(MethodData::new_multibase(fragment.as_bytes())) - .build() - .unwrap() - } - - fn generate_document(id: &StardustDID) -> StardustDocument { - let mut metadata: StardustDocumentMetadata = StardustDocumentMetadata::new(); - metadata.created = Some(Timestamp::parse("2020-01-02T00:00:00Z").unwrap()); - metadata.updated = Some(Timestamp::parse("2020-01-02T00:00:00Z").unwrap()); - - let document: StardustCoreDocument = StardustCoreDocument::builder(Object::default()) - .id(id.clone()) - .controller(id.clone()) - .verification_method(generate_method(id, "#key-1")) - .verification_method(generate_method(id, "#key-2")) - .verification_method(generate_method(id, "#key-3")) - .authentication(generate_method(id, "#auth-key")) - .authentication(id.to_url().join("#key-3").unwrap()) - .build() - .unwrap(); - - StardustDocument::from((document, metadata)) - } - - #[test] - fn test_new() { - // VALID new(). - let network: NetworkName = NetworkName::try_from("test").unwrap(); - let placeholder: StardustDID = StardustDID::placeholder(&network); - let doc1: StardustDocument = StardustDocument::new(&network); - assert_eq!(doc1.id().network_str(), network.as_ref()); - assert_eq!(doc1.id().tag(), placeholder.tag()); - assert_eq!(doc1.id(), &placeholder); - assert_eq!(doc1.methods().count(), 0); - assert!(doc1.service().is_empty()); - - // VALID new_with_id(). - let did: StardustDID = valid_did(); - let doc2: StardustDocument = StardustDocument::new_with_id(did.clone()); - assert_eq!(doc2.id(), &did); - assert_eq!(doc2.methods().count(), 0); - assert!(doc2.service().is_empty()); - } - - #[test] - fn test_methods() { - let controller: StardustDID = valid_did(); - let document: StardustDocument = generate_document(&controller); - let expected: Vec = vec![ - generate_method(&controller, "#key-1"), - generate_method(&controller, "#key-2"), - generate_method(&controller, "#key-3"), - generate_method(&controller, "#auth-key"), - ]; - - let mut methods = document.methods(); - assert_eq!(methods.next(), Some(&expected[0])); - assert_eq!(methods.next(), Some(&expected[1])); - assert_eq!(methods.next(), Some(&expected[2])); - assert_eq!(methods.next(), Some(&expected[3])); - assert_eq!(methods.next(), None); - } - - #[test] - fn test_verify_data_with_scope() { - fn generate_data() -> VerifiableProperties { - use identity_core::json; - let mut properties: VerifiableProperties = VerifiableProperties::default(); - properties.properties.insert("int_key".to_owned(), json!(1)); - properties.properties.insert("str".to_owned(), json!("some value")); - properties - .properties - .insert("object".to_owned(), json!({ "inner": 42 })); - properties - } - - let mut document: StardustDocument = StardustDocument::new_with_id(valid_did()); - - // Try sign using each type of verification relationship. - for scope in [ - MethodScope::assertion_method(), - MethodScope::authentication(), - MethodScope::capability_delegation(), - MethodScope::capability_invocation(), - MethodScope::key_agreement(), - MethodScope::VerificationMethod, - ] { - // Add a new method. - let key_new: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); - let method_fragment = format!("{}-1", scope.as_str().to_ascii_lowercase()); - let method_new: StardustVerificationMethod = StardustVerificationMethod::new( - document.id().clone(), - key_new.type_(), - key_new.public(), - method_fragment.as_str(), - ) - .unwrap(); - document.insert_method(method_new, scope).unwrap(); - - // Sign and verify data. - let mut data = generate_data(); - document - .sign_data( - &mut data, - key_new.private(), - method_fragment.as_str(), - ProofOptions::default(), - ) - .unwrap(); - // Signature should still be valid for every scope. - assert!(document.verify_data(&data, &VerifierOptions::default()).is_ok()); - - // Ensure only the correct scope is valid. - for scope_check in [ - MethodScope::assertion_method(), - MethodScope::authentication(), - MethodScope::capability_delegation(), - MethodScope::capability_invocation(), - MethodScope::key_agreement(), - MethodScope::VerificationMethod, - ] { - let result = document.verify_data(&data, &VerifierOptions::new().method_scope(scope_check)); - // Any other scope should fail validation. - if scope_check == scope { - assert!(result.is_ok()); - } else { - assert!(result.is_err()); - } - } - } - } - - #[test] - fn test_services() { - // VALID: add one service. - let mut document: StardustDocument = StardustDocument::new_with_id(valid_did()); - let url1: StardustDIDUrl = document.id().to_url().join("#linked-domain").unwrap(); - let service1: StardustService = Service::from_json(&format!( - r#"{{ - "id":"{}", - "type": "LinkedDomains", - "serviceEndpoint": "https://bar.example.com" - }}"#, - url1 - )) - .unwrap(); - document.insert_service(service1.clone()); - assert_eq!(1, document.service().len()); - assert_eq!(document.resolve_service(&url1), Some(&service1)); - assert_eq!(document.resolve_service("#linked-domain"), Some(&service1)); - assert_eq!(document.resolve_service("linked-domain"), Some(&service1)); - assert_eq!(document.resolve_service(""), None); - assert_eq!(document.resolve_service("#other"), None); - - // VALID: add two services. - let url2: StardustDIDUrl = document.id().to_url().join("#revocation").unwrap(); - let service2: StardustService = Service::from_json(&format!( - r#"{{ - "id":"{}", - "type": "RevocationBitmap2022", - "serviceEndpoint": "data:,blah" - }}"#, - url2 - )) - .unwrap(); - document.insert_service(service2.clone()); - assert_eq!(2, document.service().len()); - assert_eq!(document.resolve_service(&url2), Some(&service2)); - assert_eq!(document.resolve_service("#revocation"), Some(&service2)); - assert_eq!(document.resolve_service("revocation"), Some(&service2)); - assert_eq!(document.resolve_service(""), None); - assert_eq!(document.resolve_service("#other"), None); - - // INVALID: insert service with duplicate fragment fails. - let duplicate: StardustService = Service::from_json(&format!( - r#"{{ - "id":"{}", - "type": "DuplicateService", - "serviceEndpoint": "data:,duplicate" - }}"#, - url1 - )) - .unwrap(); - assert!(!document.insert_service(duplicate.clone())); - assert_eq!(2, document.service().len()); - let resolved: &StardustService = document.resolve_service(&url1).unwrap(); - assert_eq!(resolved, &service1); - assert_ne!(resolved, &duplicate); - - // VALID: remove services. - assert!(document.remove_service(&url1)); - assert_eq!(1, document.service().len()); - let last_service: &StardustService = document.resolve_service(&url2).unwrap(); - assert_eq!(last_service, &service2); - - assert!(document.remove_service(&url2)); - assert_eq!(0, document.service().len()); - } - - #[test] - fn test_document_equality() { - let mut original_doc: StardustDocument = StardustDocument::new_with_id(valid_did()); - let keypair1: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); - let method1: StardustVerificationMethod = StardustVerificationMethod::new( - original_doc.id().to_owned(), - keypair1.type_(), - keypair1.public(), - "test-0", - ) - .unwrap(); - original_doc - .insert_method(method1, MethodScope::capability_invocation()) - .unwrap(); - - // Update the key material of the existing verification method #test-0. - let mut doc1 = original_doc.clone(); - let keypair2: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); - let method2: StardustVerificationMethod = - StardustVerificationMethod::new(doc1.id().to_owned(), keypair2.type_(), keypair2.public(), "test-0").unwrap(); - - doc1 - .remove_method(&doc1.id().to_url().join("#test-0").unwrap()) - .unwrap(); - doc1 - .insert_method(method2, MethodScope::capability_invocation()) - .unwrap(); - - // Even though the method fragment is the same, the key material has been updated - // so the two documents are expected to not be equal. - assert_ne!(original_doc, doc1); - - let mut doc2 = doc1.clone(); - let keypair3: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); - let method3: StardustVerificationMethod = - StardustVerificationMethod::new(doc1.id().to_owned(), keypair3.type_(), keypair3.public(), "test-0").unwrap(); - - let insertion_result = doc2.insert_method(method3, MethodScope::capability_invocation()); - - // Nothing was inserted, because a method with the same fragment already existed. - assert!(insertion_result.is_err()); - assert_eq!(doc1, doc2); - } - - #[test] - fn test_unpack_empty() { - // VALID: unpack empty, deactivated document. - let did: StardustDID = valid_did(); - let empty: &[u8] = &[]; - let document: StardustDocument = StardustDocument::unpack(&did, empty, true).unwrap(); - assert_eq!(document.id(), &did); - assert_eq!(document.metadata.deactivated, Some(true)); - - // Ensure no other fields are injected. - // TODO: update this when controller/governor fields are added? - let json: String = format!("{{\"doc\":{{\"id\":\"{did}\"}},\"meta\":{{\"deactivated\":true}}}}"); - assert_eq!(document.to_json().unwrap(), json); - - // INVALID: reject empty document. - assert!(StardustDocument::unpack(&did, empty, false).is_err()); - } - - #[test] - fn test_json_roundtrip() { - let document: StardustDocument = generate_document(&valid_did()); - - let ser: String = document.to_json().unwrap(); - let de: StardustDocument = StardustDocument::from_json(&ser).unwrap(); - assert_eq!(document, de); - } - - #[test] - fn test_json_fieldnames() { - // Changing the serialization is a breaking change! - let document: StardustDocument = StardustDocument::new_with_id(valid_did()); - let serialization: String = document.to_json().unwrap(); - assert_eq!( - serialization, - format!("{{\"doc\":{},\"meta\":{}}}", document.document, document.metadata) - ); - } -} diff --git a/identity_stardust/src/error.rs b/identity_stardust/src/error.rs deleted file mode 100644 index 08f5542732..0000000000 --- a/identity_stardust/src/error.rs +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -pub type Result = core::result::Result; - -#[derive(Debug, thiserror::Error, strum::IntoStaticStr)] -#[non_exhaustive] -pub enum Error { - #[error("serialization error")] - SerializationError(#[source] identity_core::Error), - #[error("{0}")] - DIDSyntaxError(#[from] identity_did::did::DIDError), - #[error("{0}")] - InvalidDoc(#[from] identity_did::Error), - #[cfg(feature = "iota-client")] - #[error("DID update: {0}")] - DIDUpdateError(&'static str, #[source] Option), - #[cfg(feature = "iota-client")] - #[error("DID resolution failed")] - DIDResolutionError(#[source] iota_client::error::Error), - #[cfg(feature = "iota-client")] - #[error("{0}")] - BasicOutputBuildError(#[source] iota_client::block::Error), - #[error("\"{0}\" is not a valid network name")] - InvalidNetworkName(String), - #[error("unable to resolve a `{expected}` DID on network `{actual}`")] - NetworkMismatch { expected: String, actual: String }, - #[error("invalid state metadata {0}")] - InvalidStateMetadata(&'static str), - #[error("credential revocation error")] - RevocationError(#[source] identity_did::Error), - #[cfg(feature = "client")] - #[error("alias output build error")] - AliasOutputBuildError(#[source] crate::block::Error), - #[cfg(feature = "iota-client")] - #[error("output with id `{0}` is not an alias output")] - NotAnAliasOutput(iota_client::block::output::OutputId), - #[cfg(feature = "iota-client")] - #[error("converting a DTO to an output failed")] - OutputConversionError(#[source] iota_client::block::DtoError), - #[error("conversion to an OutputId failed: {0}")] - OutputIdConversionError(String), - #[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] - #[error("JavaScript function threw an exception: {0}")] - JsError(String), -} diff --git a/identity_stardust/src/lib.rs b/identity_stardust/src/lib.rs deleted file mode 100644 index 2c38e7dbbc..0000000000 --- a/identity_stardust/src/lib.rs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -#![forbid(unsafe_code)] -#![allow(clippy::upper_case_acronyms)] - -// Re-export the `bee_block` crate for implementer convenience. -#[cfg(all(feature = "client", not(feature = "iota-client")))] -pub use bee_block as block; -#[cfg(feature = "iota-client")] -pub use iota_client::block; - -#[cfg(feature = "client")] -pub use client::*; -pub use did::StardustDID; -pub use did::StardustDIDUrl; -pub use document::*; -pub use network::NetworkName; -pub use state_metadata::*; - -pub use self::error::Error; -pub use self::error::Result; - -#[cfg(feature = "client")] -mod client; -mod did; -mod document; -mod error; -mod network; -mod state_metadata; From d2478c3cdccc07e3d4b97a2f6788d9e2fc74344e Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Mon, 12 Sep 2022 12:05:40 +0200 Subject: [PATCH 54/89] Feature-gate `Resolver` (#1007) * Feature gate resolver; change prelude * Add `resolver` to default features * Remove direct resolver dependency in Wasm --- bindings/wasm/Cargo.toml | 8 +------- bindings/wasm/src/error.rs | 8 ++++---- bindings/wasm/src/resolver/mixed_resolver.rs | 2 +- identity_iota/Cargo.toml | 11 +++++++---- identity_iota/src/lib.rs | 15 +++++++++++++++ 5 files changed, 28 insertions(+), 16 deletions(-) diff --git a/bindings/wasm/Cargo.toml b/bindings/wasm/Cargo.toml index 75b74c4961..d21f9649fe 100644 --- a/bindings/wasm/Cargo.toml +++ b/bindings/wasm/Cargo.toml @@ -33,13 +33,7 @@ wasm-bindgen-futures = { version = "0.4", default-features = false } version = "=0.6.0" path = "../../identity_iota" default-features = false -features = ["client", "revocation-bitmap"] - -[dependencies.identity_resolver] -version = "=0.6.0" -path = "../../identity_resolver" -default-features = false -features = ["revocation-bitmap"] +features = ["client", "revocation-bitmap", "resolver"] [dev-dependencies] rand = "0.8.5" diff --git a/bindings/wasm/src/error.rs b/bindings/wasm/src/error.rs index 2c1d760419..e2217953c6 100644 --- a/bindings/wasm/src/error.rs +++ b/bindings/wasm/src/error.rs @@ -1,10 +1,10 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use identity_iota::resolver; use std::borrow::Cow; use std::fmt::Display; use std::result::Result as StdResult; - use wasm_bindgen::JsValue; /// Convenience wrapper for `Result`. @@ -133,14 +133,14 @@ fn error_chain_fmt(e: &impl std::error::Error, f: &mut std::fmt::Formatter<'_>) struct ErrorMessage<'a, E: std::error::Error>(&'a E); -impl<'a> Display for ErrorMessage<'a, identity_resolver::Error> { +impl<'a> Display for ErrorMessage<'a, resolver::Error> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { error_chain_fmt(self.0, f) } } -impl From for WasmError<'_> { - fn from(error: identity_resolver::Error) -> Self { +impl From for WasmError<'_> { + fn from(error: resolver::Error) -> Self { Self { name: Cow::Owned(format!("ResolverError::{}", <&'static str>::from(error.error_cause()))), message: Cow::Owned(ErrorMessage(&error).to_string()), diff --git a/bindings/wasm/src/resolver/mixed_resolver.rs b/bindings/wasm/src/resolver/mixed_resolver.rs index 507d60ede1..b3b62b0a8e 100644 --- a/bindings/wasm/src/resolver/mixed_resolver.rs +++ b/bindings/wasm/src/resolver/mixed_resolver.rs @@ -12,7 +12,7 @@ use identity_iota::did::DID; use identity_iota::iota::IotaDID; use identity_iota::iota::IotaDocument; use identity_iota::iota::IotaIdentityClientExt; -use identity_resolver::SingleThreadedResolver; +use identity_iota::resolver::SingleThreadedResolver; use js_sys::Function; use js_sys::Map; use js_sys::Promise; diff --git a/identity_iota/Cargo.toml b/identity_iota/Cargo.toml index f642f2d3d5..8ae283cf10 100644 --- a/identity_iota/Cargo.toml +++ b/identity_iota/Cargo.toml @@ -17,7 +17,7 @@ identity_core = { version = "=0.6.0", path = "../identity_core", default-feature identity_credential = { version = "=0.6.0", path = "../identity_credential", features = ["validator"], default-features = false } identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } identity_iota_core = { version = "=0.6.0", path = "../identity_iota_core", default-features = false } -identity_resolver = { version = "=0.6.0", path = "../identity_resolver", default-features = false } +identity_resolver = { version = "=0.6.0", path = "../identity_resolver", default-features = false, optional = true } [dev-dependencies] anyhow = "1.0.64" @@ -25,21 +25,24 @@ iota-client = { version = "2.0.0-beta.3", default-features = false, features = [ tokio = { version = "1.17.0", features = ["full"] } [features] -default = ["revocation-bitmap", "client", "iota-client"] +default = ["revocation-bitmap", "client", "iota-client", "resolver"] # Exposes the `IotaIdentityClient` and `IotaIdentityClientExt` traits. client = ["identity_iota_core/client"] # Enables the iota-client integration, the client trait implementations for it, and the `IotaClientExt` trait. -iota-client = ["identity_iota_core/iota-client", "identity_resolver/iota"] +iota-client = ["identity_iota_core/iota-client", "identity_resolver?/iota"] # Enables revocation with `RevocationBitmap2022`. revocation-bitmap = [ "identity_credential/revocation-bitmap", "identity_iota_core/revocation-bitmap", - "identity_resolver/revocation-bitmap", + "identity_resolver?/revocation-bitmap", ] +# Enables support for the `Resolver`. +resolver = ["dep:identity_resolver"] + # Enables support for the unstable identity agent. # Breaking changes to types and functions behind this flag are not covered by semver. unstable-agent = ["dep:identity_agent"] diff --git a/identity_iota/src/lib.rs b/identity_iota/src/lib.rs index f700d290fb..0c5d7a5ef5 100644 --- a/identity_iota/src/lib.rs +++ b/identity_iota/src/lib.rs @@ -93,11 +93,26 @@ pub mod prelude { pub use identity_iota_core::IotaDID; pub use identity_iota_core::IotaDocument; + #[cfg(feature = "iota-client")] + #[cfg_attr(docsrs, doc(cfg(feature = "iota-client")))] + pub use identity_iota_core::IotaClientExt; + #[cfg(feature = "client")] + #[cfg_attr(docsrs, doc(cfg(feature = "client")))] + pub use identity_iota_core::IotaIdentityClient; + #[cfg(feature = "client")] + #[cfg_attr(docsrs, doc(cfg(feature = "client")))] + pub use identity_iota_core::IotaIdentityClientExt; + pub use identity_core::crypto::KeyPair; pub use identity_core::crypto::KeyType; + + #[cfg(feature = "resolver")] + #[cfg_attr(docsrs, doc(cfg(feature = "resolver")))] pub use identity_resolver::Resolver; } +#[cfg(feature = "resolver")] +#[cfg_attr(docsrs, doc(cfg(feature = "resolver")))] pub mod resolver { //! DID resolution utilities From 7d38d0115237c009aa0e2b77eab32d719513b3d6 Mon Sep 17 00:00:00 2001 From: cycraig Date: Mon, 12 Sep 2022 22:25:37 +0200 Subject: [PATCH 55/89] Add length prefix to DID Document payloads (#1010) * Add length prefix to packed document * Update specification documentation * Add length prefix unit test * Apply suggestions from code review * Allow deprecated into_serde in Wasm bindings * Fix identity_iota_core README.md link --- bindings/wasm/tests/wasm.rs | 3 + .../docs/specs/did/iota_did_method_spec.md | 21 ++-- identity_iota_core/Cargo.toml | 2 +- .../src/document/iota_document.rs | 2 +- identity_iota_core/src/error.rs | 2 +- .../src/state_metadata/document.rs | 116 ++++++++++++++---- .../src/state_metadata/encoding.rs | 3 +- 7 files changed, 109 insertions(+), 40 deletions(-) diff --git a/bindings/wasm/tests/wasm.rs b/bindings/wasm/tests/wasm.rs index c67abb9c2e..e24a5ba5eb 100644 --- a/bindings/wasm/tests/wasm.rs +++ b/bindings/wasm/tests/wasm.rs @@ -1,6 +1,9 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +// TODO: remove allow after https://github.com/iotaledger/identity.rs/issues/1011 is fixed. +#![allow(deprecated)] + use std::borrow::Cow; use identity_iota::core::Timestamp; diff --git a/documentation/docs/specs/did/iota_did_method_spec.md b/documentation/docs/specs/did/iota_did_method_spec.md index e45dc24a67..721b082148 100644 --- a/documentation/docs/specs/did/iota_did_method_spec.md +++ b/documentation/docs/specs/did/iota_did_method_spec.md @@ -111,19 +111,14 @@ This tag identifies the Alias Output where the DID Document is stored, and it wi In the `State Metadata` of the Alias Output must be a byte packed payload with header fields as follows: -| Name | Type | Description | -|---------------|--------------|----------------------------------------------------------------------------------------------------------------------------------------------------------| -| Document Type | ByteArray[3] | Set to value **DID** to denote a DID Document. | -| Version | uint8 | Set value **1** to denote the version number of this method | -| Encoding | uint8 | Set to value to **0** to denote JSON encoding without compression. | -| Payload | ByteArray | A DID Document and its metadata, where every occurrence of the DID in the document is replaced by `did:0:0`. It must be encoded according to `Encoding`. | - -Next to [TIP-21](#data-types--subschema-notation), we use the following type definitions: - -| Name | Description | -|-----------|-------------------------------------------------| -| ByteArray | A dynamically sized, but unprefixed byte array. | - +| Name | Type | Description | +|---------------|-------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------| +| Document Type | ByteArray[3] | Set to value **DID** to denote a DID Document. | +| Version | uint8 | Set value **1** to denote the version number of this method | +| Encoding | uint8 | Set to value to **0** to denote JSON encoding without compression. | +| Payload | (uint16)ByteArray | A DID Document and its metadata, where every occurrence of the DID in the document is replaced by `did:0:0`. It must be encoded according to `Encoding`. | + +The types are defined in [TIP-21](#data-types--subschema-notation). #### Payload diff --git a/identity_iota_core/Cargo.toml b/identity_iota_core/Cargo.toml index 223328ce96..90aa0e211d 100644 --- a/identity_iota_core/Cargo.toml +++ b/identity_iota_core/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" homepage = "https://www.iota.org" keywords = ["iota", "tangle", "utxo", "shimmer", "stardust", "identity"] license = "Apache-2.0" -readme = "../README.md" +readme = "./README.md" repository = "https://github.com/iotaledger/identity.rs" rust-version = "1.62" description = "An IOTA Ledger integration for the IOTA DID Method." diff --git a/identity_iota_core/src/document/iota_document.rs b/identity_iota_core/src/document/iota_document.rs index ab5f5194c9..c4b65082e2 100644 --- a/identity_iota_core/src/document/iota_document.rs +++ b/identity_iota_core/src/document/iota_document.rs @@ -267,7 +267,7 @@ impl IotaDocument { /// Serializes the document for inclusion in an Alias Output's state metadata /// with the default [`StateMetadataEncoding`]. pub fn pack(self) -> Result> { - self.pack_with_encoding(StateMetadataEncoding::Json) + self.pack_with_encoding(StateMetadataEncoding::default()) } /// Serializes the document for inclusion in an Alias Output's state metadata. diff --git a/identity_iota_core/src/error.rs b/identity_iota_core/src/error.rs index 08f5542732..9f3b72b814 100644 --- a/identity_iota_core/src/error.rs +++ b/identity_iota_core/src/error.rs @@ -7,7 +7,7 @@ pub type Result = core::result::Result; #[non_exhaustive] pub enum Error { #[error("serialization error")] - SerializationError(#[source] identity_core::Error), + SerializationError(&'static str, #[source] Option), #[error("{0}")] DIDSyntaxError(#[from] identity_did::did::DIDError), #[error("{0}")] diff --git a/identity_iota_core/src/state_metadata/document.rs b/identity_iota_core/src/state_metadata/document.rs index 696f7104e0..011456bde6 100644 --- a/identity_iota_core/src/state_metadata/document.rs +++ b/identity_iota_core/src/state_metadata/document.rs @@ -60,20 +60,22 @@ impl StateMetadataDocument { /// an Alias Output's state metadata, according to the given `encoding`. pub fn pack(self, encoding: StateMetadataEncoding) -> Result> { let encoded_message_data: Vec = match encoding { - StateMetadataEncoding::Json => self.to_json_vec().map_err(Error::SerializationError)?, + StateMetadataEncoding::Json => self + .to_json_vec() + .map_err(|err| Error::SerializationError("failed to serialize document to JSON", Some(err)))?, }; - // Prepend flags. + // Prepend flags and length. let encoded_message_data_with_flags = - add_flags_to_message(encoded_message_data, StateMetadataVersion::CURRENT, encoding); + add_flags_to_message(encoded_message_data, StateMetadataVersion::CURRENT, encoding)?; Ok(encoded_message_data_with_flags) } /// Unpack bytes into a [`StateMetadataDocument`]. pub fn unpack(data: &[u8]) -> Result { // Check marker. - let marker: &[u8] = data.get(0..3).ok_or(identity_did::Error::InvalidDocument( - "unpack expected data to have at least length 3", + let marker: &[u8] = data.get(0..=2).ok_or(identity_did::Error::InvalidDocument( + "state metadata decoding: expected DID marker at offset [0..=2]", None, ))?; if marker != DID_MARKER { @@ -82,7 +84,7 @@ impl StateMetadataDocument { // Check version. let version: StateMetadataVersion = StateMetadataVersion::try_from(*data.get(3).ok_or( - identity_did::Error::InvalidDocument("expected data to have at least length 4", None), + identity_did::Error::InvalidDocument("state metadata decoding: expected version at offset 3", None), )?)?; if version != StateMetadataVersion::V1 { return Err(Error::InvalidStateMetadata("unsupported version")); @@ -90,29 +92,56 @@ impl StateMetadataDocument { // Decode data. let encoding: StateMetadataEncoding = StateMetadataEncoding::try_from(*data.get(4).ok_or( - identity_did::Error::InvalidDocument("expected data to have at least length 5", None), + identity_did::Error::InvalidDocument("state metadata decoding: expected encoding at offset 4", None), )?)?; - let inner: &[u8] = data.get(5..).ok_or(identity_did::Error::InvalidDocument( - "expected data to have at least length 6", - None, - ))?; + let data_len_packed: [u8; 2] = data + .get(5..=6) + .ok_or(identity_did::Error::InvalidDocument( + "state metadata decoding: expected data length at offset [5..=6]", + None, + ))? + .try_into() + .map_err(|_| { + identity_did::Error::InvalidDocument("state metadata decoding: data length conversion error", None) + })?; + let data_len: u16 = u16::from_le_bytes(data_len_packed); + + let data: &[u8] = data + .get(7..(7 + data_len as usize)) + .ok_or(identity_did::Error::InvalidDocument( + "state metadata decoding: encoded document shorter than length prefix", + None, + ))?; match encoding { - StateMetadataEncoding::Json => StateMetadataDocument::from_json_slice(inner).map_err(Error::SerializationError), + StateMetadataEncoding::Json => StateMetadataDocument::from_json_slice(data).map_err(|err| { + Error::SerializationError( + "state metadata decoding: failed to deserialize JSON document", + Some(err), + ) + }), } } } /// Prepends the message flags and marker magic bytes to the data in the following order: -/// `[marker, version, encoding, data]`. -fn add_flags_to_message(mut data: Vec, version: StateMetadataVersion, encoding: StateMetadataEncoding) -> Vec { - let mut buffer: Vec = Vec::with_capacity(1 + DID_MARKER.len() + 1 + data.len()); +/// `[marker, version, encoding, data length, data]`. +fn add_flags_to_message( + mut data: Vec, + version: StateMetadataVersion, + encoding: StateMetadataEncoding, +) -> Result> { + let data_len: u16 = + u16::try_from(data.len()).map_err(|_| Error::SerializationError("failed to convert usize to u16", None))?; + let data_len_packed: [u8; 2] = data_len.to_le_bytes(); + let mut buffer: Vec = Vec::with_capacity(DID_MARKER.len() + 1 + 1 + data_len_packed.len() + data_len as usize); buffer.extend_from_slice(DID_MARKER); buffer.push(version as u8); buffer.push(encoding as u8); + buffer.extend_from_slice(&data_len_packed); buffer.append(&mut data); - buffer + Ok(buffer) } impl From for StateMetadataDocument { @@ -309,6 +338,10 @@ mod tests { let TestSetup { document, .. } = test_document(); let state_metadata_doc: StateMetadataDocument = StateMetadataDocument::from(document); let packed: Vec = state_metadata_doc.clone().pack(StateMetadataEncoding::Json).unwrap(); + let expected_payload: String = format!( + "{{\"doc\":{},\"meta\":{}}}", + state_metadata_doc.document, state_metadata_doc.metadata + ); // DID marker. assert_eq!(&packed[0..3], DID_MARKER); @@ -316,14 +349,51 @@ mod tests { assert_eq!(packed[3], StateMetadataVersion::V1 as u8); // Encoding. assert_eq!(packed[4], StateMetadataEncoding::Json as u8); - // JSON payload. + // JSON length. assert_eq!( - &packed[5..], - format!( - "{{\"doc\":{},\"meta\":{}}}", - state_metadata_doc.document, state_metadata_doc.metadata - ) - .as_bytes() + &packed[5..=6], + (expected_payload.as_bytes().len() as u16).to_le_bytes().as_ref() ); + // JSON payload. + assert_eq!(&packed[7..], expected_payload.as_bytes()); + } + + #[test] + fn test_unpack_length_prefix() { + // Changing the serialization is a breaking change! + let TestSetup { document, .. } = test_document(); + let state_metadata_doc: StateMetadataDocument = StateMetadataDocument::from(document); + let mut packed: Vec = state_metadata_doc.clone().pack(StateMetadataEncoding::Json).unwrap(); + let original_length = u16::from_le_bytes(packed[5..=6].try_into().unwrap()); + + // INVALID: length is too long. + let longer = (original_length + 1_u16).to_le_bytes(); + packed[5] = longer[0]; + packed[6] = longer[1]; + assert!(StateMetadataDocument::unpack(&packed).is_err()); + + // INVALID: length is too long. + let max: [u8; 2] = u16::MAX.to_le_bytes(); + packed[5] = max[0]; + packed[6] = max[1]; + assert!(StateMetadataDocument::unpack(&packed).is_err()); + + // INVALID: length is too short (JSON deserialization fails). + let shorter = (original_length - 1_u16).to_le_bytes(); + packed[5] = shorter[0]; + packed[6] = shorter[1]; + assert!(StateMetadataDocument::unpack(&packed).is_err()); + + // INVALID: length is too short (JSON deserialization fails). + let min = 0_u16.to_le_bytes(); + packed[5] = min[0]; + packed[6] = min[1]; + assert!(StateMetadataDocument::unpack(&packed).is_err()); + + // VALID: length is just right. + let original = original_length.to_le_bytes(); + packed[5] = original[0]; + packed[6] = original[1]; + assert_eq!(StateMetadataDocument::unpack(&packed).unwrap(), state_metadata_doc); } } diff --git a/identity_iota_core/src/state_metadata/encoding.rs b/identity_iota_core/src/state_metadata/encoding.rs index 80e5c8f6b1..9c7dd4b863 100644 --- a/identity_iota_core/src/state_metadata/encoding.rs +++ b/identity_iota_core/src/state_metadata/encoding.rs @@ -6,8 +6,9 @@ use num_traits::FromPrimitive; use crate::Error; /// Indicates the encoding of a DID document in state metadata. -#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, num_derive::FromPrimitive)] +#[derive(Copy, Clone, Debug, Default, Ord, PartialOrd, Eq, PartialEq, num_derive::FromPrimitive)] pub enum StateMetadataEncoding { + #[default] Json = 0, } From af570b4ddc4fa410e3a843866ed14191e78c4e19 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Tue, 13 Sep 2022 10:26:35 +0200 Subject: [PATCH 56/89] Stardust Private Tangle Example (#1001) * Add private_tangle example * Add private tangle Readme description * Fix 3_did_issues_tokens description * Combine create_did and private_tangle examples * rustfmt 0_create_did * Address review comments * Use `API_ENDPOINT` everywhere for consistency * Address more review comments * Don't split address & funds requesting --- examples/0_basic/0_create_did.rs | 37 +++++++++++++--------- examples/0_basic/1_update_did.rs | 4 +-- examples/0_basic/2_resolve_did.rs | 4 +-- examples/0_basic/3_deactivate_did.rs | 4 +-- examples/0_basic/4_delete_did.rs | 4 +-- examples/1_advanced/0_did_controls_did.rs | 4 +-- examples/1_advanced/1_did_issues_nft.rs | 4 +-- examples/1_advanced/2_nft_owns_did.rs | 7 ++-- examples/1_advanced/3_did_issues_tokens.rs | 8 ++--- examples/1_advanced/4_key_exchange.rs | 7 ++-- examples/README.md | 2 +- examples/utils/utils.rs | 32 +++++++++++++------ 12 files changed, 70 insertions(+), 47 deletions(-) diff --git a/examples/0_basic/0_create_did.rs b/examples/0_basic/0_create_did.rs index 581da20b99..44bdb2f358 100644 --- a/examples/0_basic/0_create_did.rs +++ b/examples/0_basic/0_create_did.rs @@ -1,17 +1,8 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use anyhow::Context; -use iota_client::block::address::Address; -use iota_client::block::output::AliasOutput; -use iota_client::secret::stronghold::StrongholdSecretManager; -use iota_client::secret::SecretManager; -use iota_client::Client; - use examples::get_address_with_funds; use examples::random_stronghold_path; -use examples::NETWORK_ENDPOINT; -use identity_iota::core::ToJson; use identity_iota::crypto::KeyPair; use identity_iota::crypto::KeyType; use identity_iota::did::MethodScope; @@ -20,12 +11,31 @@ use identity_iota::iota::IotaDocument; use identity_iota::iota::IotaIdentityClientExt; use identity_iota::iota::IotaVerificationMethod; use identity_iota::iota::NetworkName; +use iota_client::block::address::Address; +use iota_client::block::output::AliasOutput; +use iota_client::secret::stronghold::StrongholdSecretManager; +use iota_client::secret::SecretManager; +use iota_client::Client; /// Demonstrates how to create a DID Document and publish it in a new Alias Output. +/// +/// In this example we connect to the Shimmer testnet, but it can be adapted +/// to run on a private network by setting the network and faucet endpoints. +/// +/// See the following instructions on running your own private network +/// https://wiki.iota.org/hornet/develop/how_tos/private_tangle #[tokio::main] async fn main() -> anyhow::Result<()> { + // The API endpoint of an IOTA node, e.g. Hornet. + // For a local Hornet node, this would usually be `http://127.0.0.1:14265`. + let api_endpoint: &str = "https://api.testnet.shimmer.network/"; + + // The faucet endpoint allows requesting funds for testing purposes. + // For a local Hornet node, this would usually be `http://127.0.0.1:8091/api/enqueue`. + let faucet_endpoint: &str = "https://faucet.testnet.shimmer.network/api/enqueue"; + // Create a new client to interact with the IOTA ledger. - let client: Client = Client::builder().with_primary_node(NETWORK_ENDPOINT, None)?.finish()?; + let client: Client = Client::builder().with_primary_node(api_endpoint, None)?.finish()?; // Create a new secret manager backed by a Stronghold. let mut secret_manager: SecretManager = SecretManager::Stronghold( @@ -34,10 +44,8 @@ async fn main() -> anyhow::Result<()> { .build(random_stronghold_path())?, ); - // Get an address and with funds for testing. - let address: Address = get_address_with_funds(&client, &mut secret_manager) - .await - .context("failed to get address with funds")?; + // Get an address with funds for testing. + let address: Address = get_address_with_funds(&client, &mut secret_manager, faucet_endpoint).await?; // Get the Bech32 human-readable part (HRP) of the network. let network_name: NetworkName = client.network_name().await?; @@ -55,7 +63,6 @@ async fn main() -> anyhow::Result<()> { // Construct an Alias Output containing the DID document, with the wallet address // set as both the state controller and governor. let alias_output: AliasOutput = client.new_did_output(address, document, None).await?; - println!("Alias Output: {}", alias_output.to_json()?); // Publish the Alias Output and get the published DID document. let document: IotaDocument = client.publish_did_output(&secret_manager, alias_output).await?; diff --git a/examples/0_basic/1_update_did.rs b/examples/0_basic/1_update_did.rs index feaa4fc35a..0278b34d62 100644 --- a/examples/0_basic/1_update_did.rs +++ b/examples/0_basic/1_update_did.rs @@ -3,7 +3,7 @@ use examples::create_did; use examples::random_stronghold_path; -use examples::NETWORK_ENDPOINT; +use examples::API_ENDPOINT; use identity_iota::core::json; use identity_iota::core::FromJson; use identity_iota::core::Timestamp; @@ -27,7 +27,7 @@ use iota_client::Client; #[tokio::main] async fn main() -> anyhow::Result<()> { // Create a new client to interact with the IOTA ledger. - let client: Client = Client::builder().with_primary_node(NETWORK_ENDPOINT, None)?.finish()?; + let client: Client = Client::builder().with_primary_node(API_ENDPOINT, None)?.finish()?; // Create a new secret manager backed by a Stronghold. let mut secret_manager: SecretManager = SecretManager::Stronghold( diff --git a/examples/0_basic/2_resolve_did.rs b/examples/0_basic/2_resolve_did.rs index 9c501c5296..407d44cdb9 100644 --- a/examples/0_basic/2_resolve_did.rs +++ b/examples/0_basic/2_resolve_did.rs @@ -3,7 +3,7 @@ use examples::create_did; use examples::random_stronghold_path; -use examples::NETWORK_ENDPOINT; +use examples::API_ENDPOINT; use identity_iota::iota::block::address::Address; use identity_iota::iota::IotaDID; use identity_iota::iota::IotaDocument; @@ -18,7 +18,7 @@ use iota_client::Client; #[tokio::main] async fn main() -> anyhow::Result<()> { // Create a new client to interact with the IOTA ledger. - let client: Client = Client::builder().with_primary_node(NETWORK_ENDPOINT, None)?.finish()?; + let client: Client = Client::builder().with_primary_node(API_ENDPOINT, None)?.finish()?; // Create a new secret manager backed by a Stronghold. let mut secret_manager: SecretManager = SecretManager::Stronghold( diff --git a/examples/0_basic/3_deactivate_did.rs b/examples/0_basic/3_deactivate_did.rs index 00e2fbaf16..6792b866c7 100644 --- a/examples/0_basic/3_deactivate_did.rs +++ b/examples/0_basic/3_deactivate_did.rs @@ -3,7 +3,7 @@ use examples::create_did; use examples::random_stronghold_path; -use examples::NETWORK_ENDPOINT; +use examples::API_ENDPOINT; use identity_iota::iota::block::address::Address; use identity_iota::iota::IotaClientExt; use identity_iota::iota::IotaDID; @@ -19,7 +19,7 @@ use iota_client::Client; #[tokio::main] async fn main() -> anyhow::Result<()> { // Create a new client to interact with the IOTA ledger. - let client: Client = Client::builder().with_primary_node(NETWORK_ENDPOINT, None)?.finish()?; + let client: Client = Client::builder().with_primary_node(API_ENDPOINT, None)?.finish()?; // Create a new secret manager backed by a Stronghold. let mut secret_manager: SecretManager = SecretManager::Stronghold( diff --git a/examples/0_basic/4_delete_did.rs b/examples/0_basic/4_delete_did.rs index b2a52e5de3..72a6dfc5d6 100644 --- a/examples/0_basic/4_delete_did.rs +++ b/examples/0_basic/4_delete_did.rs @@ -3,7 +3,7 @@ use examples::create_did; use examples::random_stronghold_path; -use examples::NETWORK_ENDPOINT; +use examples::API_ENDPOINT; use identity_iota::iota::Error; use identity_iota::iota::IotaClientExt; use identity_iota::iota::IotaDID; @@ -17,7 +17,7 @@ use iota_client::Client; #[tokio::main] async fn main() -> anyhow::Result<()> { // Create a new client to interact with the IOTA ledger. - let client: Client = Client::builder().with_primary_node(NETWORK_ENDPOINT, None)?.finish()?; + let client: Client = Client::builder().with_primary_node(API_ENDPOINT, None)?.finish()?; // Create a new secret manager backed by a Stronghold. let mut secret_manager: SecretManager = SecretManager::Stronghold( diff --git a/examples/1_advanced/0_did_controls_did.rs b/examples/1_advanced/0_did_controls_did.rs index 6896635684..1f8da40465 100644 --- a/examples/1_advanced/0_did_controls_did.rs +++ b/examples/1_advanced/0_did_controls_did.rs @@ -5,7 +5,7 @@ use std::ops::Deref; use examples::create_did; use examples::random_stronghold_path; -use examples::NETWORK_ENDPOINT; +use examples::API_ENDPOINT; use identity_iota::crypto::KeyPair; use identity_iota::crypto::KeyType; use identity_iota::did::MethodScope; @@ -37,7 +37,7 @@ async fn main() -> anyhow::Result<()> { // ======================================================== // Create a new client to interact with the IOTA ledger. - let client: Client = Client::builder().with_primary_node(NETWORK_ENDPOINT, None)?.finish()?; + let client: Client = Client::builder().with_primary_node(API_ENDPOINT, None)?.finish()?; // Create a new secret manager backed by a Stronghold. let mut secret_manager: SecretManager = SecretManager::Stronghold( diff --git a/examples/1_advanced/1_did_issues_nft.rs b/examples/1_advanced/1_did_issues_nft.rs index 230f76ce42..1ce84523fe 100644 --- a/examples/1_advanced/1_did_issues_nft.rs +++ b/examples/1_advanced/1_did_issues_nft.rs @@ -3,7 +3,7 @@ use examples::create_did; use examples::random_stronghold_path; -use examples::NETWORK_ENDPOINT; +use examples::API_ENDPOINT; use identity_iota::iota::block::output::feature::MetadataFeature; use identity_iota::iota::IotaDID; use identity_iota::iota::IotaDocument; @@ -42,7 +42,7 @@ async fn main() -> anyhow::Result<()> { // ============================================== // Create a new client to interact with the IOTA ledger. - let client: Client = Client::builder().with_primary_node(NETWORK_ENDPOINT, None)?.finish()?; + let client: Client = Client::builder().with_primary_node(API_ENDPOINT, None)?.finish()?; // Create a new secret manager backed by a Stronghold. let mut secret_manager: SecretManager = SecretManager::Stronghold( diff --git a/examples/1_advanced/2_nft_owns_did.rs b/examples/1_advanced/2_nft_owns_did.rs index 7f934b7757..d474dab761 100644 --- a/examples/1_advanced/2_nft_owns_did.rs +++ b/examples/1_advanced/2_nft_owns_did.rs @@ -4,7 +4,8 @@ use examples::create_did_document; use examples::get_address_with_funds; use examples::random_stronghold_path; -use examples::NETWORK_ENDPOINT; +use examples::API_ENDPOINT; +use examples::FAUCET_ENDPOINT; use identity_iota::iota::block::address::NftAddress; use identity_iota::iota::block::output::AliasOutput; use identity_iota::iota::IotaClientExt; @@ -40,7 +41,7 @@ async fn main() -> anyhow::Result<()> { // ============================= // Create a new client to interact with the IOTA ledger. - let client: Client = Client::builder().with_primary_node(NETWORK_ENDPOINT, None)?.finish()?; + let client: Client = Client::builder().with_primary_node(API_ENDPOINT, None)?.finish()?; // Create a new secret manager backed by a Stronghold. let mut secret_manager: SecretManager = SecretManager::Stronghold( @@ -50,7 +51,7 @@ async fn main() -> anyhow::Result<()> { ); // Get an address with funds for testing. - let address: Address = get_address_with_funds(&client, &mut secret_manager).await?; + let address: Address = get_address_with_funds(&client, &mut secret_manager, FAUCET_ENDPOINT).await?; // Get the current byte cost. let rent_structure: RentStructure = client.get_rent_structure().await?; diff --git a/examples/1_advanced/3_did_issues_tokens.rs b/examples/1_advanced/3_did_issues_tokens.rs index d0601e2ab9..ed931d648b 100644 --- a/examples/1_advanced/3_did_issues_tokens.rs +++ b/examples/1_advanced/3_did_issues_tokens.rs @@ -6,7 +6,7 @@ use std::ops::Deref; use examples::create_did; use examples::get_address; use examples::random_stronghold_path; -use examples::NETWORK_ENDPOINT; +use examples::API_ENDPOINT; use identity_iota::core::Duration; use identity_iota::core::Timestamp; use identity_iota::iota::block::output::unlock_condition::AddressUnlockCondition; @@ -41,8 +41,8 @@ use iota_client::secret::SecretManager; use iota_client::Client; use primitive_types::U256; -/// Demonstrates how an identity can issue and control native assets -/// such as Token Foundries and NFTs. +/// Demonstrates how an identity can issue and control +/// a Token Foundry and its tokens. /// /// For this example, we consider the case where an authority issues /// carbon credits that can be used to pay for carbon emissions or traded on a marketplace. @@ -53,7 +53,7 @@ async fn main() -> anyhow::Result<()> { // =========================================== // Create a new client to interact with the IOTA ledger. - let client: Client = Client::builder().with_primary_node(NETWORK_ENDPOINT, None)?.finish()?; + let client: Client = Client::builder().with_primary_node(API_ENDPOINT, None)?.finish()?; // Create a new secret manager backed by a Stronghold. let mut secret_manager: SecretManager = SecretManager::Stronghold( diff --git a/examples/1_advanced/4_key_exchange.rs b/examples/1_advanced/4_key_exchange.rs index 230e2378cd..0062c64ad4 100644 --- a/examples/1_advanced/4_key_exchange.rs +++ b/examples/1_advanced/4_key_exchange.rs @@ -4,7 +4,8 @@ use anyhow::Context; use examples::get_address_with_funds; use examples::random_stronghold_path; -use examples::NETWORK_ENDPOINT; +use examples::API_ENDPOINT; +use examples::FAUCET_ENDPOINT; use identity_iota::crypto::KeyPair; use identity_iota::crypto::KeyType; use identity_iota::crypto::X25519; @@ -34,7 +35,7 @@ async fn main() -> anyhow::Result<()> { // ============================== // Create a new client to interact with the IOTA ledger. - let client: Client = Client::builder().with_primary_node(NETWORK_ENDPOINT, None)?.finish()?; + let client: Client = Client::builder().with_primary_node(API_ENDPOINT, None)?.finish()?; // Create a new secret manager backed by a Stronghold. let mut secret_manager: SecretManager = SecretManager::Stronghold( @@ -44,7 +45,7 @@ async fn main() -> anyhow::Result<()> { ); // Get an address and with funds for testing. - let address: Address = get_address_with_funds(&client, &mut secret_manager) + let address: Address = get_address_with_funds(&client, &mut secret_manager, FAUCET_ENDPOINT) .await .context("failed to get address with funds")?; diff --git a/examples/README.md b/examples/README.md index 8ed43604d1..94b3ea3c45 100644 --- a/examples/README.md +++ b/examples/README.md @@ -37,5 +37,5 @@ The following advanced examples are available: | [0_did_controls_did](./1_advanced/0_did_controls_did.rs) | Demonstrates how an identity can control another identity. | | [1_did_issues_nft](./1_advanced/1_did_issues_nft.rs) | Demonstrates how an identity can issue and own NFTs, and how observers can verify the issuer of the NFT. | | [2_nft_owns_did](./1_advanced/2_nft_owns_did.rs) | Demonstrates how an identity can be owned by NFTs, and how observers can verify that relationship. | -| [3_did_issues_tokens](./1_advanced/3_did_issues_tokens.rs) | Demonstrates how an identity can issue and control native assets such as Token Foundries and NFTs. | +| [3_did_issues_tokens](./1_advanced/3_did_issues_tokens.rs) | Demonstrates how an identity can issue and control a Token Foundry and its tokens. | | [4_key_exchange](./1_advanced/4_key_exchange.rs) | Demonstrates Elliptic-curve Diffie-Hellman (ECDH) cryptographic key exchange with DID Documents. | diff --git a/examples/utils/utils.rs b/examples/utils/utils.rs index 79f6870c6b..55a047bebd 100644 --- a/examples/utils/utils.rs +++ b/examples/utils/utils.rs @@ -21,15 +21,15 @@ use iota_client::secret::SecretManager; use iota_client::Client; use rand::distributions::DistString; -pub static NETWORK_ENDPOINT: &str = "https://api.testnet.shimmer.network/"; -pub static FAUCET_URL: &str = "https://faucet.testnet.shimmer.network/api/enqueue"; +pub static API_ENDPOINT: &str = "https://api.testnet.shimmer.network/"; +pub static FAUCET_ENDPOINT: &str = "https://faucet.testnet.shimmer.network/api/enqueue"; /// Creates a DID Document and publishes it in a new Alias Output. /// /// Its functionality is equivalent to the "create DID" example /// and exists for convenient calling from the other examples. pub async fn create_did(client: &Client, secret_manager: &mut SecretManager) -> anyhow::Result<(Address, IotaDID)> { - let address: Address = get_address_with_funds(client, secret_manager) + let address: Address = get_address_with_funds(client, secret_manager, FAUCET_ENDPOINT) .await .context("failed to get address with funds")?; @@ -62,12 +62,21 @@ pub fn create_did_document(network_name: &NetworkName) -> anyhow::Result anyhow::Result
{ +pub async fn get_address_with_funds( + client: &Client, + stronghold: &mut SecretManager, + faucet_endpoint: &str, +) -> anyhow::Result
{ let address: Address = get_address(client, stronghold).await?; - request_faucet_funds(client, address, client.get_bech32_hrp().await?.as_str()) - .await - .context("failed to request faucet funds")?; + request_faucet_funds( + client, + address, + client.get_bech32_hrp().await?.as_str(), + faucet_endpoint, + ) + .await + .context("failed to request faucet funds")?; Ok(address) } @@ -96,10 +105,15 @@ pub async fn get_address(client: &Client, secret_manager: &mut SecretManager) -> } /// Requests funds from the testnet faucet for the given `address`. -async fn request_faucet_funds(client: &Client, address: Address, network_hrp: &str) -> anyhow::Result<()> { +async fn request_faucet_funds( + client: &Client, + address: Address, + network_hrp: &str, + faucet_endpoint: &str, +) -> anyhow::Result<()> { let address_bech32 = address.to_bech32(network_hrp); - iota_client::request_funds_from_faucet(FAUCET_URL, &address_bech32).await?; + iota_client::request_funds_from_faucet(faucet_endpoint, &address_bech32).await?; tokio::time::timeout(std::time::Duration::from_secs(45), async { loop { From 3c9ffe9e04672204b1df2320e38011e1c8ca8844 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Tue, 13 Sep 2022 13:19:35 +0200 Subject: [PATCH 57/89] Support case insensitive serialization (#1012) --- bindings/wasm/src/iota/identity_client_ext.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/bindings/wasm/src/iota/identity_client_ext.rs b/bindings/wasm/src/iota/identity_client_ext.rs index 7ee818955f..4bdb1300cd 100644 --- a/bindings/wasm/src/iota/identity_client_ext.rs +++ b/bindings/wasm/src/iota/identity_client_ext.rs @@ -1,6 +1,7 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use bee_block::output::RentStructureBuilder; use identity_iota::iota::block::address::dto::AddressDto; use identity_iota::iota::block::address::Address; use identity_iota::iota::block::output::dto::AliasOutputDto; @@ -71,8 +72,14 @@ impl WasmIotaIdentityClientExt { let doc: IotaDocument = document.0.clone(); let promise: Promise = future_to_promise(async move { - let rent_structure: Option = - rentStructure.map(|rent| rent.into_serde()).transpose().wasm_result()?; + let rent_structure: Option = rentStructure + .map(|rent| { + rent + .into_serde::() + .map(RentStructureBuilder::finish) + }) + .transpose() + .wasm_result()?; let output: AliasOutput = IotaIdentityClientExt::new_did_output(&client, address, doc, rent_structure) .await From ec0955c62243d1dbb3bdd45769b86c7868247668 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Ha=C3=9F?= Date: Tue, 13 Sep 2022 16:52:44 +0200 Subject: [PATCH 58/89] Allow pre-releases in CI (#996) --- .../actions/release/bump-versions/action.yml | 41 ++++++++++------- .../rust-automatic-release-and-publish.yml | 4 +- .github/workflows/rust-create-release-pr.yml | 9 ++-- .github/workflows/shared-release.yml | 44 +++++++++++-------- .../wasm-automatic-release-and-publish.yml | 8 ++-- .github/workflows/wasm-create-release-pr.yml | 13 +++--- 6 files changed, 71 insertions(+), 48 deletions(-) diff --git a/.github/actions/release/bump-versions/action.yml b/.github/actions/release/bump-versions/action.yml index 0243bed1ac..426266dd0a 100644 --- a/.github/actions/release/bump-versions/action.yml +++ b/.github/actions/release/bump-versions/action.yml @@ -31,22 +31,6 @@ runs: command: install args: -f --no-default-features --features "add set-version" --version ^0.8 cargo-edit - - name: Replace identity_iota version in Wasm bindings - shell: bash - if: ${{inputs.release-target == 'rust'}} - working-directory: bindings/wasm - run: | - cargo add identity_iota@=${{ inputs.version }} --path=../../identity_iota - - - name: Replace identity versions in stronghold nodejs bindings - shell: bash - if: ${{inputs.release-target == 'rust'}} - working-directory: bindings/stronghold-nodejs - run: | - cargo add identity_core@=${{ inputs.version }} --path=../../identity_core - cargo add identity_iota_core_legacy@=${{ inputs.version }} --path=../../identity_iota_core_legacy - cargo add identity_account_storage@=${{ inputs.version }} --path=../../identity_account_storage - - name: Bump Rust crate versions shell: bash if: ${{inputs.release-target == 'rust'}} @@ -61,6 +45,14 @@ runs: run: | cargo set-version ${{ inputs.version }} + # cargo workspaces ignores examples_legacy/ but cargo release still tries to version it during publishing. + - name: Bump Rust examples_legacy version + shell: bash + if: ${{inputs.release-target == 'rust'}} + working-directory: examples_legacy + run: | + cargo set-version ${{ inputs.version }} + - name: Bump Wasm bindings crate version shell: bash if: ${{inputs.release-target == 'wasm'}} @@ -75,6 +67,23 @@ runs: run: | cargo set-version ${{ inputs.version }} + - name: Replace identity_iota version in Wasm bindings + shell: bash + if: ${{inputs.release-target == 'rust'}} + working-directory: bindings/wasm + run: | + cargo add identity_iota --path=../../identity_iota + + - name: Replace identity versions in stronghold nodejs bindings + shell: bash + if: ${{inputs.release-target == 'rust'}} + working-directory: bindings/stronghold-nodejs + run: | + cargo add identity_core --path=../../identity_core + cargo add identity_did --path=../../identity_did + cargo add identity_iota_core_legacy --path=../../identity_iota_core_legacy + cargo add identity_account_storage --path=../../identity_account_storage + - name: Set up Node.js uses: actions/setup-node@v2 if: ${{inputs.release-target == 'wasm'}} diff --git a/.github/workflows/rust-automatic-release-and-publish.yml b/.github/workflows/rust-automatic-release-and-publish.yml index 4d16525e0c..f0da799f3a 100644 --- a/.github/workflows/rust-automatic-release-and-publish.yml +++ b/.github/workflows/rust-automatic-release-and-publish.yml @@ -14,8 +14,8 @@ jobs: uses: iotaledger/identity.rs/.github/workflows/shared-release.yml@dev with: changelog-config-path: ./.github/.github_changelog_generator - dev-tag-regex: ^v[0-9]+\.[0-9]+\.[0-9]+-(dev)\.\d*$ - main-tag-regex: ^v[0-9]+\.[0-9]+\.[0-9]+$ + pre-release-tag-regex: ^v[0-9]+\.[0-9]+\.[0-9]+-(?\w+)\.\d+$ + main-release-tag-regex: ^v[0-9]+\.[0-9]+\.[0-9]+$ create-github-release: true secrets: GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} diff --git a/.github/workflows/rust-create-release-pr.yml b/.github/workflows/rust-create-release-pr.yml index 055e5ca9fe..b5d0247d5e 100644 --- a/.github/workflows/rust-create-release-pr.yml +++ b/.github/workflows/rust-create-release-pr.yml @@ -7,21 +7,24 @@ on: description: 'Version to release Rust under (e.g. `1.2.3`)' required: true release-type: - description: Create a `dev` or `main` release. If `dev`, a `dev` postfix and auto-incrementing number will be added automatically (e.g. `1.2.3-dev.x`)' + description: Create a `main` or a pre-release. If anything other then `main` is selected, a postfix and auto-incrementing number will be added automatically (e.g. `1.2.3-dev.x`)' type: choice required: true options: + - alpha + - beta + - rc - dev - main jobs: create-dev-release-pr: - if: github.event.inputs.release-type == 'dev' + if: github.event.inputs.release-type != 'main' # owner/repository of workflow has to be static, see https://github.community/t/env-variables-in-uses/17466 uses: iotaledger/identity.rs/.github/workflows/shared-create-dev-release-pr.yml@dev with: tag-prefix: v - tag-postfix: -dev. + tag-postfix: -${{ github.event.inputs.release-type }}. tag-base: ${{ github.event.inputs.version }} main-tag-regex: ^v[0-9]+\.[0-9]+\.[0-9]+$ changelog-config-path: ./.github/.github_changelog_generator diff --git a/.github/workflows/shared-release.yml b/.github/workflows/shared-release.yml index ee220c1008..6abc32940f 100644 --- a/.github/workflows/shared-release.yml +++ b/.github/workflows/shared-release.yml @@ -19,27 +19,30 @@ on: description: "path to the changelog config" required: true type: string - main-tag-regex: + main-release-tag-regex: description: "the regex to find all related main releases" required: true type: string - dev-tag-regex: - description: "the regex to find all related dev releases" + pre-release-tag-regex: + description: "the regex to find all related pre-releases. Must include a named capture group 'pre_release', which captures the pre-release version (e.g. 'alpha', 'beta', etc.)" required: true type: string outputs: is-release: description: "is release" value: ${{ jobs.build.outputs.is-release }} - is-dev-release: - description: "is dev release" - value: ${{ jobs.build.outputs.is-dev-release }} + is-pre-release: + description: "is pre-release" + value: ${{ jobs.build.outputs.is-pre-release }} previous-version: - description: "tag of the previous version" + description: "tag of the previous version (e.g. '0.5.0')" value: ${{ jobs.build.outputs.previous-version }} current-version: - description: "tag of the current version" + description: "tag of the current version (e.g. '0.6.0')" value: ${{ jobs.build.outputs.current-version }} + pre-release-identifier: + description: "identifier of the pre-release (e.g. 'alpha', 'beta', etc.)" + value: ${{ jobs.build.outputs.pre-release-identifier }} secrets: GPG_PRIVATE_KEY: description: "GPG private key for signing commits and tags" @@ -53,7 +56,8 @@ jobs: runs-on: ubuntu-latest outputs: is-release: ${{ steps.determine-version.outputs.is-release }} - is-dev-release: ${{ steps.determine-version.outputs.is-dev-release }} + is-pre-release: ${{ steps.determine-version.outputs.is-pre-release }} + pre-release-identifier: ${{ steps.determine-version.outputs.pre-release-identifier }} previous-version: ${{ steps.determine-version.outputs.previous-version }} current-version: ${{ steps.determine-version.outputs.current-version }} steps: @@ -68,18 +72,19 @@ jobs: run: | BRANCHNAME=${{ github.head_ref }} CURRENT_VERSION=${BRANCHNAME##*/} - if ! [[ $(echo $CURRENT_VERSION | grep -o -P '${{ inputs.dev-tag-regex }}') || $(echo $CURRENT_VERSION | grep -o -P '${{ inputs.main-tag-regex }}') ]]; then + if ! [[ $(echo $CURRENT_VERSION | grep -o -P '${{ inputs.pre-release-tag-regex }}') || $(echo $CURRENT_VERSION | grep -o -P '${{ inputs.main-release-tag-regex }}') ]]; then exit 0 fi IS_RELEASE=true echo IS_RELEASE=$IS_RELEASE echo IS_RELEASE=$IS_RELEASE >> $GITHUB_ENV echo "::set-output name=is-release::$IS_RELEASE" - if [[ $(echo $CURRENT_VERSION | grep -w -P '${{ inputs.dev-tag-regex }}') ]]; then - IS_DEV_RELEASE=true - PREVIOUS_VERSION=$(git tag -l --sort=-version:refname --merged $(git log -n 1 refs/remotes/origin/$(git rev-parse --abbrev-ref HEAD) --pretty=format:"%H") | grep -w -P '${{ inputs.dev-tag-regex }}' | head -n 1) + if [[ $(echo $CURRENT_VERSION | grep -w -P '${{ inputs.pre-release-tag-regex }}') ]]; then + IS_PRE_RELEASE=true + PRE_RELEASE_IDENTIFIER=$(echo $CURRENT_VERSION | perl -pe '/${{ inputs.pre-release-tag-regex }}/; $_ = "$+{pre_release}\n"') + PREVIOUS_VERSION=$(git tag -l --sort=-version:refname --merged $(git log -n 1 refs/remotes/origin/$(git rev-parse --abbrev-ref HEAD) --pretty=format:"%H") | grep -w -P '${{ inputs.pre-release-tag-regex }}' | head -n 1) else - PREVIOUS_VERSION=$(git tag -l --sort=-version:refname --merged $(git log -n 1 refs/remotes/origin/$(git rev-parse --abbrev-ref HEAD) --pretty=format:"%H") | grep -w -P '${{ inputs.main-tag-regex }}' | head -n 1) + PREVIOUS_VERSION=$(git tag -l --sort=-version:refname --merged $(git log -n 1 refs/remotes/origin/$(git rev-parse --abbrev-ref HEAD) --pretty=format:"%H") | grep -w -P '${{ inputs.main-release-tag-regex }}' | head -n 1) fi # create a list of tags that are unrelated to the current release @@ -100,9 +105,12 @@ jobs: EXCLUDE_ARG=$FIRST$SECOND fi - echo IS_DEV_RELEASE=$IS_DEV_RELEASE - echo IS_DEV_RELEASE=$IS_DEV_RELEASE >> $GITHUB_ENV - echo "::set-output name=is-dev-release::$IS_DEV_RELEASE" + echo IS_PRE_RELEASE=$IS_PRE_RELEASE + echo IS_PRE_RELEASE=$IS_PRE_RELEASE >> $GITHUB_ENV + echo "::set-output name=is-pre-release::$IS_PRE_RELEASE" + echo PRE_RELEASE_IDENTIFIER=$PRE_RELEASE_IDENTIFIER + echo PRE_RELEASE_IDENTIFIER=$PRE_RELEASE_IDENTIFIER >> $GITHUB_ENV + echo "::set-output name=pre-release-identifier::$PRE_RELEASE_IDENTIFIER" echo CURRENT_VERSION=$CURRENT_VERSION echo CURRENT_VERSION=$CURRENT_VERSION >> $GITHUB_ENV echo "::set-output name=current-version::$CURRENT_VERSION" @@ -148,6 +156,6 @@ jobs: uses: softprops/action-gh-release@1e07f4398721186383de40550babbdf2b84acfc5 with: body_path: RELEASE_CHANGELOG.md - prerelease: ${{env.IS_DEV_RELEASE}} + prerelease: ${{env.IS_PRE_RELEASE}} tag_name: ${{env.CURRENT_VERSION}} diff --git a/.github/workflows/wasm-automatic-release-and-publish.yml b/.github/workflows/wasm-automatic-release-and-publish.yml index be48a4042c..0f914e8f75 100644 --- a/.github/workflows/wasm-automatic-release-and-publish.yml +++ b/.github/workflows/wasm-automatic-release-and-publish.yml @@ -15,8 +15,8 @@ jobs: uses: iotaledger/identity.rs/.github/workflows/shared-release.yml@dev with: changelog-config-path: ./bindings/wasm/.github_changelog_generator - dev-tag-regex: ^wasm-v[0-9]+\.[0-9]+\.[0-9]+-(dev)\.\d*$ - main-tag-regex: ^wasm-v[0-9]+\.[0-9]+\.[0-9]+$ + pre-release-tag-regex: ^wasm-v[0-9]+\.[0-9]+\.[0-9]+-(?\w+)\.\d+$ + main-release-tag-regex: ^wasm-v[0-9]+\.[0-9]+\.[0-9]+$ create-github-release: false secrets: GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} @@ -42,7 +42,7 @@ jobs: with: input-artifact-name: identity-wasm-bindings-build npm-token: ${{ secrets.NPM_TOKEN }} - tag: ${{ needs.call-create-release-workflow.outputs.is-dev-release && 'dev' }} + tag: ${{ needs.call-create-release-workflow.outputs.is-pre-release && needs.call-create-release-workflow.outputs.pre-release-identifier }} # build-stronghold-nodejs: # needs: build-wasm @@ -66,5 +66,5 @@ jobs: # uses: './.github/actions/publish/publish-stronghold-nodejs' # with: # npm-token: ${{ secrets.NPM_TOKEN }} - # tag: ${{ needs.call-create-release-workflow.outputs.is-dev-release && 'dev' }} + # tag: ${{ needs.call-create-release-workflow.outputs.is-pre-release && needs.call-create-release-workflow.outputs.pre-release-identifier }} # input-artifact-name: identity-stronghold-nodejs-bindings-build diff --git a/.github/workflows/wasm-create-release-pr.yml b/.github/workflows/wasm-create-release-pr.yml index 409f26e3ec..f02dce3053 100644 --- a/.github/workflows/wasm-create-release-pr.yml +++ b/.github/workflows/wasm-create-release-pr.yml @@ -7,26 +7,29 @@ on: description: 'Version to release Wasm under (e.g. `1.2.3`)' required: true release-type: - description: Create a `dev` or `main` release. If `dev`, a `dev` postfix and auto-incrementing number will be added automatically (e.g. `1.2.3-dev.x`)' + description: Create a `main` or a pre-release. If anything other then `main` is selected, a postfix and auto-incrementing number will be added automatically (e.g. `1.2.3-dev.x`)' type: choice required: true - options: + options: + - alpha + - beta + - rc - dev - main jobs: create-dev-release-pr: - if: github.event.inputs.release-type == 'dev' + if: github.event.inputs.release-type != 'main' # owner/repository of workflow has to be static, see https://github.community/t/env-variables-in-uses/17466 uses: iotaledger/identity.rs/.github/workflows/shared-create-dev-release-pr.yml@dev with: tag-prefix: wasm-v - tag-postfix: -dev. + tag-postfix: -${{ github.event.inputs.release-type }}. tag-base: ${{ github.event.inputs.version }} main-tag-regex: ^wasm-v[0-9]+\.[0-9]+\.[0-9]+$ changelog-config-path: ./bindings/wasm/.github_changelog_generator changelog-path: ./bindings/wasm/CHANGELOG.md - pr-body-text: On merge a `dev` release will be published to npm. + pr-body-text: On merge a pre-release will be published to npm. release-target: wasm secrets: GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} From 8e6212e322533a36262658727d978431c200c6e9 Mon Sep 17 00:00:00 2001 From: Henrique Nogara Date: Tue, 13 Sep 2022 11:53:04 -0300 Subject: [PATCH 59/89] Add alias output history example (#998) * Add alias output history example * Fix clippy warnings * Run cargo fmt * Improve comments; Match AliasOutput id with expected AliasId * Add API_Endpoint import --- examples/1_advanced/5_alias_output_history.rs | 170 ++++++++++++++++++ examples/Cargo.toml | 4 + examples/README.md | 1 + 3 files changed, 175 insertions(+) create mode 100644 examples/1_advanced/5_alias_output_history.rs diff --git a/examples/1_advanced/5_alias_output_history.rs b/examples/1_advanced/5_alias_output_history.rs new file mode 100644 index 0000000000..e2616a6bbd --- /dev/null +++ b/examples/1_advanced/5_alias_output_history.rs @@ -0,0 +1,170 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::str::FromStr; + +use anyhow::Context; +use examples::create_did; +use examples::random_stronghold_path; +use examples::API_ENDPOINT; +use identity_iota::core::json; +use identity_iota::core::FromJson; +use identity_iota::core::Timestamp; +use identity_iota::did::MethodRelationship; +use identity_iota::did::Service; +use identity_iota::did::DID; +use identity_iota::iota::block::address::Address; +use identity_iota::iota::block::output::RentStructure; +use identity_iota::iota::IotaClientExt; +use identity_iota::iota::IotaDID; +use identity_iota::iota::IotaDocument; +use identity_iota::iota::IotaIdentityClient; +use identity_iota::iota::IotaIdentityClientExt; +use identity_iota::iota::IotaService; +use iota_client::api_types::responses::OutputMetadataResponse; +use iota_client::block::input::Input; +use iota_client::block::output::AliasId; +use iota_client::block::output::AliasOutput; +use iota_client::block::output::AliasOutputBuilder; +use iota_client::block::output::Output; +use iota_client::block::output::OutputId; +use iota_client::block::payload::transaction::TransactionEssence; +use iota_client::block::payload::Payload; +use iota_client::block::Block; +use iota_client::block::BlockId; +use iota_client::secret::stronghold::StrongholdSecretManager; +use iota_client::secret::SecretManager; +use iota_client::Client; + +/// Demonstrates how to obtain the alias output history. +#[tokio::main] +async fn main() -> anyhow::Result<()> { + // Create a new client to interact with the IOTA ledger. + // NOTE: a permanode is required to fetch older output histories. + let client: Client = Client::builder().with_primary_node(API_ENDPOINT, None)?.finish()?; + + // Create a new secret manager backed by a Stronghold. + let mut secret_manager: SecretManager = SecretManager::Stronghold( + StrongholdSecretManager::builder() + .password("secure_password") + .build(random_stronghold_path())?, + ); + + // Create a new DID in an Alias Output for us to modify. + let (_, did): (Address, IotaDID) = create_did(&client, &mut secret_manager).await?; + + // Resolve the latest state of the document. + let mut document: IotaDocument = client.resolve_did(&did).await?; + + // Attach a new method relationship to the existing method. + document.attach_method_relationship( + &document.id().to_url().join("#key-1")?, + MethodRelationship::Authentication, + )?; + + // Adding multiple services. + let services = [ + json!({"id": document.id().to_url().join("#my-service-0")?, "type": "MyService", "serviceEndpoint": "https://iota.org/"}), + ]; + for service in services { + let service: IotaService = Service::from_json_value(service)?; + assert!(document.insert_service(service)); + document.metadata.updated = Some(Timestamp::now_utc()); + + // Increase the storage deposit and publish the update. + let alias_output: AliasOutput = client.update_did_output(document.clone()).await?; + let rent_structure: RentStructure = client.get_rent_structure().await?; + let alias_output: AliasOutput = AliasOutputBuilder::from(&alias_output) + .with_minimum_storage_deposit(rent_structure) + .finish()?; + client.publish_did_output(&secret_manager, alias_output).await?; + } + + // ==================================== + // Retrieving the Alias Output History + // ==================================== + let mut alias_history: Vec = Vec::new(); + + // Step 0 - Get the latest Alias Output + let alias_id: AliasId = AliasId::from(client.resolve_did(&did).await?.id()); + let (mut output_id, mut alias_output): (OutputId, AliasOutput) = client.get_alias_output(alias_id).await?; + + while alias_output.state_index() != 0 { + // Step 1 - Get the current block + let block: Block = current_block(&client, &output_id).await?; + // Step 2 - Get the OutputId of the previous block + output_id = previous_output_id(&block)?; + // Step 3 - Get the Alias Output from the block + alias_output = block_alias_output(&block, &alias_id)?; + alias_history.push(alias_output.clone()); + } + + println!("Alias History: {:?}", alias_history); + + Ok(()) +} + +async fn current_block(client: &Client, output_id: &OutputId) -> anyhow::Result { + let output_metadata: OutputMetadataResponse = client.get_output_metadata(output_id).await?; + let block_id: BlockId = BlockId::from_str(&output_metadata.block_id)?; + let block: Block = client.get_block(&block_id).await?; + Ok(block) +} + +fn previous_output_id(block: &Block) -> anyhow::Result { + match block + .payload() + .context("expected a transaction payload, but no payload was found")? + { + Payload::Transaction(transaction_payload) => match transaction_payload.essence() { + TransactionEssence::Regular(regular_transaction_essence) => { + match regular_transaction_essence + .inputs() + .first() + .context("expected an utxo for the block, but no input was found")? + { + Input::Utxo(utxo_input) => Ok(*utxo_input.output_id()), + Input::Treasury(_) => { + anyhow::bail!("expected an utxo input, found a treasury input"); + } + } + } + }, + Payload::Milestone(_) | Payload::TreasuryTransaction(_) | Payload::TaggedData(_) => { + anyhow::bail!("expected a transaction payload"); + } + } +} + +fn block_alias_output(block: &Block, alias_id: &AliasId) -> anyhow::Result { + match block + .payload() + .context("expected a transaction payload, but no payload was found")? + { + Payload::Transaction(transaction_payload) => match transaction_payload.essence() { + TransactionEssence::Regular(regular_transaction_essence) => { + for (index, output) in regular_transaction_essence.outputs().iter().enumerate() { + match output { + Output::Alias(alias_output) => { + if &alias_output.alias_id().or_from_output_id( + OutputId::new( + transaction_payload.id(), + index.try_into().context("output index must fit into a u16")?, + ) + .context("failed to create OutputId")?, + ) == alias_id + { + return Ok(alias_output.clone()); + } + } + Output::Basic(_) | Output::Foundry(_) | Output::Nft(_) | Output::Treasury(_) => continue, + } + } + } + }, + Payload::Milestone(_) | Payload::TreasuryTransaction(_) | Payload::TaggedData(_) => { + anyhow::bail!("expected a transaction payload"); + } + } + anyhow::bail!("no alias output has been found"); +} diff --git a/examples/Cargo.toml b/examples/Cargo.toml index f6073d2923..6b57c7acc0 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -55,3 +55,7 @@ name = "3_did_issues_tokens" [[example]] path = "1_advanced/4_key_exchange.rs" name = "4_key_exchange" + +[[example]] +path = "1_advanced/5_alias_output_history.rs" +name = "5_alias_output_history" diff --git a/examples/README.md b/examples/README.md index 94b3ea3c45..570bb40bbf 100644 --- a/examples/README.md +++ b/examples/README.md @@ -39,3 +39,4 @@ The following advanced examples are available: | [2_nft_owns_did](./1_advanced/2_nft_owns_did.rs) | Demonstrates how an identity can be owned by NFTs, and how observers can verify that relationship. | | [3_did_issues_tokens](./1_advanced/3_did_issues_tokens.rs) | Demonstrates how an identity can issue and control a Token Foundry and its tokens. | | [4_key_exchange](./1_advanced/4_key_exchange.rs) | Demonstrates Elliptic-curve Diffie-Hellman (ECDH) cryptographic key exchange with DID Documents. | +| [5_alias_output_history](./1_advanced/5_alias_output_history.rs) | Demonstrates fetching the history of an Alias Output. | From 24594e35eb022b1cf132de47ef80fc5a02f7295c Mon Sep 17 00:00:00 2001 From: Abdulrahim Al Methiab <31316147+abdulmth@users.noreply.github.com> Date: Tue, 13 Sep 2022 22:20:12 +0200 Subject: [PATCH 60/89] Update `update` wiki page (#1008) --- bindings/wasm/examples/src/ex1_update_did.ts | 15 +- .../decentralized_identifiers/update.mdx | 494 +++++++++--------- examples/0_basic/1_update_did.rs | 20 +- 3 files changed, 278 insertions(+), 251 deletions(-) diff --git a/bindings/wasm/examples/src/ex1_update_did.ts b/bindings/wasm/examples/src/ex1_update_did.ts index ff0a5cb3c3..bad987fca0 100644 --- a/bindings/wasm/examples/src/ex1_update_did.ts +++ b/bindings/wasm/examples/src/ex1_update_did.ts @@ -1,7 +1,7 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { MethodRelationship, IotaDocument, IotaService, Timestamp } from '../../node'; +import { MethodRelationship, IotaDocument, IotaService, Timestamp, IotaVerificationMethod, KeyPair, KeyType, MethodScope } from '../../node'; import { IAliasOutput, IRent, TransactionHelper } from '@iota/iota.js'; import { createIdentity } from "./ex0_create_did"; @@ -15,8 +15,13 @@ export async function updateIdentity() { // Technically this is equivalent to the document above. const document: IotaDocument = await didClient.resolveDid(did); - // Attach a new method relationship to the existing method. - document.attachMethodRelationship(did.join("#key-1"), MethodRelationship.Authentication); + // Insert a new Ed25519 verification method in the DID document. + let keypair = new KeyPair(KeyType.Ed25519); + let method = new IotaVerificationMethod(document.id(), keypair.type(), keypair.public(), "#key-2"); + document.insertMethod(method, MethodScope.VerificationMethod()); + + // Attach a new method relationship to the inserted method. + document.attachMethodRelationship(did.join("#key-2"), MethodRelationship.Authentication); // Add a new Service. const service: IotaService = new IotaService({ @@ -27,6 +32,10 @@ export async function updateIdentity() { document.insertService(service); document.setMetadataUpdated(Timestamp.nowUTC()); + // Remove a verification method. + let originalMethod = document.resolveMethod("key-1") as IotaVerificationMethod; + document.removeMethod(originalMethod?.id()); + // Resolve the latest output and update it with the given document. const aliasOutput: IAliasOutput = await didClient.updateDidOutput(document); diff --git a/documentation/docs/concepts/decentralized_identifiers/update.mdx b/documentation/docs/concepts/decentralized_identifiers/update.mdx index 91bd8e2d48..f75db1ea36 100644 --- a/documentation/docs/concepts/decentralized_identifiers/update.mdx +++ b/documentation/docs/concepts/decentralized_identifiers/update.mdx @@ -4,405 +4,407 @@ sidebar_label: Update description: How DID Documents can be manipulated and how updates should be published image: /img/Identity_icon.png keywords: -- Documents -- DID -- Tangle -- Update -- Publish + - Documents + - DID + - Tangle + - Update + - Publish --- -import account_manipulate_did_rs from '!!raw-loader!../../../../examples_legacy/account/manipulate_did.rs'; -import CodeSnippet from '../../../src/components/CodeSnippetComponent' -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -import CodeBlock from '@theme/CodeBlock'; +import update_did_rust from "!!raw-loader!../../../../examples/0_basic/1_update_did.rs"; +import update_did_node from "!!raw-loader!../../../../bindings/wasm/examples/src/ex1_update_did.ts"; +import CodeSnippet from "../../../src/components/CodeSnippetComponent"; +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import CodeBlock from "@theme/CodeBlock"; -DID Documents can be extended by adding [Verification Methods](https://www.w3.org/TR/did-core/#verification-methods), [Services](https://www.w3.org/TR/did-core/#services) and custom properties. +DID Documents can be extended by adding [verification methods](#verification-methods), [services](#services) and custom properties. A verification method adds public keys, which can be used to digitally sign things like a DID message or a verifiable credential, while a service can provide metadata around the identity via URIs. ### Verification Methods -As demonstrated by the [example](#example) below, the Iota identity framework offers easy-to-use methods for adding verification methods. + +As demonstrated by the [example](#example) below, the IOTA Identity framework offers easy-to-use methods for adding [verification methods](https://www.w3.org/TR/did-core/#verification-methods). The following properties can be specified for a verification method: - **id**: a [DID URL](https://www.w3.org/TR/did-core/#did-url-syntax) for the verification method. It can be specified by setting the [fragment](https://www.w3.org/TR/did-core/#fragment); -- **type**: specifies the type of the Verification Method. The framework supports `Ed25519` and `X25519` key types. This property is automatically filled by the framework when specifying the verification material. +- **type**: specifies the type of the verification method. The framework supports `Ed25519` and `X25519` key types. This property is automatically filled by the framework when specifying the verification material. - **publicKeyMultibase**: multibase encoded public key which concludes the [verification material](https://www.w3.org/TR/did-core/#verification-material). This can be automatically generated by the framework or manually provided by users. - ### Verification Relationships + [Verification relationships](https://www.w3.org/TR/did-core/#verification-relationships) express the relationship between the DID subject and the verification method. It can be used to specify the - the purpose of the verification method. +the purpose of the verification method. The following relationships are supported by the Identity Framework: + - **[Authentication](https://www.w3.org/TR/did-core/#authentication)**: used to specify authentication methods for the DID subject. - **[Assertion](https://www.w3.org/TR/did-core/#assertion)**: can be used for verifiable credential verification. - **[Key Agreement](https://www.w3.org/TR/did-core/#assertion)**: used for establishing secure communication channels. - **[Capability Invocation](https://www.w3.org/TR/did-core/#capability-invocation)**: can be used to authorize updates to the DID Document. - **[Capability Delegation](https://www.w3.org/TR/did-core/#capability-delegation)**: a mechanism to delegate cryptographic capability to another party. -Verification methods can be either [embedded or referenced](https://www.w3.org/TR/did-core/#example-14-embedding-and-referencing-verification-methods). Referencing verification +Verification methods can be either [embedded or referenced](https://www.w3.org/TR/did-core/#referring-to-verification-methods). Referencing verification methods allow them to be used by more than one verification relationship. Upon creating a verification method using the identity framework, specifying the `MethodScope` option will result in an embedded verification method. Leaving that option unset will create the verification method as a map entry of the `verificationMethod` property. Verification relationships can be added afterwards using references. -:::warning +:::note -Any update to the DID document must be signed using a verification method with `capability invocation` relationship to be valid. Removing all capability invocation verification methods -disallows any further updates to the document. +Updates to the DID Document are done through a state transition of the [Alias Output](../../specs/did/iota_did_method_spec#alias-output) by its state controller. The public key or address of the state controller does not need to be a verification method in the DID Document, since it is defined in the containing Alias Output. ::: ### Services + [Services](https://www.w3.org/TR/did-core/#services) allow adding other ways of communicating with the DID subject. An endpoint included in the DID Document can offer a way of reaching services for different purposes like authentication, communicating, and discovery. The following properties can be specified for a service: -- **id**: a [DID URL](https://www.w3.org/TR/did-core/#did-url-syntax) for referecing the service in the DID document. -It can be specified by setting the [fragment](https://www.w3.org/TR/did-core/#fragment). +- **id**: a [DID URL](https://www.w3.org/TR/did-core/#did-url-syntax) for referencing the service in the DID document. + It can be specified by setting the [fragment](https://www.w3.org/TR/did-core/#fragment). - **type**: a string used to maximize interoperability between services. The framework does not perform any checks on the content of this string. - **serviceEndpoint**: a URL that points to the service endpoint. ## Example -The following example demonstrates adding verification methods and services to a DID Document. +The following example demonstrates managing verification methods and services in a DID Document. - -### Creating Identity +### Creating Identity -The Example above starts by [creating an identity using the account](./create.mdx). +The example above starts by [creating an identity](./create.mdx). ```rust -let mut account: Account = Account::builder() - .storage(stronghold) - .create_identity(IdentitySetup::default()) - .await?; +// Create a new client to interact with the IOTA ledger. +let client: Client = Client::builder().with_primary_node(NETWORK_ENDPOINT, None)?.finish()?; + +// Create a new secret manager backed by a Stronghold. +let mut secret_manager: SecretManager = SecretManager::Stronghold( +StrongholdSecretManager::builder() + .password("secure_password") + .build(random_stronghold_path())?, +); + +// Create a new DID in an Alias Output for us to modify. +let (_, did): (Address, StardustDID) = create_did(&client, &mut secret_manager).await?; + ``` ```js -let builder = new AccountBuilder({ - storage, -}); -let account = await builder.createIdentity(); +const {didClient, secretManager, did} = await createIdentity(); ``` + -This will create a DID document and publish it to the tangle. +This creates and publishes an Alias Output containing a DID Document with one verification method. ```json { - "doc":{ - "id":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf", - "capabilityInvocation":[ - { - "id":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf#sign-0", - "controller":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf", - "type":"Ed25519VerificationKey2018", - "publicKeyMultibase":"z5k7vzMVuXXj8MJDcfzP2owvc8xKBA6BBsAkFf1GSNu2X" - } - ] - }, - "meta":{ - "created":"2022-04-13T09:27:48Z", - "updated":"2022-04-13T09:27:48Z" - } + "doc": { + "id": "did:iota:rms:0x6fdcc441ab461aaee2ec1837ea5068fe2bc643a9ac0729a055ef5df42a762483", + "verificationMethod": [ + { + "id": "did:iota:rms:0x6fdcc441ab461aaee2ec1837ea5068fe2bc643a9ac0729a055ef5df42a762483#key-1", + "controller": "did:iota:rms:0x6fdcc441ab461aaee2ec1837ea5068fe2bc643a9ac0729a055ef5df42a762483", + "type": "Ed25519VerificationKey2018", + "publicKeyMultibase": "z94fP8Vo6qJtejpycjUcYtiSTbLGuCNYUTrjQX9hT2gSv" + } + ] + }, + "meta": { + "created": "2022-09-12T21:58:06Z", + "updated": "2022-09-12T21:58:06Z" + } } ``` -The created document only contains one verification method with [capabilityInvocation](https://www.w3.org/TR/did-core/#capability-invocation) relationship. -This method is used to sign the DID Document for publication to the Tangle. -The signature proves that the publisher of the document is in control over the capability invocation keys and is allowed to create, update or delete the DID Document. - -Any future updates to the DID Document in this example will be signed using this verification method. The Account will automatically sign each update with this method so individual -updates don't have to be explicitly signed. - -Furthermore, it's possible to rotate a capability Invocation key. In this case, the Account will sign next update with a key which was valid in the previous state of the DID Document. Afterwards it will - use the first (oldest) of the remaining capability invocation keys as a default signing method. -Other capability invocation keys can still be explicitly specified to sign an update. These can be set in `PublishOptions`. - -Note that the Account does not allow removing all capability invocation keys. - - -### Adding Verification Methods - -Another verification method can be added to the DID document using the Account: +### Adding Verification Method ```rust -account - .update_identity() - .create_method() - .content(methodcontent::generateed25519) - .fragment("my-next-key") - .apply() - .await?; + // Insert a new Ed25519 verification method in the DID document. + let keypair: KeyPair = KeyPair::new(KeyType::Ed25519)?; + let method: IotaVerificationMethod = + IotaVerificationMethod::new(document.id().clone(), keypair.type_(), keypair.public(), "#key-2")?; + document.insert_method(method, MethodScope::VerificationMethod)?; ``` ```js -await account.createMethod({ - content: MethodContent.GenerateEd25519(), - fragment: "my-next-key" -}) +// Insert a new Ed25519 verification method in the DID document. +let keypair = new KeyPair(KeyType.Ed25519); +let method = new IotaVerificationMethod(document.id(), keypair.type(), keypair.public(), "#key-2"); +document.insertMethod(method, MethodScope.VerificationMethod()); ``` + -The code above creates a new verification method that includes a newly generated Ed25519 public key, -signs the updated document using the private key of the default `capabilityInvocation` verification method -and publishes the document to the tangle. - -Since the `MethodScope` is not specified, the verification method will be created in the `verificationMethod` map. The updated DID Document will look as follows: - +This creates a new verification method that includes a newly generated Ed25519 public key. ```json { - "doc":{ - "id":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf", - "verificationMethod":[ - { - "id":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf#my-next-key", - "controller":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf", - "type":"Ed25519VerificationKey2018", - "publicKeyMultibase":"z2Zthec5siTfxCjPwZUHGDGybKNy9oc3ZYeftvEE2nEL3" - } - ], - "capabilityInvocation":[ - { - "id":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf#sign-0", - "controller":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf", - "type":"Ed25519VerificationKey2018", - "publicKeyMultibase":"z5k7vzMVuXXj8MJDcfzP2owvc8xKBA6BBsAkFf1GSNu2X" - } - ] - }, - "meta":{ - "created":"2022-04-13T09:27:48Z", - "updated":"2022-04-13T09:28:06Z" - } + "doc": { + "id": "did:iota:rms:0x6fdcc441ab461aaee2ec1837ea5068fe2bc643a9ac0729a055ef5df42a762483", + "verificationMethod": [ + { + "id": "did:iota:rms:0x6fdcc441ab461aaee2ec1837ea5068fe2bc643a9ac0729a055ef5df42a762483#key-1", + "controller": "did:iota:rms:0x6fdcc441ab461aaee2ec1837ea5068fe2bc643a9ac0729a055ef5df42a762483", + "type": "Ed25519VerificationKey2018", + "publicKeyMultibase": "z94fP8Vo6qJtejpycjUcYtiSTbLGuCNYUTrjQX9hT2gSv" + }, + { + "id": "did:iota:rms:0x6fdcc441ab461aaee2ec1837ea5068fe2bc643a9ac0729a055ef5df42a762483#key-2", + "controller": "did:iota:rms:0x6fdcc441ab461aaee2ec1837ea5068fe2bc643a9ac0729a055ef5df42a762483", + "type": "Ed25519VerificationKey2018", + "publicKeyMultibase": "zHiCj7kbZdWznNvBhqxXwgoEChYseKrArLFdi5kPKAVRq" + } + ] + }, + "meta": { + "created": "2022-09-12T21:58:06Z", + "updated": "2022-09-12T21:58:06Z" + } } ``` - +Notice that these changes to the document are not published yet. This will be done in a later stage. ### Adding Verification Relationships + Verification relationship can be attached to a verification method by referencing its fragment. ```rust -account - .update_identity() - .attach_method_relationship() - .fragment("my-next-key") - .relationships(vec![ - MethodRelationship::CapabilityDelegation, - MethodRelationship::CapabilityInvocation, - ]) - .apply() - .await?; +// Attach a new method relationship to the existing method. +document.attach_method_relationship( + &document.id().to_url().join("#key-1")?, + MethodRelationship::Authentication, +)?; ``` - ```js -await account.attachMethodRelationships({ - fragment: "my-next-key", - relationships: [ - MethodRelationship.CapabilityDelegation, - MethodRelationship.CapabilityInvocation - ] -}) +// Attach a new method relationship to the existing method. +document.attachMethodRelationship(did.join("#key-1"), MethodRelationship.Authentication); ``` + -This will add `CapabilityDelegation` and `CapabilityInvocation` relationships to the created verification method with the fragment `my-next-key`. The `capabilityInvocation` -property now has both an embedded and a referenced verification method. +This will add `Authentication` relationship to the verification method with the fragment `key-1`. Note that `Authentication` references the already included verification method: ```json { - "doc":{ - "id":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf", - "verificationMethod":[ - { - "id":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf#my-next-key", - "controller":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf", - "type":"Ed25519VerificationKey2018", - "publicKeyMultibase":"z2Zthec5siTfxCjPwZUHGDGybKNy9oc3ZYeftvEE2nEL3" - } - ], - "capabilityDelegation":[ - "did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf#my-next-key" - ], - "capabilityInvocation":[ - { - "id":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf#sign-0", - "controller":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf", - "type":"Ed25519VerificationKey2018", - "publicKeyMultibase":"z5k7vzMVuXXj8MJDcfzP2owvc8xKBA6BBsAkFf1GSNu2X" - }, - "did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf#my-next-key" - ] - }, - "meta":{ - "created":"2022-04-13T09:27:48Z", - "updated":"2022-04-13T09:28:23Z" - } + "doc": { + "id": "did:iota:rms:0x6fdcc441ab461aaee2ec1837ea5068fe2bc643a9ac0729a055ef5df42a762483", + "verificationMethod": [ + { + "id": "did:iota:rms:0x6fdcc441ab461aaee2ec1837ea5068fe2bc643a9ac0729a055ef5df42a762483#key-1", + "controller": "did:iota:rms:0x6fdcc441ab461aaee2ec1837ea5068fe2bc643a9ac0729a055ef5df42a762483", + "type": "Ed25519VerificationKey2018", + "publicKeyMultibase": "z94fP8Vo6qJtejpycjUcYtiSTbLGuCNYUTrjQX9hT2gSv" + }, + { + "id": "did:iota:rms:0x6fdcc441ab461aaee2ec1837ea5068fe2bc643a9ac0729a055ef5df42a762483#key-2", + "controller": "did:iota:rms:0x6fdcc441ab461aaee2ec1837ea5068fe2bc643a9ac0729a055ef5df42a762483", + "type": "Ed25519VerificationKey2018", + "publicKeyMultibase": "zHiCj7kbZdWznNvBhqxXwgoEChYseKrArLFdi5kPKAVRq" + } + ], + "authentication": ["did:iota:rms:0x6fdcc441ab461aaee2ec1837ea5068fe2bc643a9ac0729a055ef5df42a762483#key-2"] + }, + "meta": { + "created": "2022-09-12T21:58:06Z", + "updated": "2022-09-12T21:58:06Z" + } } ``` ### Adding a Service -Similar to verification methods, services can be added to a DID Document. + ```rust -account - .update_identity() - .create_service() - .fragment("my-service-1") - .type_("MyCustomService") - .endpoint(Url::parse("https://example.com")?) - .apply() - .await?; +// Add a new Service. +let service: StardustService = Service::from_json_value(json!({ + "id": document.id().to_url().join("#linked-domain")?, + "type": "LinkedDomains", + "serviceEndpoint": "https://iota.org/" +}))?; +assert!(document.insert_service(service)); +document.metadata.updated = Some(Timestamp::now_utc()); ``` ```js -await account.createService({ - fragment: "my-service-1", - type: "MyCustomService", - endpoint: "https://example.com" -}) +// Add a new Service. +const service: StardustService = new StardustService({ + id: did.join("#linked-domain"), + type: "LinkedDomains", + serviceEndpoint: "https://iota.org/" +}); +document.insertService(service); +document.setMetadataUpdated(Timestamp.nowUTC()); ``` + -In JavaScript, the endpoint property type is a string, this must be a valid URL, otherwise an error will be thrown. Additionally, custom properties can be added to a service by setting `properties` in both Rust and JavaScript. - The updated Document with the newly created service looks as follows. + ```json { - "doc":{ - "id":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf", - "verificationMethod":[ - { - "id":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf#my-next-key", - "controller":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf", - "type":"Ed25519VerificationKey2018", - "publicKeyMultibase":"z2Zthec5siTfxCjPwZUHGDGybKNy9oc3ZYeftvEE2nEL3" - } - ], - "capabilityDelegation":[ - "did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf#my-next-key" - ], - "capabilityInvocation":[ - { - "id":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf#sign-0", - "controller":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf", - "type":"Ed25519VerificationKey2018", - "publicKeyMultibase":"z5k7vzMVuXXj8MJDcfzP2owvc8xKBA6BBsAkFf1GSNu2X" - }, - "did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf#my-next-key" - ], - "service":[ - { - "id":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf#my-service-1", - "type":"MyCustomService", - "serviceEndpoint":"https://example.com/" - } - ] - }, - "meta":{ - "created":"2022-04-13T09:27:48Z", - "updated":"2022-04-13T09:28:34Z" - } + "doc": { + "id": "did:iota:rms:0x6fdcc441ab461aaee2ec1837ea5068fe2bc643a9ac0729a055ef5df42a762483", + "verificationMethod": [ + { + "id": "did:iota:rms:0x6fdcc441ab461aaee2ec1837ea5068fe2bc643a9ac0729a055ef5df42a762483#key-1", + "controller": "did:iota:rms:0x6fdcc441ab461aaee2ec1837ea5068fe2bc643a9ac0729a055ef5df42a762483", + "type": "Ed25519VerificationKey2018", + "publicKeyMultibase": "z94fP8Vo6qJtejpycjUcYtiSTbLGuCNYUTrjQX9hT2gSv" + }, + { + "id": "did:iota:rms:0x6fdcc441ab461aaee2ec1837ea5068fe2bc643a9ac0729a055ef5df42a762483#key-2", + "controller": "did:iota:rms:0x6fdcc441ab461aaee2ec1837ea5068fe2bc643a9ac0729a055ef5df42a762483", + "type": "Ed25519VerificationKey2018", + "publicKeyMultibase": "zHiCj7kbZdWznNvBhqxXwgoEChYseKrArLFdi5kPKAVRq" + } + ], + "authentication": ["did:iota:rms:0x6fdcc441ab461aaee2ec1837ea5068fe2bc643a9ac0729a055ef5df42a762483#key-2"], + "service": [ + { + "id": "did:iota:rms:0x6fdcc441ab461aaee2ec1837ea5068fe2bc643a9ac0729a055ef5df42a762483#linked-domain", + "type": "LinkedDomains", + "serviceEndpoint": "https://iota.org/" + } + ] + }, + "meta": { + "created": "2022-09-12T21:58:06Z", + "updated": "2022-09-12T21:58:19Z" + } } ``` -### Removing a Verification Method - -Verification methods and/or their relationships can be removed from the DID Document. The following code removes the verification method that we created previously. +### Removing Verification Method ```rust -account - .update_identity() - .delete_method() - .fragment("my-next-key") - .apply() - .await?; +// Remove a verification method. +let original_method: DIDUrl = document.resolve_method("key-1", None).unwrap().id().clone(); +document.remove_method(&original_method).unwrap(); ``` ```js -await account.deleteMethod({fragment: "my-next-key"}) +// Remove a verification method. +let originalMethod = document.resolveMethod("key-1") as IotaVerificationMethod; +document.removeMethod(originalMethod?.id()); ``` + +This removes the original verification method with the fragment `key-1`. + ```json { - "doc":{ - "id":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf", - "capabilityInvocation":[ - { - "id":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf#sign-0", - "controller":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf", - "type":"Ed25519VerificationKey2018", - "publicKeyMultibase":"z5k7vzMVuXXj8MJDcfzP2owvc8xKBA6BBsAkFf1GSNu2X" - } - ], - "service":[ - { - "id":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf#my-service-1", - "type":"MyCustomService", - "serviceEndpoint":"https://example.com/" - } - ] - }, - "meta":{ - "created":"2022-04-13T09:27:48Z", - "updated":"2022-04-13T09:29:03Z" - } + "doc": { + "id": "did:iota:rms:0x6fdcc441ab461aaee2ec1837ea5068fe2bc643a9ac0729a055ef5df42a762483", + "verificationMethod": [ + { + "id": "did:iota:rms:0x6fdcc441ab461aaee2ec1837ea5068fe2bc643a9ac0729a055ef5df42a762483#key-2", + "controller": "did:iota:rms:0x6fdcc441ab461aaee2ec1837ea5068fe2bc643a9ac0729a055ef5df42a762483", + "type": "Ed25519VerificationKey2018", + "publicKeyMultibase": "zHiCj7kbZdWznNvBhqxXwgoEChYseKrArLFdi5kPKAVRq" + } + ], + "authentication": ["did:iota:rms:0x6fdcc441ab461aaee2ec1837ea5068fe2bc643a9ac0729a055ef5df42a762483#key-2"], + "service": [ + { + "id": "did:iota:rms:0x6fdcc441ab461aaee2ec1837ea5068fe2bc643a9ac0729a055ef5df42a762483#linked-domain", + "type": "LinkedDomains", + "serviceEndpoint": "https://iota.org/" + } + ] + }, + "meta": { + "created": "2022-09-12T21:58:06Z", + "updated": "2022-09-12T21:58:19Z" + } } ``` -Notice that the `capabilityDelegation` and `verificationMethod` properties are also removed from the DID Document since they became empty after the only verification method they contained and referenced was removed. +### Publishing -Furthermore and similar to deleting verification methods, services can be deleted using `account.update_identity().delete_service()...` in Rust and `account.deleteService(..)` in JavaScript. +Publish the updated DID Document inside the Alias Output taking into account the increase in the storage deposit needed. -:::tip -In this example, a message is published to the tangle every time the document is updated. These messages can be unnecessary. Instead, one message can be published that contains all the updates to the DID Document. -See the [lazy example for Rust](https://github.com/iotaledger/identity.rs/blob/dev/examples_legacy/account/lazy.rs) and [lazy example for JS](https://github.com/iotaledger/identity.rs/blob/dev/bindings/wasm/examples-account/src/lazy.ts) to learn more about lazy publishing. -::: + + + +```rust +// Resolve the latest output and update it with the given document. +let alias_output: AliasOutput = client.update_did_output(document.clone()).await?; + +// Because the size of the DID document increased, we have to increase the allocated storage deposit. +// This increases the deposit amount to the new minimum. +let rent_structure: RentStructure = client.get_rent_structure().await?; +let alias_output: AliasOutput = AliasOutputBuilder::from(&alias_output) + .with_minimum_storage_deposit(rent_structure) + .finish()?; + +// Publish the updated Alias Output. +let updated: StardustDocument = client.publish_did_output(&secret_manager, alias_output).await?; +``` + + + + +```js +// Resolve the latest output and update it with the given document. +const aliasOutput: IAliasOutput = await didClient.updateDidOutput(document); + +// Because the size of the DID document increased, we have to increase the allocated storage deposit. +// This increases the deposit amount to the new minimum. +const rentStructure: IRent = await didClient.getRentStructure(); +aliasOutput.amount = TransactionHelper.getStorageDeposit(aliasOutput, rentStructure).toString(); + +// Publish the output. +const updated: StardustDocument = await didClient.publishDidOutput(secretManager, aliasOutput); +``` + + + diff --git a/examples/0_basic/1_update_did.rs b/examples/0_basic/1_update_did.rs index 0278b34d62..bb27f96d10 100644 --- a/examples/0_basic/1_update_did.rs +++ b/examples/0_basic/1_update_did.rs @@ -7,7 +7,12 @@ use examples::API_ENDPOINT; use identity_iota::core::json; use identity_iota::core::FromJson; use identity_iota::core::Timestamp; +use identity_iota::crypto::KeyPair; +use identity_iota::crypto::KeyType; +use identity_iota::did::DIDUrl; +use identity_iota::did::Document; use identity_iota::did::MethodRelationship; +use identity_iota::did::MethodScope; use identity_iota::did::Service; use identity_iota::did::DID; use identity_iota::iota::block::address::Address; @@ -17,6 +22,7 @@ use identity_iota::iota::IotaDID; use identity_iota::iota::IotaDocument; use identity_iota::iota::IotaIdentityClientExt; use identity_iota::iota::IotaService; +use identity_iota::iota::IotaVerificationMethod; use iota_client::block::output::AliasOutput; use iota_client::block::output::AliasOutputBuilder; use iota_client::secret::stronghold::StrongholdSecretManager; @@ -42,9 +48,15 @@ async fn main() -> anyhow::Result<()> { // Resolve the latest state of the document. let mut document: IotaDocument = client.resolve_did(&did).await?; - // Attach a new method relationship to the existing method. + // Insert a new Ed25519 verification method in the DID document. + let keypair: KeyPair = KeyPair::new(KeyType::Ed25519)?; + let method: IotaVerificationMethod = + IotaVerificationMethod::new(document.id().clone(), keypair.type_(), keypair.public(), "#key-2")?; + document.insert_method(method, MethodScope::VerificationMethod)?; + + // Attach a new method relationship to the inserted method. document.attach_method_relationship( - &document.id().to_url().join("#key-1")?, + &document.id().to_url().join("#key-2")?, MethodRelationship::Authentication, )?; @@ -57,6 +69,10 @@ async fn main() -> anyhow::Result<()> { assert!(document.insert_service(service)); document.metadata.updated = Some(Timestamp::now_utc()); + // Remove a verification method. + let original_method: DIDUrl = document.resolve_method("key-1", None).unwrap().id().clone(); + document.remove_method(&original_method).unwrap(); + // Resolve the latest output and update it with the given document. let alias_output: AliasOutput = client.update_did_output(document.clone()).await?; From b137d55d466cba2aede0158f5a5a2f4768d7073b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Ha=C3=9F?= Date: Wed, 14 Sep 2022 20:10:16 +0200 Subject: [PATCH 61/89] Add 0.7.0-alpha release blurb (#1016) --- .github/releases/v0.7.0-alpha.1.md | 6 ++++++ .github/releases/wasm-v0.7.0-alpha.1.md | 6 ++++++ 2 files changed, 12 insertions(+) create mode 100644 .github/releases/v0.7.0-alpha.1.md create mode 100644 .github/releases/wasm-v0.7.0-alpha.1.md diff --git a/.github/releases/v0.7.0-alpha.1.md b/.github/releases/v0.7.0-alpha.1.md new file mode 100644 index 0000000000..b1df0ecd55 --- /dev/null +++ b/.github/releases/v0.7.0-alpha.1.md @@ -0,0 +1,6 @@ +This version introduces a new DID method targeting the IOTA UTXO ledger. This method works fundamentally differently from the previous method and introduces new capabilities to interact with Layer 1 entities like native tokens, NFTs and smart contracts. +\n\n +This is an early alpha release, so there may be breaking changes in upcoming versions that invalidate DIDs created with this version. The method at this point is only intended for experimentation. +\n\n +Note: Identities created with the earlier versions cannot be resolved with this version of the library. +\n\n \ No newline at end of file diff --git a/.github/releases/wasm-v0.7.0-alpha.1.md b/.github/releases/wasm-v0.7.0-alpha.1.md new file mode 100644 index 0000000000..b1df0ecd55 --- /dev/null +++ b/.github/releases/wasm-v0.7.0-alpha.1.md @@ -0,0 +1,6 @@ +This version introduces a new DID method targeting the IOTA UTXO ledger. This method works fundamentally differently from the previous method and introduces new capabilities to interact with Layer 1 entities like native tokens, NFTs and smart contracts. +\n\n +This is an early alpha release, so there may be breaking changes in upcoming versions that invalidate DIDs created with this version. The method at this point is only intended for experimentation. +\n\n +Note: Identities created with the earlier versions cannot be resolved with this version of the library. +\n\n \ No newline at end of file From e45fc39d3227c46615e019de05ddda486b6c6162 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Ha=C3=9F?= Date: Thu, 15 Sep 2022 09:14:09 +0200 Subject: [PATCH 62/89] use iota scoped package (#1019) --- bindings/wasm/README.md | 8 +- bindings/wasm/examples/src/ex0_create_did.ts | 2 +- bindings/wasm/lib/tsconfig.json | 2 +- bindings/wasm/lib/tsconfig.web.json | 2 +- bindings/wasm/package-lock.json | 2467 +++++------------ bindings/wasm/package.json | 2 +- .../decentralized_identifiers/resolve.mdx | 4 +- 7 files changed, 757 insertions(+), 1730 deletions(-) diff --git a/bindings/wasm/README.md b/bindings/wasm/README.md index cf645de6c5..9cf93f1246 100644 --- a/bindings/wasm/README.md +++ b/bindings/wasm/README.md @@ -75,7 +75,7 @@ const { MethodRelationship, IotaIdentityClient, } = require('@iota/identity-wasm/node'); -const { Client } = require('@cycraig/iota-client-wasm/node'); +const { Client } = require('@iota/iota-client-wasm/node'); const API_ENDPOINT = "https://api.testnet.shimmer.network/"; @@ -182,7 +182,7 @@ import copy from "rollup-plugin-copy"; copy({ targets: [ { - src: "node_modules/@cycraig/iota-client-wasm/web/wasm/client_wasm_bg.wasm", + src: "node_modules/@iota/iota-client-wasm/web/wasm/client_wasm_bg.wasm", dest: "public", rename: "client_wasm_bg.wasm", }, @@ -212,7 +212,7 @@ const CopyWebPlugin= require('copy-webpack-plugin'); new CopyWebPlugin({ patterns: [ { - from: 'node_modules/@cycraig/iota-client-wasm/web/wasm/client_wasm_bg.wasm', + from: 'node_modules/@iota/iota-client-wasm/web/wasm/client_wasm_bg.wasm', to: 'client_wasm_bg.wasm' }, { @@ -226,7 +226,7 @@ new CopyWebPlugin({ ### Web Usage ```typescript -import * as client from "@cycraig/iota-client-wasm/web"; +import * as client from "@iota/iota-client-wasm/web"; import * as identity from "@iota/identity-wasm/web"; /** Demonstrate how to create a DID Document. */ diff --git a/bindings/wasm/examples/src/ex0_create_did.ts b/bindings/wasm/examples/src/ex0_create_did.ts index 3b8417e9a7..500a74ce38 100644 --- a/bindings/wasm/examples/src/ex0_create_did.ts +++ b/bindings/wasm/examples/src/ex0_create_did.ts @@ -13,7 +13,7 @@ import { import { Bech32Helper, IAliasOutput } from '@iota/iota.js'; import { Bip39 } from "@iota/crypto.js"; import fetch from "node-fetch"; -import { Client, MnemonicSecretManager, SecretManager } from "@cycraig/iota-client-wasm/node"; +import { Client, MnemonicSecretManager, SecretManager } from "@iota/iota-client-wasm/node"; const API_ENDPOINT = "https://api.testnet.shimmer.network/"; const FAUCET = "https://faucet.testnet.shimmer.network/api/enqueue"; diff --git a/bindings/wasm/lib/tsconfig.json b/bindings/wasm/lib/tsconfig.json index 808233bc3d..70888116e1 100644 --- a/bindings/wasm/lib/tsconfig.json +++ b/bindings/wasm/lib/tsconfig.json @@ -4,7 +4,7 @@ "baseUrl": "./", "paths": { "~identity_wasm": ["../node/identity_wasm"], - "~iota-client-wasm": ["../node_modules/@cycraig/iota-client-wasm/node"], + "~iota-client-wasm": ["../node_modules/@iota/iota-client-wasm/node"], }, "outDir": "../node", "declarationDir": "../node", diff --git a/bindings/wasm/lib/tsconfig.web.json b/bindings/wasm/lib/tsconfig.web.json index 43e10d9865..db862e23cc 100644 --- a/bindings/wasm/lib/tsconfig.web.json +++ b/bindings/wasm/lib/tsconfig.web.json @@ -4,7 +4,7 @@ "baseUrl": "./", "paths": { "~identity_wasm": ["../web/identity_wasm"], - "~iota-client-wasm": ["../node_modules/@cycraig/iota-client-wasm/web"], + "~iota-client-wasm": ["../node_modules/@iota/iota-client-wasm/web"], }, "outDir": "../web", "declarationDir": "../web", diff --git a/bindings/wasm/package-lock.json b/bindings/wasm/package-lock.json index 67cca14774..431ac1306b 100644 --- a/bindings/wasm/package-lock.json +++ b/bindings/wasm/package-lock.json @@ -38,46 +38,14 @@ "node": ">=16" }, "peerDependencies": { - "@cycraig/iota-client-wasm": "^0.5.0-alpha.1", + "@iota/iota-client-wasm": "^0.5.0-alpha.1", "@iota/iota.js": "^1.9.0-stardust.25" } }, - "../../../iota.rs/bindings/wasm": { - "name": "@cycraig/iota-client-wasm", - "version": "0.5.0-alpha.1", - "extraneous": true, - "license": "Apache-2.0", - "dependencies": { - "@iota/types": "^1.0.0-beta.11", - "node-fetch": "^2.6.7", - "text-encoding": "^0.7.0" - }, - "devDependencies": { - "@types/jest": "^27.5.2", - "@typescript-eslint/eslint-plugin": "^5.31.0", - "@typescript-eslint/parser": "^5.31.0", - "dotenv": "^16.0.1", - "eslint": "^8.20.0", - "eslint-config-prettier": "^8.5.0", - "fs-extra": "^10.1.0", - "jest": "^27.5.1", - "jest-matcher-utils": "^28.1.3", - "prettier": "^2.7.1", - "ts-jest": "^27.1.5", - "ts-node": "^10.9.1", - "typedoc": "^0.23.9", - "typedoc-plugin-markdown": "^3.13.4", - "typescript": "^4.7.4", - "wasm-opt": "^1.3.0" - }, - "engines": { - "node": ">=16" - } - }, "node_modules/@babel/parser": { "version": "7.18.11", - "integrity": "sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==", "dev": true, + "license": "MIT", "bin": { "parser": "bin/babel-parser.js" }, @@ -87,8 +55,8 @@ }, "node_modules/@colors/colors": { "version": "1.5.0", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", "dev": true, + "license": "MIT", "optional": true, "engines": { "node": ">=0.1.90" @@ -96,8 +64,8 @@ }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -105,25 +73,10 @@ "node": ">=12" } }, - "node_modules/@cycraig/iota-client-wasm": { - "version": "0.5.0-alpha.2", - "resolved": "https://registry.npmjs.org/@cycraig/iota-client-wasm/-/iota-client-wasm-0.5.0-alpha.2.tgz", - "integrity": "sha512-KbA7YUzEf+A1KoucQaPkKHTm8SFAB9ZX9WKAvRjgI+qh40xVubykbaj1cZ+x1UNekMK3K8Oc7uOmRtzBnsZZkA==", - "peer": true, - "dependencies": { - "@iota/types": "^1.0.0-beta.11", - "node-fetch": "^2.6.7", - "text-encoding": "^0.7.0" - }, - "engines": { - "node": ">=16" - } - }, "node_modules/@cypress/request": { "version": "2.88.10", - "resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.10.tgz", - "integrity": "sha512-Zp7F+R93N0yZyG34GutyTNr+okam7s/Fzc1+i3kcqOP8vk6OuajuE9qZJ6Rs+10/1JFtXFYMdyarnU1rZuJesg==", "dev": true, + "license": "Apache-2.0", "dependencies": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", @@ -150,9 +103,8 @@ }, "node_modules/@cypress/request/node_modules/form-data": { "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", "dev": true, + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.6", @@ -164,9 +116,8 @@ }, "node_modules/@cypress/xvfb": { "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz", - "integrity": "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==", "dev": true, + "license": "MIT", "dependencies": { "debug": "^3.1.0", "lodash.once": "^4.1.1" @@ -174,25 +125,23 @@ }, "node_modules/@cypress/xvfb/node_modules/debug": { "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, + "license": "MIT", "dependencies": { "ms": "^2.1.1" } }, "node_modules/@discoveryjs/json-ext": { "version": "0.5.7", - "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", "dev": true, + "license": "MIT", "engines": { "node": ">=10.0.0" } }, "node_modules/@iota/crypto.js": { "version": "1.9.0-stardust.6", - "resolved": "https://registry.npmjs.org/@iota/crypto.js/-/crypto.js-1.9.0-stardust.6.tgz", - "integrity": "sha512-jbruSRXlEKW2dgfPFG7e42ODBp4dVu2/CAh/oZrzMjehPdyF2tgAeN1woOqOAmVONWoGVV9GViZti3nShhGF1g==", + "license": "Apache-2.0", "dependencies": { "@iota/util.js": "^1.9.0-stardust.5", "big-integer": "^1.6.51" @@ -201,10 +150,22 @@ "node": ">=14.0.0" } }, + "node_modules/@iota/iota-client-wasm": { + "version": "0.5.0-alpha.2", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@iota/types": "^1.0.0-beta.11", + "node-fetch": "^2.6.7", + "text-encoding": "^0.7.0" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/@iota/iota.js": { "version": "1.9.0-stardust.25", - "resolved": "https://registry.npmjs.org/@iota/iota.js/-/iota.js-1.9.0-stardust.25.tgz", - "integrity": "sha512-mx/ZpkrUEgPjd1FxVxDKGpmDF/aTuUcRXjzZWjBL8v2w9dn3/Bs2fZ3AJw7wIwMG6v48AMXWq2yPwndpwDx/qw==", + "license": "Apache-2.0", "peer": true, "dependencies": { "@iota/crypto.js": "^1.9.0-stardust.6", @@ -218,12 +179,11 @@ }, "node_modules/@iota/types": { "version": "1.0.0-beta.11", - "integrity": "sha512-jFma7n4dpf+AikSIvY6CzrDDQ/TVnlV3HBIeOrDUDJku3oCcOkm8Jp/i0uKSD/krP2BAcDrWrNUr5Tn9MIw7+A==" + "license": "Apache-2.0" }, "node_modules/@iota/util.js": { "version": "1.9.0-stardust.5", - "resolved": "https://registry.npmjs.org/@iota/util.js/-/util.js-1.9.0-stardust.5.tgz", - "integrity": "sha512-BGXzzjUQQYaV/H0bXJYKW+Zgd9PIN+HLPH8BcYhubI65X4G2ID23/osmpK/za+Ds6t/MkpdPKIiBwZPGXuCUBw==", + "license": "Apache-2.0", "dependencies": { "big-integer": "^1.6.51" }, @@ -233,8 +193,8 @@ }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.2", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/set-array": "^1.0.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -246,24 +206,24 @@ }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.0", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/set-array": { "version": "1.1.2", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/source-map": { "version": "0.3.2", - "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" @@ -271,13 +231,13 @@ }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.14", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.9", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -285,8 +245,8 @@ }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -297,16 +257,16 @@ }, "node_modules/@nodelib/fs.stat": { "version": "2.0.5", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, + "license": "MIT", "engines": { "node": ">= 8" } }, "node_modules/@nodelib/fs.walk": { "version": "1.2.8", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -317,36 +277,36 @@ }, "node_modules/@tsconfig/node10": { "version": "1.0.9", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@tsconfig/node12": { "version": "1.0.11", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@tsconfig/node14": { "version": "1.0.3", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@tsconfig/node16": { "version": "1.0.3", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/debug": { "version": "4.1.7", - "integrity": "sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==", "dev": true, + "license": "MIT", "dependencies": { "@types/ms": "*" } }, "node_modules/@types/eslint": { "version": "8.4.5", - "integrity": "sha512-dhsC09y1gpJWnK+Ff4SGvCuSnk9DaU0BJZSzOwa6GVSg65XtTugLBITDAAzRU5duGBoXBHpdR/9jHGxJjNflJQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -354,8 +314,8 @@ }, "node_modules/@types/eslint-scope": { "version": "3.7.4", - "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", "dev": true, + "license": "MIT", "dependencies": { "@types/eslint": "*", "@types/estree": "*" @@ -363,30 +323,29 @@ }, "node_modules/@types/estree": { "version": "0.0.51", - "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/json-schema": { "version": "7.0.11", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/json5": { "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true, + "license": "MIT", "optional": true }, "node_modules/@types/linkify-it": { "version": "3.0.2", - "integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/markdown-it": { "version": "12.2.3", - "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/linkify-it": "*", "@types/mdurl": "*" @@ -394,34 +353,34 @@ }, "node_modules/@types/mdast": { "version": "3.0.10", - "integrity": "sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==", "dev": true, + "license": "MIT", "dependencies": { "@types/unist": "*" } }, "node_modules/@types/mdurl": { "version": "1.0.2", - "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/mocha": { "version": "9.1.1", - "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/ms": { "version": "0.7.31", - "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/node": { "version": "18.6.5", - "integrity": "sha512-Xjt5ZGUa5WusGZJ4WJPbOT8QOqp6nDynVFRKcUt32bOgvXEoc6o085WNkYTMO7ifAj2isEfQQ2cseE+wT6jsRw==" + "license": "MIT" }, "node_modules/@types/node-fetch": { "version": "2.6.2", - "integrity": "sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==", + "license": "MIT", "dependencies": { "@types/node": "*", "form-data": "^3.0.0" @@ -429,26 +388,23 @@ }, "node_modules/@types/sinonjs__fake-timers": { "version": "8.1.1", - "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz", - "integrity": "sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/sizzle": { "version": "2.3.3", - "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz", - "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/unist": { "version": "2.0.6", - "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/yauzl": { "version": "2.10.0", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "@types/node": "*" @@ -456,13 +412,13 @@ }, "node_modules/@ungap/promise-all-settled": { "version": "1.1.2", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/@webassemblyjs/ast": { "version": "1.11.1", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/helper-numbers": "1.11.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.1" @@ -470,23 +426,23 @@ }, "node_modules/@webassemblyjs/floating-point-hex-parser": { "version": "1.11.1", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/helper-api-error": { "version": "1.11.1", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/helper-buffer": { "version": "1.11.1", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/helper-numbers": { "version": "1.11.1", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/floating-point-hex-parser": "1.11.1", "@webassemblyjs/helper-api-error": "1.11.1", @@ -495,13 +451,13 @@ }, "node_modules/@webassemblyjs/helper-wasm-bytecode": { "version": "1.11.1", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/helper-wasm-section": { "version": "1.11.1", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.11.1", "@webassemblyjs/helper-buffer": "1.11.1", @@ -511,29 +467,29 @@ }, "node_modules/@webassemblyjs/ieee754": { "version": "1.11.1", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", "dev": true, + "license": "MIT", "dependencies": { "@xtuc/ieee754": "^1.2.0" } }, "node_modules/@webassemblyjs/leb128": { "version": "1.11.1", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/utf8": { "version": "1.11.1", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/wasm-edit": { "version": "1.11.1", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.11.1", "@webassemblyjs/helper-buffer": "1.11.1", @@ -547,8 +503,8 @@ }, "node_modules/@webassemblyjs/wasm-gen": { "version": "1.11.1", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.11.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.1", @@ -559,8 +515,8 @@ }, "node_modules/@webassemblyjs/wasm-opt": { "version": "1.11.1", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.11.1", "@webassemblyjs/helper-buffer": "1.11.1", @@ -570,8 +526,8 @@ }, "node_modules/@webassemblyjs/wasm-parser": { "version": "1.11.1", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.11.1", "@webassemblyjs/helper-api-error": "1.11.1", @@ -583,8 +539,8 @@ }, "node_modules/@webassemblyjs/wast-printer": { "version": "1.11.1", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.11.1", "@xtuc/long": "4.2.2" @@ -592,8 +548,8 @@ }, "node_modules/@webpack-cli/configtest": { "version": "1.2.0", - "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", "dev": true, + "license": "MIT", "peerDependencies": { "webpack": "4.x.x || 5.x.x", "webpack-cli": "4.x.x" @@ -601,8 +557,8 @@ }, "node_modules/@webpack-cli/info": { "version": "1.5.0", - "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", "dev": true, + "license": "MIT", "dependencies": { "envinfo": "^7.7.3" }, @@ -612,8 +568,8 @@ }, "node_modules/@webpack-cli/serve": { "version": "1.7.0", - "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", "dev": true, + "license": "MIT", "peerDependencies": { "webpack-cli": "4.x.x" }, @@ -625,18 +581,18 @@ }, "node_modules/@xtuc/ieee754": { "version": "1.2.0", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/@xtuc/long": { "version": "4.2.2", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/acorn": { "version": "8.8.0", - "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", "dev": true, + "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -646,25 +602,24 @@ }, "node_modules/acorn-import-assertions": { "version": "1.8.0", - "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", "dev": true, + "license": "MIT", "peerDependencies": { "acorn": "^8" } }, "node_modules/acorn-walk": { "version": "8.2.0", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.4.0" } }, "node_modules/aggregate-error": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "dev": true, + "license": "MIT", "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" @@ -675,8 +630,8 @@ }, "node_modules/ajv": { "version": "6.12.6", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -690,25 +645,24 @@ }, "node_modules/ajv-keywords": { "version": "3.5.2", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "dev": true, + "license": "MIT", "peerDependencies": { "ajv": "^6.9.1" } }, "node_modules/ansi-colors": { "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/ansi-escape-sequences": { "version": "4.1.0", - "integrity": "sha512-dzW9kHxH011uBsidTXd14JXgzye/YLb2LzeKZ4bsgl/Knwx8AtbSFkkGxagdNOoh0DlqHCmfiEjWKBaqjOanVw==", "dev": true, + "license": "MIT", "dependencies": { "array-back": "^3.0.1" }, @@ -718,17 +672,16 @@ }, "node_modules/ansi-escape-sequences/node_modules/array-back": { "version": "3.1.0", - "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/ansi-escapes": { "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, + "license": "MIT", "dependencies": { "type-fest": "^0.21.3" }, @@ -741,17 +694,16 @@ }, "node_modules/ansi-regex": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/ansi-styles": { "version": "4.3.0", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -764,8 +716,8 @@ }, "node_modules/anymatch": { "version": "3.1.2", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, + "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -776,8 +728,6 @@ }, "node_modules/arch": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", - "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", "dev": true, "funding": [ { @@ -792,114 +742,109 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/arg": { "version": "4.1.3", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/argparse": { "version": "2.0.1", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "dev": true, + "license": "Python-2.0" }, "node_modules/array-back": { "version": "6.2.2", - "integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==", "dev": true, + "license": "MIT", "engines": { "node": ">=12.17" } }, "node_modules/array-union": { "version": "2.1.0", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/arrify": { "version": "1.0.1", - "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/asn1": { "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", "dev": true, + "license": "MIT", "dependencies": { "safer-buffer": "~2.1.0" } }, "node_modules/assert-plus": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8" } }, "node_modules/astral-regex": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/async": { "version": "3.2.4", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/asynckit": { "version": "0.4.0", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + "license": "MIT" }, "node_modules/at-least-node": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", "dev": true, + "license": "ISC", "engines": { "node": ">= 4.0.0" } }, "node_modules/aws-sign2": { "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "*" } }, "node_modules/aws4": { "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/axios": { "version": "0.21.4", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", "dev": true, + "license": "MIT", "dependencies": { "follow-redirects": "^1.14.0" } }, "node_modules/bail": { "version": "2.0.2", - "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", "dev": true, + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -907,13 +852,11 @@ }, "node_modules/balanced-match": { "version": "1.0.2", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/base64-js": { "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "dev": true, "funding": [ { @@ -928,12 +871,13 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/basic-auth": { "version": "2.0.1", - "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", "dev": true, + "license": "MIT", "dependencies": { "safe-buffer": "5.1.2" }, @@ -943,46 +887,44 @@ }, "node_modules/basic-auth/node_modules/safe-buffer": { "version": "5.1.2", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/bcrypt-pbkdf": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "tweetnacl": "^0.14.3" } }, "node_modules/big-integer": { "version": "1.6.51", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", - "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "license": "Unlicense", "engines": { "node": ">=0.6" } }, "node_modules/big.js": { "version": "5.2.2", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", "dev": true, + "license": "MIT", "engines": { "node": "*" } }, "node_modules/binary-extensions": { "version": "2.2.0", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/binary-install": { "version": "0.1.1", - "integrity": "sha512-DqED0D/6LrS+BHDkKn34vhRqOGjy5gTMgvYZsGK2TpNbdPuz4h+MRlNgGv5QBRd7pWq/jylM4eKNCizgAq3kNQ==", "dev": true, + "license": "MIT", "dependencies": { "axios": "^0.21.1", "rimraf": "^3.0.2", @@ -994,19 +936,18 @@ }, "node_modules/blob-util": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz", - "integrity": "sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/bluebird": { "version": "3.7.2", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/brace-expansion": { "version": "1.1.11", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1014,8 +955,8 @@ }, "node_modules/braces": { "version": "3.0.2", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, + "license": "MIT", "dependencies": { "fill-range": "^7.0.1" }, @@ -1025,12 +966,11 @@ }, "node_modules/browser-stdout": { "version": "1.3.1", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/browserslist": { "version": "4.21.3", - "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", "dev": true, "funding": [ { @@ -1042,6 +982,7 @@ "url": "https://tidelift.com/funding/github/npm/browserslist" } ], + "license": "MIT", "dependencies": { "caniuse-lite": "^1.0.30001370", "electron-to-chromium": "^1.4.202", @@ -1057,8 +998,6 @@ }, "node_modules/buffer": { "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "dev": true, "funding": [ { @@ -1074,6 +1013,7 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" @@ -1081,22 +1021,21 @@ }, "node_modules/buffer-crc32": { "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "dev": true, + "license": "MIT", "engines": { "node": "*" } }, "node_modules/buffer-from": { "version": "1.1.2", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/cache-point": { "version": "2.0.0", - "integrity": "sha512-4gkeHlFpSKgm3vm2gJN5sPqfmijYRFYCQ6tv5cLw0xVmT6r1z1vd4FNnpuOREco3cBs1G709sZ72LdgddKvL5w==", "dev": true, + "license": "MIT", "dependencies": { "array-back": "^4.0.1", "fs-then-native": "^2.0.0", @@ -1108,25 +1047,24 @@ }, "node_modules/cache-point/node_modules/array-back": { "version": "4.0.2", - "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/cachedir": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz", - "integrity": "sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/camelcase": { "version": "6.3.0", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -1136,7 +1074,6 @@ }, "node_modules/caniuse-lite": { "version": "1.0.30001375", - "integrity": "sha512-kWIMkNzLYxSvnjy0hL8w1NOaWNr2rn39RTAVyIwcw8juu60bZDWiF1/loOYANzjtJmy6qPgNmn38ro5Pygagdw==", "dev": true, "funding": [ { @@ -1147,18 +1084,18 @@ "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/caniuse-lite" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/caseless": { "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/catharsis": { "version": "0.9.0", - "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", "dev": true, + "license": "MIT", "dependencies": { "lodash": "^4.17.15" }, @@ -1168,8 +1105,8 @@ }, "node_modules/chalk": { "version": "4.1.2", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -1183,8 +1120,8 @@ }, "node_modules/chalk/node_modules/supports-color": { "version": "7.2.0", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -1194,8 +1131,8 @@ }, "node_modules/character-entities": { "version": "2.0.2", - "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", "dev": true, + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -1203,16 +1140,14 @@ }, "node_modules/check-more-types": { "version": "2.24.0", - "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", - "integrity": "sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8.0" } }, "node_modules/chokidar": { "version": "3.5.3", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, "funding": [ { @@ -1220,6 +1155,7 @@ "url": "https://paulmillr.com/funding/" } ], + "license": "MIT", "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -1238,32 +1174,29 @@ }, "node_modules/chrome-trace-event": { "version": "1.0.3", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.0" } }, "node_modules/ci-info": { "version": "3.3.2", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.2.tgz", - "integrity": "sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/clean-stack": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/cli-cursor": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "dev": true, + "license": "MIT", "dependencies": { "restore-cursor": "^3.1.0" }, @@ -1273,8 +1206,8 @@ }, "node_modules/cli-table3": { "version": "0.6.2", - "integrity": "sha512-QyavHCaIC80cMivimWu4aWHilIpiDpfm3hGmqAmXVL1UsnbLuBSMd21hTX6VY4ZSDSM73ESLeF8TOYId3rBTbw==", "dev": true, + "license": "MIT", "dependencies": { "string-width": "^4.2.0" }, @@ -1287,9 +1220,8 @@ }, "node_modules/cli-truncate": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", - "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", "dev": true, + "license": "MIT", "dependencies": { "slice-ansi": "^3.0.0", "string-width": "^4.2.0" @@ -1303,8 +1235,8 @@ }, "node_modules/cliui": { "version": "7.0.4", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, + "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", @@ -1313,16 +1245,16 @@ }, "node_modules/cliui/node_modules/ansi-regex": { "version": "5.0.1", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/cliui/node_modules/strip-ansi": { "version": "6.0.1", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -1332,8 +1264,8 @@ }, "node_modules/clone-deep": { "version": "4.0.1", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", "dev": true, + "license": "MIT", "dependencies": { "is-plain-object": "^2.0.4", "kind-of": "^6.0.2", @@ -1345,8 +1277,8 @@ }, "node_modules/collect-all": { "version": "1.0.4", - "integrity": "sha512-RKZhRwJtJEP5FWul+gkSMEnaK6H3AGPTTWOiRimCcs+rc/OmQE3Yhy1Q7A7KsdkG3ZXVdZq68Y6ONSdvkeEcKA==", "dev": true, + "license": "MIT", "dependencies": { "stream-connect": "^1.0.2", "stream-via": "^1.0.4" @@ -1357,8 +1289,8 @@ }, "node_modules/color-convert": { "version": "2.0.1", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -1368,26 +1300,25 @@ }, "node_modules/color-name": { "version": "1.1.4", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/colorette": { "version": "2.0.19", - "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/colors": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.1.90" } }, "node_modules/combined-stream": { "version": "1.0.8", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" }, @@ -1397,8 +1328,8 @@ }, "node_modules/command-line-args": { "version": "5.2.1", - "integrity": "sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==", "dev": true, + "license": "MIT", "dependencies": { "array-back": "^3.1.0", "find-replace": "^3.0.0", @@ -1411,24 +1342,24 @@ }, "node_modules/command-line-args/node_modules/array-back": { "version": "3.1.0", - "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/command-line-args/node_modules/typical": { "version": "4.0.0", - "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/command-line-tool": { "version": "0.8.0", - "integrity": "sha512-Xw18HVx/QzQV3Sc5k1vy3kgtOeGmsKIqwtFFoyjI4bbcpSgnw2CWVULvtakyw4s6fhyAdI6soQQhXc2OzJy62g==", "dev": true, + "license": "MIT", "dependencies": { "ansi-escape-sequences": "^4.0.0", "array-back": "^2.0.0", @@ -1442,8 +1373,8 @@ }, "node_modules/command-line-tool/node_modules/array-back": { "version": "2.0.0", - "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", "dev": true, + "license": "MIT", "dependencies": { "typical": "^2.6.1" }, @@ -1453,8 +1384,8 @@ }, "node_modules/command-line-usage": { "version": "4.1.0", - "integrity": "sha512-MxS8Ad995KpdAC0Jopo/ovGIroV/m0KHwzKfXxKag6FHOkGsH8/lv5yjgablcRxCJJC0oJeUMuO/gmaq+Wq46g==", "dev": true, + "license": "MIT", "dependencies": { "ansi-escape-sequences": "^4.0.0", "array-back": "^2.0.0", @@ -1467,8 +1398,8 @@ }, "node_modules/command-line-usage/node_modules/array-back": { "version": "2.0.0", - "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", "dev": true, + "license": "MIT", "dependencies": { "typical": "^2.6.1" }, @@ -1478,39 +1409,37 @@ }, "node_modules/commander": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 6" } }, "node_modules/common-sequence": { "version": "2.0.2", - "integrity": "sha512-jAg09gkdkrDO9EWTdXfv80WWH3yeZl5oT69fGfedBNS9pXUKYInVJ1bJ+/ht2+Moeei48TmSbQDYMc8EOx9G0g==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/common-tags": { "version": "1.8.2", - "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", - "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", "dev": true, + "license": "MIT", "engines": { "node": ">=4.0.0" } }, "node_modules/concat-map": { "version": "0.0.1", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/concurrently": { "version": "7.3.0", - "integrity": "sha512-IiDwm+8DOcFEInca494A8V402tNTQlJaYq78RF2rijOrKEk/AOHTxhN4U1cp7GYKYX5Q6Ymh1dLTBlzIMN0ikA==", "dev": true, + "license": "MIT", "dependencies": { "chalk": "^4.1.0", "date-fns": "^2.16.1", @@ -1531,24 +1460,24 @@ }, "node_modules/config-master": { "version": "3.1.0", - "integrity": "sha512-n7LBL1zBzYdTpF1mx5DNcZnZn05CWIdsdvtPL4MosvqbBUK3Rq6VWEtGUuF3Y0s9/CIhMejezqlSkP6TnCJ/9g==", "dev": true, + "license": "MIT", "dependencies": { "walk-back": "^2.0.1" } }, "node_modules/config-master/node_modules/walk-back": { "version": "2.0.1", - "integrity": "sha512-Nb6GvBR8UWX1D+Le+xUq0+Q1kFmRBIWVrfLnQAOmcpEzA9oAxwJ9gIr36t9TWYfzvWRvuMtjHiVsJYEkXWaTAQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/copy-webpack-plugin": { "version": "7.0.0", - "integrity": "sha512-SLjQNa5iE3BoCP76ESU9qYo9ZkEWtXoZxDurHoqPchAFRblJ9g96xTeC560UXBMre1Nx6ixIIUfiY3VcjpJw3g==", "dev": true, + "license": "MIT", "dependencies": { "fast-glob": "^3.2.4", "glob-parent": "^5.1.1", @@ -1572,27 +1501,26 @@ }, "node_modules/core-util-is": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/corser": { "version": "2.0.1", - "integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4.0" } }, "node_modules/create-require": { "version": "1.1.1", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/cross-spawn": { "version": "7.0.3", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -1604,10 +1532,9 @@ }, "node_modules/cypress": { "version": "10.6.0", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-10.6.0.tgz", - "integrity": "sha512-6sOpHjostp8gcLO34p6r/Ci342lBs8S5z9/eb3ZCQ22w2cIhMWGUoGKkosabPBfKcvRS9BE4UxybBtlIs8gTQA==", "dev": true, "hasInstallScript": true, + "license": "MIT", "dependencies": { "@cypress/request": "^2.88.10", "@cypress/xvfb": "^1.2.4", @@ -1661,9 +1588,8 @@ }, "node_modules/cypress-multi-reporters": { "version": "1.6.1", - "resolved": "https://registry.npmjs.org/cypress-multi-reporters/-/cypress-multi-reporters-1.6.1.tgz", - "integrity": "sha512-FPeC0xWF1N6Myrwc2m7KC0xxlrtG8+x4hlsPFBDRWP8u/veR2x90pGaH3BuJfweV7xoQ4Zo85Qjhu3fgZGrBQQ==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "debug": "^4.1.1", @@ -1678,9 +1604,8 @@ }, "node_modules/cypress-parallel": { "version": "0.9.1", - "resolved": "https://registry.npmjs.org/cypress-parallel/-/cypress-parallel-0.9.1.tgz", - "integrity": "sha512-7VSfFr8HEEN6zkgo6SkG7pPoHK7VakFhEH1jbM4+Ire/I+O2jNzyd1bRUA+O3V2DIMow64ECDJKf13YHBon+BQ==", "dev": true, + "license": "MIT", "dependencies": { "cli-table3": "^0.6.0", "colors": "^1.4.0", @@ -1701,36 +1626,32 @@ }, "node_modules/cypress-parallel/node_modules/ansi-colors": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/cypress-parallel/node_modules/ansi-regex": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/cypress-parallel/node_modules/camelcase": { "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/cypress-parallel/node_modules/chokidar": { "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", "dev": true, + "license": "MIT", "dependencies": { "anymatch": "~3.1.1", "braces": "~3.0.2", @@ -1749,9 +1670,8 @@ }, "node_modules/cypress-parallel/node_modules/debug": { "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, + "license": "MIT", "dependencies": { "ms": "2.1.2" }, @@ -1766,18 +1686,16 @@ }, "node_modules/cypress-parallel/node_modules/decamelize": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/cypress-parallel/node_modules/escape-string-regexp": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -1787,9 +1705,8 @@ }, "node_modules/cypress-parallel/node_modules/glob": { "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "dev": true, + "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -1807,9 +1724,8 @@ }, "node_modules/cypress-parallel/node_modules/js-yaml": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", - "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -1819,9 +1735,8 @@ }, "node_modules/cypress-parallel/node_modules/locate-path": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, + "license": "MIT", "dependencies": { "p-locate": "^4.1.0" }, @@ -1831,9 +1746,8 @@ }, "node_modules/cypress-parallel/node_modules/log-symbols": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", - "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", "dev": true, + "license": "MIT", "dependencies": { "chalk": "^4.0.0" }, @@ -1843,9 +1757,8 @@ }, "node_modules/cypress-parallel/node_modules/minimatch": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -1855,9 +1768,8 @@ }, "node_modules/cypress-parallel/node_modules/mocha": { "version": "8.4.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz", - "integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==", "dev": true, + "license": "MIT", "dependencies": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", @@ -1899,15 +1811,13 @@ }, "node_modules/cypress-parallel/node_modules/mocha/node_modules/ms": { "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/cypress-parallel/node_modules/mocha/node_modules/yargs": { "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, + "license": "MIT", "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", @@ -1923,9 +1833,8 @@ }, "node_modules/cypress-parallel/node_modules/nanoid": { "version": "3.1.20", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", - "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", "dev": true, + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -1935,9 +1844,8 @@ }, "node_modules/cypress-parallel/node_modules/p-limit": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, + "license": "MIT", "dependencies": { "p-try": "^2.0.0" }, @@ -1950,9 +1858,8 @@ }, "node_modules/cypress-parallel/node_modules/p-locate": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, + "license": "MIT", "dependencies": { "p-limit": "^2.2.0" }, @@ -1962,9 +1869,8 @@ }, "node_modules/cypress-parallel/node_modules/readdirp": { "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", "dev": true, + "license": "MIT", "dependencies": { "picomatch": "^2.2.1" }, @@ -1974,9 +1880,8 @@ }, "node_modules/cypress-parallel/node_modules/strip-ansi": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -1986,15 +1891,13 @@ }, "node_modules/cypress-parallel/node_modules/workerpool": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz", - "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/cypress-parallel/node_modules/wrap-ansi": { "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -2006,9 +1909,8 @@ }, "node_modules/cypress-parallel/node_modules/yargs": { "version": "15.3.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz", - "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==", "dev": true, + "license": "MIT", "dependencies": { "cliui": "^6.0.0", "decamelize": "^1.2.0", @@ -2028,9 +1930,8 @@ }, "node_modules/cypress-parallel/node_modules/yargs/node_modules/cliui": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", "dev": true, + "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", @@ -2039,9 +1940,8 @@ }, "node_modules/cypress-parallel/node_modules/yargs/node_modules/find-up": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, + "license": "MIT", "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -2052,15 +1952,13 @@ }, "node_modules/cypress-parallel/node_modules/yargs/node_modules/y18n": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/cypress-parallel/node_modules/yargs/node_modules/yargs-parser": { "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "dev": true, + "license": "ISC", "dependencies": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" @@ -2071,15 +1969,13 @@ }, "node_modules/cypress/node_modules/@types/node": { "version": "14.18.25", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.25.tgz", - "integrity": "sha512-9pLfceRSrKIsv/MISN6RoFWTIzka36Uk2Uuf5a8cHyDYhEgl5Hm5dXoe621KULeBjt+cFsY18mILsWWtJeG80w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/cypress/node_modules/fs-extra": { "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, + "license": "MIT", "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", @@ -2092,9 +1988,8 @@ }, "node_modules/dashdash": { "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", "dev": true, + "license": "MIT", "dependencies": { "assert-plus": "^1.0.0" }, @@ -2104,8 +1999,8 @@ }, "node_modules/date-fns": { "version": "2.29.1", - "integrity": "sha512-dlLD5rKaKxpFdnjrs+5azHDFOPEu4ANy/LTh04A1DTzMM7qoajmKCBc8pkKRFT41CNzw+4gQh79X5C+Jq27HAw==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.11" }, @@ -2116,14 +2011,13 @@ }, "node_modules/dayjs": { "version": "1.11.5", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.5.tgz", - "integrity": "sha512-CAdX5Q3YW3Gclyo5Vpqkgpj8fSdLQcRuzfX6mC6Phy0nfJ0eGYOeS7m4mt2plDWLAtA4TqTakvbboHvUxfe4iA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/debug": { "version": "4.3.4", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, + "license": "MIT", "dependencies": { "ms": "2.1.2" }, @@ -2138,8 +2032,8 @@ }, "node_modules/decamelize": { "version": "4.0.0", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -2149,8 +2043,8 @@ }, "node_modules/decode-named-character-reference": { "version": "1.0.2", - "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", "dev": true, + "license": "MIT", "dependencies": { "character-entities": "^2.0.0" }, @@ -2161,44 +2055,44 @@ }, "node_modules/deep-extend": { "version": "0.6.0", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true, + "license": "MIT", "engines": { "node": ">=4.0.0" } }, "node_modules/delayed-stream": { "version": "1.0.0", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", "engines": { "node": ">=0.4.0" } }, "node_modules/dequal": { "version": "2.0.3", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/diff": { "version": "5.0.0", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } }, "node_modules/diff-match-patch": { "version": "1.0.5", - "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/dir-glob": { "version": "3.0.1", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, + "license": "MIT", "dependencies": { "path-type": "^4.0.0" }, @@ -2208,8 +2102,8 @@ }, "node_modules/dmd": { "version": "6.1.0", - "integrity": "sha512-0zQIJ873gay1scCTFZvHPWM9mVJBnaylB2NQDI8O9u8O32m00Jb6uxDKexZm8hjTRM7RiWe0FJ32pExHoXdwoQ==", "dev": true, + "license": "MIT", "dependencies": { "array-back": "^6.2.2", "cache-point": "^2.0.0", @@ -2230,9 +2124,8 @@ }, "node_modules/ecc-jsbn": { "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", "dev": true, + "license": "MIT", "dependencies": { "jsbn": "~0.1.0", "safer-buffer": "^2.1.0" @@ -2240,35 +2133,34 @@ }, "node_modules/electron-to-chromium": { "version": "1.4.213", - "integrity": "sha512-+3DbGHGOCHTVB/Ms63bGqbyC1b8y7Fk86+7ltssB8NQrZtSCvZG6eooSl9U2Q0yw++fL2DpHKOdTU0NVEkFObg==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/emoji-regex": { "version": "8.0.0", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/emojis-list": { "version": "3.0.0", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">= 4" } }, "node_modules/end-of-stream": { "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "dev": true, + "license": "MIT", "dependencies": { "once": "^1.4.0" } }, "node_modules/enhanced-resolve": { "version": "5.10.0", - "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -2279,9 +2171,8 @@ }, "node_modules/enquirer": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", "dev": true, + "license": "MIT", "dependencies": { "ansi-colors": "^4.1.1" }, @@ -2291,16 +2182,16 @@ }, "node_modules/entities": { "version": "2.1.0", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", "dev": true, + "license": "BSD-2-Clause", "funding": { "url": "https://github.com/fb55/entities?sponsor=1" } }, "node_modules/envinfo": { "version": "7.8.1", - "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", "dev": true, + "license": "MIT", "bin": { "envinfo": "dist/cli.js" }, @@ -2310,30 +2201,29 @@ }, "node_modules/es-module-lexer": { "version": "0.9.3", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/escalade": { "version": "3.1.1", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/escape-string-regexp": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.0" } }, "node_modules/eslint-scope": { "version": "5.1.1", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" @@ -2344,8 +2234,8 @@ }, "node_modules/esrecurse": { "version": "4.3.0", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" }, @@ -2355,44 +2245,42 @@ }, "node_modules/esrecurse/node_modules/estraverse": { "version": "5.3.0", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } }, "node_modules/estraverse": { "version": "4.3.0", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } }, "node_modules/eventemitter2": { "version": "6.4.7", - "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.7.tgz", - "integrity": "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/eventemitter3": { "version": "4.0.7", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/events": { "version": "3.3.0", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.x" } }, "node_modules/execa": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", - "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", "dev": true, + "license": "MIT", "dependencies": { "cross-spawn": "^7.0.0", "get-stream": "^5.0.0", @@ -2413,9 +2301,8 @@ }, "node_modules/executable": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", - "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", "dev": true, + "license": "MIT", "dependencies": { "pify": "^2.2.0" }, @@ -2425,14 +2312,13 @@ }, "node_modules/extend": { "version": "3.0.2", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/extract-zip": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", - "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "debug": "^4.1.1", "get-stream": "^5.1.0", @@ -2450,22 +2336,21 @@ }, "node_modules/extsprintf": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", "dev": true, "engines": [ "node >=0.6.0" - ] + ], + "license": "MIT" }, "node_modules/fast-deep-equal": { "version": "3.1.3", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-glob": { "version": "3.2.11", - "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -2479,39 +2364,37 @@ }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fastest-levenshtein": { "version": "1.0.16", - "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 4.9.1" } }, "node_modules/fastq": { "version": "1.13.0", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", "dev": true, + "license": "ISC", "dependencies": { "reusify": "^1.0.4" } }, "node_modules/fd-slicer": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", "dev": true, + "license": "MIT", "dependencies": { "pend": "~1.2.0" } }, "node_modules/figures": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", "dev": true, + "license": "MIT", "dependencies": { "escape-string-regexp": "^1.0.5" }, @@ -2524,8 +2407,8 @@ }, "node_modules/file-set": { "version": "4.0.2", - "integrity": "sha512-fuxEgzk4L8waGXaAkd8cMr73Pm0FxOVkn8hztzUW7BAHhOGH90viQNXbiOsnecCWmfInqU6YmAMwxRMdKETceQ==", "dev": true, + "license": "MIT", "dependencies": { "array-back": "^5.0.0", "glob": "^7.1.6" @@ -2536,16 +2419,16 @@ }, "node_modules/file-set/node_modules/array-back": { "version": "5.0.0", - "integrity": "sha512-kgVWwJReZWmVuWOQKEOohXKJX+nD02JAZ54D1RRWlv8L0NebauKAaFxACKzB74RTclt1+WNz5KHaLRDAPZbDEw==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" } }, "node_modules/fill-range": { "version": "7.0.1", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -2555,8 +2438,8 @@ }, "node_modules/find-replace": { "version": "3.0.0", - "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", "dev": true, + "license": "MIT", "dependencies": { "array-back": "^3.0.1" }, @@ -2566,16 +2449,16 @@ }, "node_modules/find-replace/node_modules/array-back": { "version": "3.1.0", - "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/find-up": { "version": "5.0.0", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, + "license": "MIT", "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -2589,15 +2472,14 @@ }, "node_modules/flat": { "version": "5.0.2", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "dev": true, + "license": "BSD-3-Clause", "bin": { "flat": "cli.js" } }, "node_modules/follow-redirects": { "version": "1.15.1", - "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", "dev": true, "funding": [ { @@ -2605,6 +2487,7 @@ "url": "https://github.com/sponsors/RubenVerborgh" } ], + "license": "MIT", "engines": { "node": ">=4.0" }, @@ -2616,16 +2499,15 @@ }, "node_modules/forever-agent": { "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "*" } }, "node_modules/form-data": { "version": "3.0.1", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -2637,9 +2519,8 @@ }, "node_modules/fs-extra": { "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -2651,8 +2532,8 @@ }, "node_modules/fs-minipass": { "version": "2.1.0", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", "dev": true, + "license": "ISC", "dependencies": { "minipass": "^3.0.0" }, @@ -2662,49 +2543,34 @@ }, "node_modules/fs-then-native": { "version": "2.0.0", - "integrity": "sha512-X712jAOaWXkemQCAmWeg5rOT2i+KOpWz1Z/txk/cW0qlOu2oQ9H61vc5w3X/iyuUEfq/OyaFJ78/cZAQD1/bgA==", "dev": true, + "license": "MIT", "engines": { "node": ">=4.0.0" } }, "node_modules/fs.realpath": { "version": "1.0.0", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } + "license": "ISC" }, "node_modules/function-bind": { "version": "1.1.1", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/get-caller-file": { "version": "2.0.5", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, + "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" } }, "node_modules/get-stream": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, + "license": "MIT", "dependencies": { "pump": "^3.0.0" }, @@ -2717,26 +2583,24 @@ }, "node_modules/getos": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz", - "integrity": "sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==", "dev": true, + "license": "MIT", "dependencies": { "async": "^3.2.0" } }, "node_modules/getpass": { "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", "dev": true, + "license": "MIT", "dependencies": { "assert-plus": "^1.0.0" } }, "node_modules/glob": { "version": "7.2.3", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, + "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -2754,17 +2618,16 @@ }, "node_modules/glob-escape": { "version": "0.0.2", - "resolved": "https://registry.npmjs.org/glob-escape/-/glob-escape-0.0.2.tgz", - "integrity": "sha512-L/cXYz8x7qer1HAyUQ+mbjcUsJVdpRxpAf7CwqHoNBs9vTpABlGfNN4tzkDxt+u3Z7ZncVyKlCNPtzb0R/7WbA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.10" } }, "node_modules/glob-parent": { "version": "5.1.2", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, @@ -2774,14 +2637,13 @@ }, "node_modules/glob-to-regexp": { "version": "0.4.1", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true + "dev": true, + "license": "BSD-2-Clause" }, "node_modules/global-dirs": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz", - "integrity": "sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==", "dev": true, + "license": "MIT", "dependencies": { "ini": "2.0.0" }, @@ -2794,8 +2656,8 @@ }, "node_modules/globby": { "version": "11.1.0", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, + "license": "MIT", "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", @@ -2813,21 +2675,21 @@ }, "node_modules/graceful-fs": { "version": "4.2.10", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/growl": { "version": "1.10.5", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", "dev": true, + "license": "MIT", "engines": { "node": ">=4.x" } }, "node_modules/handlebars": { "version": "4.7.7", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", "dev": true, + "license": "MIT", "dependencies": { "minimist": "^1.2.5", "neo-async": "^2.6.0", @@ -2846,8 +2708,8 @@ }, "node_modules/has": { "version": "1.0.3", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, + "license": "MIT", "dependencies": { "function-bind": "^1.1.1" }, @@ -2857,24 +2719,24 @@ }, "node_modules/has-flag": { "version": "4.0.0", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/he": { "version": "1.2.0", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true, + "license": "MIT", "bin": { "he": "bin/he" } }, "node_modules/html-encoding-sniffer": { "version": "3.0.0", - "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", "dev": true, + "license": "MIT", "dependencies": { "whatwg-encoding": "^2.0.0" }, @@ -2884,8 +2746,8 @@ }, "node_modules/http-proxy": { "version": "1.18.1", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", "dev": true, + "license": "MIT", "dependencies": { "eventemitter3": "^4.0.0", "follow-redirects": "^1.0.0", @@ -2897,8 +2759,8 @@ }, "node_modules/http-server": { "version": "14.1.1", - "integrity": "sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==", "dev": true, + "license": "MIT", "dependencies": { "basic-auth": "^2.0.1", "chalk": "^4.1.2", @@ -2923,9 +2785,8 @@ }, "node_modules/http-signature": { "version": "1.3.6", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz", - "integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==", "dev": true, + "license": "MIT", "dependencies": { "assert-plus": "^1.0.0", "jsprim": "^2.0.2", @@ -2937,17 +2798,16 @@ }, "node_modules/human-signals": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=8.12.0" } }, "node_modules/iconv-lite": { "version": "0.6.3", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, + "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -2957,8 +2817,6 @@ }, "node_modules/ieee754": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "dev": true, "funding": [ { @@ -2973,20 +2831,21 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "BSD-3-Clause" }, "node_modules/ignore": { "version": "5.2.0", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 4" } }, "node_modules/import-local": { "version": "3.1.0", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", "dev": true, + "license": "MIT", "dependencies": { "pkg-dir": "^4.2.0", "resolve-cwd": "^3.0.0" @@ -3003,17 +2862,16 @@ }, "node_modules/indent-string": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/inflight": { "version": "1.0.6", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, + "license": "ISC", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -3021,30 +2879,29 @@ }, "node_modules/inherits": { "version": "2.0.4", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/ini": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", - "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", "dev": true, + "license": "ISC", "engines": { "node": ">=10" } }, "node_modules/interpret": { "version": "2.2.0", - "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.10" } }, "node_modules/is-binary-path": { "version": "2.1.0", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, + "license": "MIT", "dependencies": { "binary-extensions": "^2.0.0" }, @@ -3054,7 +2911,6 @@ }, "node_modules/is-buffer": { "version": "2.0.5", - "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", "dev": true, "funding": [ { @@ -3070,15 +2926,15 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/is-ci": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", - "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", "dev": true, + "license": "MIT", "dependencies": { "ci-info": "^3.2.0" }, @@ -3088,8 +2944,8 @@ }, "node_modules/is-core-module": { "version": "2.10.0", - "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", "dev": true, + "license": "MIT", "dependencies": { "has": "^1.0.3" }, @@ -3099,24 +2955,24 @@ }, "node_modules/is-extglob": { "version": "2.1.1", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/is-glob": { "version": "4.0.3", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, + "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -3126,9 +2982,8 @@ }, "node_modules/is-installed-globally": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", - "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", "dev": true, + "license": "MIT", "dependencies": { "global-dirs": "^3.0.0", "is-path-inside": "^3.0.2" @@ -3142,9 +2997,8 @@ }, "node_modules/is-npm": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz", - "integrity": "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -3154,25 +3008,24 @@ }, "node_modules/is-number": { "version": "7.0.0", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } }, "node_modules/is-path-inside": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/is-plain-obj": { "version": "4.1.0", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -3182,8 +3035,8 @@ }, "node_modules/is-plain-object": { "version": "2.0.4", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, + "license": "MIT", "dependencies": { "isobject": "^3.0.1" }, @@ -3193,9 +3046,8 @@ }, "node_modules/is-stream": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -3205,14 +3057,13 @@ }, "node_modules/is-typedarray": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/is-unicode-supported": { "version": "0.1.0", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -3222,27 +3073,26 @@ }, "node_modules/isexe": { "version": "2.0.0", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/isobject": { "version": "3.0.1", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/isstream": { "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/jest-worker": { "version": "27.5.1", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*", "merge-stream": "^2.0.0", @@ -3254,8 +3104,8 @@ }, "node_modules/js-yaml": { "version": "4.1.0", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -3265,22 +3115,21 @@ }, "node_modules/js2xmlparser": { "version": "4.0.2", - "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", "dev": true, + "license": "Apache-2.0", "dependencies": { "xmlcreate": "^2.0.4" } }, "node_modules/jsbn": { "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/jsdoc": { "version": "3.6.11", - "integrity": "sha512-8UCU0TYeIYD9KeLzEcAu2q8N/mx9O3phAGl32nmHlE0LpaJL71mMkP4d+QE5zWfNt50qheHtOZ0qoxVrsX5TUg==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@babel/parser": "^7.9.4", "@types/markdown-it": "^12.2.3", @@ -3307,8 +3156,8 @@ }, "node_modules/jsdoc-api": { "version": "7.1.1", - "integrity": "sha512-0pkuPCzVXiqsDAsVrNFXCkHzlyNepBIDVtwwehry4RJAnZmXtlAz7rh8F9FRz53u3NeynGbex+bpYWwi8lE66A==", "dev": true, + "license": "MIT", "dependencies": { "array-back": "^6.2.2", "cache-point": "^2.0.0", @@ -3326,8 +3175,8 @@ }, "node_modules/jsdoc-parse": { "version": "6.1.0", - "integrity": "sha512-n/hDGQJa69IBun1yZAjqzV4gVR41+flZ3bIlm9fKvNe2Xjsd1/+zCo2+R9ls8LxtePgIWbpA1jU7xkB2lRdLLg==", "dev": true, + "license": "MIT", "dependencies": { "array-back": "^6.2.2", "lodash.omit": "^4.5.0", @@ -3342,8 +3191,8 @@ }, "node_modules/jsdoc-to-markdown": { "version": "7.1.1", - "integrity": "sha512-CI86d63xAVNO+ENumWwmJ034lYe5iGU5GwjtTA11EuphP9tpnoi4hrKgR/J8uME0D+o4KUpVfwX1fjZhc8dEtg==", "dev": true, + "license": "MIT", "dependencies": { "array-back": "^6.2.2", "command-line-tool": "^0.8.0", @@ -3362,38 +3211,36 @@ }, "node_modules/jsdoc/node_modules/escape-string-regexp": { "version": "2.0.0", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-schema": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "dev": true + "dev": true, + "license": "(AFL-2.1 OR BSD-3-Clause)" }, "node_modules/json-schema-traverse": { "version": "0.4.1", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-stringify-safe": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/json5": { "version": "2.2.1", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", "dev": true, + "license": "MIT", "bin": { "json5": "lib/cli.js" }, @@ -3403,8 +3250,8 @@ }, "node_modules/jsonfile": { "version": "6.1.0", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, + "license": "MIT", "dependencies": { "universalify": "^2.0.0" }, @@ -3414,12 +3261,11 @@ }, "node_modules/jsprim": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", - "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==", "dev": true, "engines": [ "node >=0.6.0" ], + "license": "MIT", "dependencies": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", @@ -3429,50 +3275,48 @@ }, "node_modules/kind-of": { "version": "6.0.3", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/klaw": { "version": "3.0.0", - "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.1.9" } }, "node_modules/kleur": { "version": "4.1.5", - "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/lazy-ass": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", - "integrity": "sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==", "dev": true, + "license": "MIT", "engines": { "node": "> 0.8" } }, "node_modules/linkify-it": { "version": "3.0.3", - "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", "dev": true, + "license": "MIT", "dependencies": { "uc.micro": "^1.0.1" } }, "node_modules/listr2": { "version": "3.14.0", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.14.0.tgz", - "integrity": "sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==", "dev": true, + "license": "MIT", "dependencies": { "cli-truncate": "^2.1.0", "colorette": "^2.0.16", @@ -3497,16 +3341,16 @@ }, "node_modules/loader-runner": { "version": "4.3.0", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.11.5" } }, "node_modules/loader-utils": { "version": "2.0.2", - "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", "dev": true, + "license": "MIT", "dependencies": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", @@ -3518,8 +3362,8 @@ }, "node_modules/locate-path": { "version": "6.0.0", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, + "license": "MIT", "dependencies": { "p-locate": "^5.0.0" }, @@ -3532,39 +3376,38 @@ }, "node_modules/lodash": { "version": "4.17.21", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.camelcase": { "version": "4.3.0", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.omit": { "version": "4.5.0", - "integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.once": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.padend": { "version": "4.6.1", - "integrity": "sha512-sOQs2aqGpbl27tmCS1QNZA09Uqp01ZzWfDUoD+xzTii0E7dSQfRKcRetFwa+uXaxaqL+TKm7CgD2JdKP7aZBSw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.pick": { "version": "4.4.0", - "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/log-symbols": { "version": "4.1.0", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, + "license": "MIT", "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" @@ -3578,9 +3421,8 @@ }, "node_modules/log-update": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", - "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", "dev": true, + "license": "MIT", "dependencies": { "ansi-escapes": "^4.3.0", "cli-cursor": "^3.1.0", @@ -3596,18 +3438,16 @@ }, "node_modules/log-update/node_modules/ansi-regex": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/log-update/node_modules/slice-ansi": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", @@ -3622,9 +3462,8 @@ }, "node_modules/log-update/node_modules/strip-ansi": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -3634,9 +3473,8 @@ }, "node_modules/log-update/node_modules/wrap-ansi": { "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -3648,9 +3486,8 @@ }, "node_modules/lru-cache": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, + "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, @@ -3660,13 +3497,13 @@ }, "node_modules/make-error": { "version": "1.3.6", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/markdown-it": { "version": "12.3.2", - "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1", "entities": "~2.1.0", @@ -3680,8 +3517,8 @@ }, "node_modules/markdown-it-anchor": { "version": "8.6.4", - "integrity": "sha512-Ul4YVYZNxMJYALpKtu+ZRdrryYt/GlQ5CK+4l1bp/gWXOG2QWElt6AqF3Mih/wfUKdZbNAZVXGR73/n6U/8img==", "dev": true, + "license": "Unlicense", "peerDependencies": { "@types/markdown-it": "*", "markdown-it": "*" @@ -3689,8 +3526,8 @@ }, "node_modules/marked": { "version": "4.0.18", - "integrity": "sha512-wbLDJ7Zh0sqA0Vdg6aqlbT+yPxqLblpAZh1mK2+AO2twQkPywvvqQNfEPVwSSRjZ7dZcdeVBIAgiO7MMp3Dszw==", "dev": true, + "license": "MIT", "bin": { "marked": "bin/marked.js" }, @@ -3700,8 +3537,8 @@ }, "node_modules/mdast-util-from-markdown": { "version": "1.2.0", - "integrity": "sha512-iZJyyvKD1+K7QX1b5jXdE7Sc5dtoTry1vzV28UZZe8Z1xVnB/czKntJ7ZAkG0tANqRnBF6p3p7GpU1y19DTf2Q==", "dev": true, + "license": "MIT", "dependencies": { "@types/mdast": "^3.0.0", "@types/unist": "^2.0.0", @@ -3723,8 +3560,8 @@ }, "node_modules/mdast-util-to-string": { "version": "3.1.0", - "integrity": "sha512-n4Vypz/DZgwo0iMHLQL49dJzlp7YtAJP+N07MZHpjPf/5XJuHUWstviF4Mn2jEiR/GNmtnRRqnwsXExk3igfFA==", "dev": true, + "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" @@ -3732,25 +3569,24 @@ }, "node_modules/mdurl": { "version": "1.0.1", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/merge-stream": { "version": "2.0.0", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/merge2": { "version": "1.4.1", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 8" } }, "node_modules/micromark": { "version": "3.0.10", - "integrity": "sha512-ryTDy6UUunOXy2HPjelppgJ2sNfcPz1pLlMdA6Rz9jPzhLikWXv/irpWV/I2jd68Uhmny7hHxAlAhk4+vWggpg==", "dev": true, "funding": [ { @@ -3762,6 +3598,7 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", @@ -3784,7 +3621,6 @@ }, "node_modules/micromark-core-commonmark": { "version": "1.0.6", - "integrity": "sha512-K+PkJTxqjFfSNkfAhp4GB+cZPfQd6dxtTXnf+RjZOV7T4EEXnvgzOcnp+eSTmpGk9d1S9sL6/lqrgSNn/s0HZA==", "dev": true, "funding": [ { @@ -3796,6 +3632,7 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { "decode-named-character-reference": "^1.0.0", "micromark-factory-destination": "^1.0.0", @@ -3817,7 +3654,6 @@ }, "node_modules/micromark-factory-destination": { "version": "1.0.0", - "integrity": "sha512-eUBA7Rs1/xtTVun9TmV3gjfPz2wEwgK5R5xcbIM5ZYAtvGF6JkyaDsj0agx8urXnO31tEO6Ug83iVH3tdedLnw==", "dev": true, "funding": [ { @@ -3829,6 +3665,7 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { "micromark-util-character": "^1.0.0", "micromark-util-symbol": "^1.0.0", @@ -3837,7 +3674,6 @@ }, "node_modules/micromark-factory-label": { "version": "1.0.2", - "integrity": "sha512-CTIwxlOnU7dEshXDQ+dsr2n+yxpP0+fn271pu0bwDIS8uqfFcumXpj5mLn3hSC8iw2MUr6Gx8EcKng1dD7i6hg==", "dev": true, "funding": [ { @@ -3849,6 +3685,7 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { "micromark-util-character": "^1.0.0", "micromark-util-symbol": "^1.0.0", @@ -3858,7 +3695,6 @@ }, "node_modules/micromark-factory-space": { "version": "1.0.0", - "integrity": "sha512-qUmqs4kj9a5yBnk3JMLyjtWYN6Mzfcx8uJfi5XAveBniDevmZasdGBba5b4QsvRcAkmvGo5ACmSUmyGiKTLZew==", "dev": true, "funding": [ { @@ -3870,6 +3706,7 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { "micromark-util-character": "^1.0.0", "micromark-util-types": "^1.0.0" @@ -3877,7 +3714,6 @@ }, "node_modules/micromark-factory-title": { "version": "1.0.2", - "integrity": "sha512-zily+Nr4yFqgMGRKLpTVsNl5L4PMu485fGFDOQJQBl2NFpjGte1e86zC0da93wf97jrc4+2G2GQudFMHn3IX+A==", "dev": true, "funding": [ { @@ -3889,6 +3725,7 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { "micromark-factory-space": "^1.0.0", "micromark-util-character": "^1.0.0", @@ -3899,7 +3736,6 @@ }, "node_modules/micromark-factory-whitespace": { "version": "1.0.0", - "integrity": "sha512-Qx7uEyahU1lt1RnsECBiuEbfr9INjQTGa6Err+gF3g0Tx4YEviPbqqGKNv/NrBaE7dVHdn1bVZKM/n5I/Bak7A==", "dev": true, "funding": [ { @@ -3911,6 +3747,7 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { "micromark-factory-space": "^1.0.0", "micromark-util-character": "^1.0.0", @@ -3920,7 +3757,6 @@ }, "node_modules/micromark-util-character": { "version": "1.1.0", - "integrity": "sha512-agJ5B3unGNJ9rJvADMJ5ZiYjBRyDpzKAOk01Kpi1TKhlT1APx3XZk6eN7RtSz1erbWHC2L8T3xLZ81wdtGRZzg==", "dev": true, "funding": [ { @@ -3932,6 +3768,7 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { "micromark-util-symbol": "^1.0.0", "micromark-util-types": "^1.0.0" @@ -3939,7 +3776,6 @@ }, "node_modules/micromark-util-chunked": { "version": "1.0.0", - "integrity": "sha512-5e8xTis5tEZKgesfbQMKRCyzvffRRUX+lK/y+DvsMFdabAicPkkZV6gO+FEWi9RfuKKoxxPwNL+dFF0SMImc1g==", "dev": true, "funding": [ { @@ -3951,13 +3787,13 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { "micromark-util-symbol": "^1.0.0" } }, "node_modules/micromark-util-classify-character": { "version": "1.0.0", - "integrity": "sha512-F8oW2KKrQRb3vS5ud5HIqBVkCqQi224Nm55o5wYLzY/9PwHGXC01tr3d7+TqHHz6zrKQ72Okwtvm/xQm6OVNZA==", "dev": true, "funding": [ { @@ -3969,6 +3805,7 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { "micromark-util-character": "^1.0.0", "micromark-util-symbol": "^1.0.0", @@ -3977,7 +3814,6 @@ }, "node_modules/micromark-util-combine-extensions": { "version": "1.0.0", - "integrity": "sha512-J8H058vFBdo/6+AsjHp2NF7AJ02SZtWaVUjsayNFeAiydTxUwViQPxN0Hf8dp4FmCQi0UUFovFsEyRSUmFH3MA==", "dev": true, "funding": [ { @@ -3989,6 +3825,7 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { "micromark-util-chunked": "^1.0.0", "micromark-util-types": "^1.0.0" @@ -3996,7 +3833,6 @@ }, "node_modules/micromark-util-decode-numeric-character-reference": { "version": "1.0.0", - "integrity": "sha512-OzO9AI5VUtrTD7KSdagf4MWgHMtET17Ua1fIpXTpuhclCqD8egFWo85GxSGvxgkGS74bEahvtM0WP0HjvV0e4w==", "dev": true, "funding": [ { @@ -4008,13 +3844,13 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { "micromark-util-symbol": "^1.0.0" } }, "node_modules/micromark-util-decode-string": { "version": "1.0.2", - "integrity": "sha512-DLT5Ho02qr6QWVNYbRZ3RYOSSWWFuH3tJexd3dgN1odEuPNxCngTCXJum7+ViRAd9BbdxCvMToPOD/IvVhzG6Q==", "dev": true, "funding": [ { @@ -4026,6 +3862,7 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { "decode-named-character-reference": "^1.0.0", "micromark-util-character": "^1.0.0", @@ -4035,7 +3872,6 @@ }, "node_modules/micromark-util-encode": { "version": "1.0.1", - "integrity": "sha512-U2s5YdnAYexjKDel31SVMPbfi+eF8y1U4pfiRW/Y8EFVCy/vgxk/2wWTxzcqE71LHtCuCzlBDRU2a5CQ5j+mQA==", "dev": true, "funding": [ { @@ -4046,11 +3882,11 @@ "type": "OpenCollective", "url": "https://opencollective.com/unified" } - ] + ], + "license": "MIT" }, "node_modules/micromark-util-html-tag-name": { "version": "1.1.0", - "integrity": "sha512-BKlClMmYROy9UiV03SwNmckkjn8QHVaWkqoAqzivabvdGcwNGMMMH/5szAnywmsTBUzDsU57/mFi0sp4BQO6dA==", "dev": true, "funding": [ { @@ -4061,11 +3897,11 @@ "type": "OpenCollective", "url": "https://opencollective.com/unified" } - ] + ], + "license": "MIT" }, "node_modules/micromark-util-normalize-identifier": { "version": "1.0.0", - "integrity": "sha512-yg+zrL14bBTFrQ7n35CmByWUTFsgst5JhA4gJYoty4Dqzj4Z4Fr/DHekSS5aLfH9bdlfnSvKAWsAgJhIbogyBg==", "dev": true, "funding": [ { @@ -4077,13 +3913,13 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { "micromark-util-symbol": "^1.0.0" } }, "node_modules/micromark-util-resolve-all": { "version": "1.0.0", - "integrity": "sha512-CB/AGk98u50k42kvgaMM94wzBqozSzDDaonKU7P7jwQIuH2RU0TeBqGYJz2WY1UdihhjweivStrJ2JdkdEmcfw==", "dev": true, "funding": [ { @@ -4095,13 +3931,13 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { "micromark-util-types": "^1.0.0" } }, "node_modules/micromark-util-sanitize-uri": { "version": "1.0.0", - "integrity": "sha512-cCxvBKlmac4rxCGx6ejlIviRaMKZc0fWm5HdCHEeDWRSkn44l6NdYVRyU+0nT1XC72EQJMZV8IPHF+jTr56lAg==", "dev": true, "funding": [ { @@ -4113,6 +3949,7 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { "micromark-util-character": "^1.0.0", "micromark-util-encode": "^1.0.0", @@ -4121,7 +3958,6 @@ }, "node_modules/micromark-util-subtokenize": { "version": "1.0.2", - "integrity": "sha512-d90uqCnXp/cy4G881Ub4psE57Sf8YD0pim9QdjCRNjfas2M1u6Lbt+XZK9gnHL2XFhnozZiEdCa9CNfXSfQ6xA==", "dev": true, "funding": [ { @@ -4133,6 +3969,7 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { "micromark-util-chunked": "^1.0.0", "micromark-util-symbol": "^1.0.0", @@ -4142,7 +3979,6 @@ }, "node_modules/micromark-util-symbol": { "version": "1.0.1", - "integrity": "sha512-oKDEMK2u5qqAptasDAwWDXq0tG9AssVwAx3E9bBF3t/shRIGsWIRG+cGafs2p/SnDSOecnt6hZPCE2o6lHfFmQ==", "dev": true, "funding": [ { @@ -4153,11 +3989,11 @@ "type": "OpenCollective", "url": "https://opencollective.com/unified" } - ] + ], + "license": "MIT" }, "node_modules/micromark-util-types": { "version": "1.0.2", - "integrity": "sha512-DCfg/T8fcrhrRKTPjRrw/5LLvdGV7BHySf/1LOZx7TzWZdYRjogNtyNq885z3nNallwr3QUKARjqvHqX1/7t+w==", "dev": true, "funding": [ { @@ -4168,12 +4004,13 @@ "type": "OpenCollective", "url": "https://opencollective.com/unified" } - ] + ], + "license": "MIT" }, "node_modules/micromatch": { "version": "4.0.5", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, + "license": "MIT", "dependencies": { "braces": "^3.0.2", "picomatch": "^2.3.1" @@ -4184,8 +4021,8 @@ }, "node_modules/mime": { "version": "1.6.0", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "dev": true, + "license": "MIT", "bin": { "mime": "cli.js" }, @@ -4195,14 +4032,14 @@ }, "node_modules/mime-db": { "version": "1.52.0", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { "version": "2.1.35", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", "dependencies": { "mime-db": "1.52.0" }, @@ -4212,17 +4049,16 @@ }, "node_modules/mimic-fn": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/minimatch": { "version": "3.1.2", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -4232,13 +4068,13 @@ }, "node_modules/minimist": { "version": "1.2.6", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/minipass": { "version": "3.3.4", - "integrity": "sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw==", "dev": true, + "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, @@ -4248,8 +4084,8 @@ }, "node_modules/minizlib": { "version": "2.1.2", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", "dev": true, + "license": "MIT", "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" @@ -4260,8 +4096,8 @@ }, "node_modules/mkdirp": { "version": "1.0.4", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true, + "license": "MIT", "bin": { "mkdirp": "bin/cmd.js" }, @@ -4271,13 +4107,13 @@ }, "node_modules/mkdirp2": { "version": "1.0.5", - "integrity": "sha512-xOE9xbICroUDmG1ye2h4bZ8WBie9EGmACaco8K8cx6RlkJJrxGIqjGqztAI+NMhexXBcdGbSEzI6N3EJPevxZw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/mocha": { "version": "9.2.2", - "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", "dev": true, + "license": "MIT", "dependencies": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", @@ -4318,16 +4154,16 @@ }, "node_modules/mocha/node_modules/ansi-colors": { "version": "4.1.1", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/mocha/node_modules/debug": { "version": "4.3.3", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "dev": true, + "license": "MIT", "dependencies": { "ms": "2.1.2" }, @@ -4342,13 +4178,13 @@ }, "node_modules/mocha/node_modules/debug/node_modules/ms": { "version": "2.1.2", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/mocha/node_modules/escape-string-regexp": { "version": "4.0.0", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -4358,8 +4194,8 @@ }, "node_modules/mocha/node_modules/glob": { "version": "7.2.0", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, + "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -4377,8 +4213,8 @@ }, "node_modules/mocha/node_modules/glob/node_modules/minimatch": { "version": "3.1.2", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -4388,8 +4224,8 @@ }, "node_modules/mocha/node_modules/minimatch": { "version": "4.2.1", - "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -4399,21 +4235,21 @@ }, "node_modules/mocha/node_modules/ms": { "version": "2.1.3", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/mocha/node_modules/serialize-javascript": { "version": "6.0.0", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "randombytes": "^2.1.0" } }, "node_modules/mocha/node_modules/yargs": { "version": "16.2.0", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, + "license": "MIT", "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", @@ -4429,22 +4265,21 @@ }, "node_modules/mri": { "version": "1.2.0", - "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/ms": { "version": "2.1.2", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/mylas": { "version": "2.1.11", - "resolved": "https://registry.npmjs.org/mylas/-/mylas-2.1.11.tgz", - "integrity": "sha512-krnPUl3n9/k52FGCltWMYcqp9SttxjRJEy0sWLk+g7mIa7wnZrmNSZ40Acx7ghzRSOsxt2rEqMbaq4jWlnTDKg==", "dev": true, + "license": "MIT", "engines": { "node": ">=12.0.0" }, @@ -4455,8 +4290,8 @@ }, "node_modules/nanoid": { "version": "3.3.1", - "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", "dev": true, + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -4466,12 +4301,12 @@ }, "node_modules/neo-async": { "version": "2.6.2", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/node-fetch": { "version": "2.6.7", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "license": "MIT", "dependencies": { "whatwg-url": "^5.0.0" }, @@ -4489,22 +4324,21 @@ }, "node_modules/node-releases": { "version": "2.0.6", - "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/normalize-path": { "version": "3.0.0", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/npm-run-path": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.0.0" }, @@ -4514,30 +4348,29 @@ }, "node_modules/object-get": { "version": "2.1.1", - "integrity": "sha512-7n4IpLMzGGcLEMiQKsNR7vCe+N5E9LORFrtNUVy4sO3dj9a3HedZCxEL2T7QuLhcHN1NBuBsMOKaOsAYI9IIvg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/object-to-spawn-args": { "version": "2.0.1", - "integrity": "sha512-6FuKFQ39cOID+BMZ3QaphcC8Y4cw6LXBLyIgPU+OhIYwviJamPAn+4mITapnSBQrejB+NNp+FMskhD8Cq+Ys3w==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.0.0" } }, "node_modules/once": { "version": "1.4.0", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, + "license": "ISC", "dependencies": { "wrappy": "1" } }, "node_modules/onetime": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, + "license": "MIT", "dependencies": { "mimic-fn": "^2.1.0" }, @@ -4550,22 +4383,21 @@ }, "node_modules/opener": { "version": "1.5.2", - "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", "dev": true, + "license": "(WTFPL OR MIT)", "bin": { "opener": "bin/opener-bin.js" } }, "node_modules/ospath": { "version": "1.2.2", - "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz", - "integrity": "sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/p-limit": { "version": "3.1.0", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, + "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" }, @@ -4578,8 +4410,8 @@ }, "node_modules/p-locate": { "version": "5.0.0", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, + "license": "MIT", "dependencies": { "p-limit": "^3.0.2" }, @@ -4592,9 +4424,8 @@ }, "node_modules/p-map": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", "dev": true, + "license": "MIT", "dependencies": { "aggregate-error": "^3.0.0" }, @@ -4607,70 +4438,68 @@ }, "node_modules/p-try": { "version": "2.2.0", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/path-exists": { "version": "4.0.0", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/path-is-absolute": { "version": "1.0.1", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/path-key": { "version": "3.1.1", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/path-parse": { "version": "1.0.7", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/path-type": { "version": "4.0.0", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/pend": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/performance-now": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/picocolors": { "version": "1.0.0", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -4680,17 +4509,16 @@ }, "node_modules/pify": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/pkg-dir": { "version": "4.2.0", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, + "license": "MIT", "dependencies": { "find-up": "^4.0.0" }, @@ -4700,8 +4528,8 @@ }, "node_modules/pkg-dir/node_modules/find-up": { "version": "4.1.0", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, + "license": "MIT", "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -4712,8 +4540,8 @@ }, "node_modules/pkg-dir/node_modules/locate-path": { "version": "5.0.0", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, + "license": "MIT", "dependencies": { "p-locate": "^4.1.0" }, @@ -4723,8 +4551,8 @@ }, "node_modules/pkg-dir/node_modules/p-limit": { "version": "2.3.0", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, + "license": "MIT", "dependencies": { "p-try": "^2.0.0" }, @@ -4737,8 +4565,8 @@ }, "node_modules/pkg-dir/node_modules/p-locate": { "version": "4.1.0", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, + "license": "MIT", "dependencies": { "p-limit": "^2.2.0" }, @@ -4748,17 +4576,16 @@ }, "node_modules/plimit-lit": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/plimit-lit/-/plimit-lit-1.3.0.tgz", - "integrity": "sha512-63qOoSzggsjBHPVbaKeEtsO8oWTeyJxUfVRLkoc59pRkDiCCLvqn2tCgAkfbejrx+V5zJtlG2wqkk/Db6cwlVQ==", "dev": true, + "license": "MIT", "dependencies": { "queue-lit": "^1.3.0" } }, "node_modules/portfinder": { "version": "1.0.29", - "integrity": "sha512-Z5+DarHWCKlufshB9Z1pN95oLtANoY5Wn9X3JGELGyQ6VhEcBfT2t+1fGUBq7MwUant6g/mqowH+4HifByPbiQ==", "dev": true, + "license": "MIT", "dependencies": { "async": "^2.6.4", "debug": "^3.2.7", @@ -4770,24 +4597,24 @@ }, "node_modules/portfinder/node_modules/async": { "version": "2.6.4", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", "dev": true, + "license": "MIT", "dependencies": { "lodash": "^4.17.14" } }, "node_modules/portfinder/node_modules/debug": { "version": "3.2.7", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, + "license": "MIT", "dependencies": { "ms": "^2.1.1" } }, "node_modules/portfinder/node_modules/mkdirp": { "version": "0.5.6", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, + "license": "MIT", "dependencies": { "minimist": "^1.2.6" }, @@ -4797,9 +4624,8 @@ }, "node_modules/pretty-bytes": { "version": "5.6.0", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", - "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" }, @@ -4809,21 +4635,18 @@ }, "node_modules/proxy-from-env": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", - "integrity": "sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/psl": { "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/pump": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, + "license": "MIT", "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -4831,29 +4654,27 @@ }, "node_modules/punycode": { "version": "2.1.1", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/qs": { "version": "6.5.3", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.6" } }, "node_modules/queue-lit": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/queue-lit/-/queue-lit-1.3.0.tgz", - "integrity": "sha512-3HpQ7bD2ct56qPithPr5IzH2Oij3WVPcgevCJ+mI9HAfVJKCqQ2yiFYgb4AUkLfsCmmbVDy9luvE97Ztzr5eBg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/queue-microtask": { "version": "1.2.3", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true, "funding": [ { @@ -4868,20 +4689,21 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/randombytes": { "version": "2.1.0", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, + "license": "MIT", "dependencies": { "safe-buffer": "^5.1.0" } }, "node_modules/readdirp": { "version": "3.6.0", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, + "license": "MIT", "dependencies": { "picomatch": "^2.2.1" }, @@ -4891,8 +4713,8 @@ }, "node_modules/rechoir": { "version": "0.7.1", - "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", "dev": true, + "license": "MIT", "dependencies": { "resolve": "^1.9.0" }, @@ -4902,8 +4724,8 @@ }, "node_modules/reduce-extract": { "version": "1.0.0", - "integrity": "sha512-QF8vjWx3wnRSL5uFMyCjDeDc5EBMiryoT9tz94VvgjKfzecHAVnqmXAwQDcr7X4JmLc2cjkjFGCVzhMqDjgR9g==", "dev": true, + "license": "MIT", "dependencies": { "test-value": "^1.0.1" }, @@ -4913,8 +4735,8 @@ }, "node_modules/reduce-extract/node_modules/array-back": { "version": "1.0.4", - "integrity": "sha512-1WxbZvrmyhkNoeYcizokbmh5oiOCIfyvGtcqbK3Ls1v1fKcquzxnQSceOx6tzq7jmai2kFLWIpGND2cLhH6TPw==", "dev": true, + "license": "MIT", "dependencies": { "typical": "^2.6.0" }, @@ -4924,8 +4746,8 @@ }, "node_modules/reduce-extract/node_modules/test-value": { "version": "1.1.0", - "integrity": "sha512-wrsbRo7qP+2Je8x8DsK8ovCGyxe3sYfQwOraIY/09A2gFXU9DYKiTF14W4ki/01AEh56kMzAmlj9CaHGDDUBJA==", "dev": true, + "license": "MIT", "dependencies": { "array-back": "^1.0.2", "typical": "^2.4.2" @@ -4936,24 +4758,24 @@ }, "node_modules/reduce-flatten": { "version": "3.0.1", - "integrity": "sha512-bYo+97BmUUOzg09XwfkwALt4PQH1M5L0wzKerBt6WLm3Fhdd43mMS89HiT1B9pJIqko/6lWx3OnV4J9f2Kqp5Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/reduce-unique": { "version": "2.0.1", - "integrity": "sha512-x4jH/8L1eyZGR785WY+ePtyMNhycl1N2XOLxhCbzZFaqF4AXjLzqSxa2UHgJ2ZVR/HHyPOvl1L7xRnW8ye5MdA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/reduce-without": { "version": "1.0.1", - "integrity": "sha512-zQv5y/cf85sxvdrKPlfcRzlDn/OqKFThNimYmsS3flmkioKvkUGn2Qg9cJVoQiEvdxFGLE0MQER/9fZ9sUqdxg==", "dev": true, + "license": "MIT", "dependencies": { "test-value": "^2.0.0" }, @@ -4963,8 +4785,8 @@ }, "node_modules/reduce-without/node_modules/array-back": { "version": "1.0.4", - "integrity": "sha512-1WxbZvrmyhkNoeYcizokbmh5oiOCIfyvGtcqbK3Ls1v1fKcquzxnQSceOx6tzq7jmai2kFLWIpGND2cLhH6TPw==", "dev": true, + "license": "MIT", "dependencies": { "typical": "^2.6.0" }, @@ -4974,8 +4796,8 @@ }, "node_modules/reduce-without/node_modules/test-value": { "version": "2.1.0", - "integrity": "sha512-+1epbAxtKeXttkGFMTX9H42oqzOTufR1ceCF+GYA5aOmvaPq9wd4PUS8329fn2RRLGNeUkgRLnVpycjx8DsO2w==", "dev": true, + "license": "MIT", "dependencies": { "array-back": "^1.0.3", "typical": "^2.6.0" @@ -4986,8 +4808,8 @@ }, "node_modules/remark-parse": { "version": "10.0.1", - "integrity": "sha512-1fUyHr2jLsVOkhbvPRBJ5zTKZZyD6yZzYaWCS6BPBdQ8vEMBCH+9zNCDA6tET/zHCi/jLqjCWtlJZUPk+DbnFw==", "dev": true, + "license": "MIT", "dependencies": { "@types/mdast": "^3.0.0", "mdast-util-from-markdown": "^1.0.0", @@ -5000,44 +4822,42 @@ }, "node_modules/request-progress": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", - "integrity": "sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==", "dev": true, + "license": "MIT", "dependencies": { "throttleit": "^1.0.0" } }, "node_modules/require-directory": { "version": "2.1.1", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/require-main-filename": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/requires-port": { "version": "1.0.0", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/requizzle": { "version": "0.2.3", - "integrity": "sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==", "dev": true, + "license": "MIT", "dependencies": { "lodash": "^4.17.14" } }, "node_modules/resolve": { "version": "1.22.1", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", "dev": true, + "license": "MIT", "dependencies": { "is-core-module": "^2.9.0", "path-parse": "^1.0.7", @@ -5052,8 +4872,8 @@ }, "node_modules/resolve-cwd": { "version": "3.0.0", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, + "license": "MIT", "dependencies": { "resolve-from": "^5.0.0" }, @@ -5063,17 +4883,16 @@ }, "node_modules/resolve-from": { "version": "5.0.0", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/restore-cursor": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "dev": true, + "license": "MIT", "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" @@ -5084,8 +4903,8 @@ }, "node_modules/reusify": { "version": "1.0.4", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true, + "license": "MIT", "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -5093,14 +4912,13 @@ }, "node_modules/rfdc": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", - "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/rimraf": { "version": "3.0.2", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, + "license": "ISC", "dependencies": { "glob": "^7.1.3" }, @@ -5113,7 +4931,6 @@ }, "node_modules/run-parallel": { "version": "1.2.0", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, "funding": [ { @@ -5129,22 +4946,23 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "dependencies": { "queue-microtask": "^1.2.2" } }, "node_modules/rxjs": { "version": "7.5.6", - "integrity": "sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw==", "dev": true, + "license": "Apache-2.0", "dependencies": { "tslib": "^2.1.0" } }, "node_modules/sade": { "version": "1.8.1", - "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", "dev": true, + "license": "MIT", "dependencies": { "mri": "^1.1.0" }, @@ -5154,7 +4972,6 @@ }, "node_modules/safe-buffer": { "version": "5.2.1", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true, "funding": [ { @@ -5169,17 +4986,18 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/safer-buffer": { "version": "2.1.2", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/schema-utils": { "version": "3.1.1", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", "dev": true, + "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", @@ -5195,14 +5013,13 @@ }, "node_modules/secure-compare": { "version": "3.0.1", - "integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/semver": { "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, + "license": "ISC", "dependencies": { "lru-cache": "^6.0.0" }, @@ -5215,22 +5032,21 @@ }, "node_modules/serialize-javascript": { "version": "5.0.1", - "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "randombytes": "^2.1.0" } }, "node_modules/set-blocking": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/shallow-clone": { "version": "3.0.1", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", "dev": true, + "license": "MIT", "dependencies": { "kind-of": "^6.0.2" }, @@ -5240,8 +5056,8 @@ }, "node_modules/shebang-command": { "version": "2.0.0", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, + "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, @@ -5251,36 +5067,34 @@ }, "node_modules/shebang-regex": { "version": "3.0.0", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/shell-quote": { "version": "1.7.3", - "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/signal-exit": { "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/slash": { "version": "3.0.0", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/slice-ansi": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", @@ -5292,8 +5106,8 @@ }, "node_modules/sort-array": { "version": "4.1.5", - "integrity": "sha512-Ya4peoS1fgFN42RN1REk2FgdNOeLIEMKFGJvs7VTP3OklF8+kl2SkpVliZ4tk/PurWsrWRsdNdU+tgyOBkB9sA==", "dev": true, + "license": "MIT", "dependencies": { "array-back": "^5.0.0", "typical": "^6.0.1" @@ -5304,32 +5118,32 @@ }, "node_modules/sort-array/node_modules/array-back": { "version": "5.0.0", - "integrity": "sha512-kgVWwJReZWmVuWOQKEOohXKJX+nD02JAZ54D1RRWlv8L0NebauKAaFxACKzB74RTclt1+WNz5KHaLRDAPZbDEw==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" } }, "node_modules/sort-array/node_modules/typical": { "version": "6.0.1", - "integrity": "sha512-+g3NEp7fJLe9DPa1TArHm9QAA7YciZmWnfAqEaFrBihQ7epOv9i99rjtgb6Iz0wh3WuQDjsCTDfgRoGnmHN81A==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" } }, "node_modules/source-map": { "version": "0.6.1", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, "node_modules/source-map-support": { "version": "0.5.21", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, + "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -5337,14 +5151,13 @@ }, "node_modules/spawn-command": { "version": "0.0.2-1", - "integrity": "sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/sshpk": { "version": "1.17.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", - "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", "dev": true, + "license": "MIT", "dependencies": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", @@ -5367,8 +5180,8 @@ }, "node_modules/stream-connect": { "version": "1.0.2", - "integrity": "sha512-68Kl+79cE0RGKemKkhxTSg8+6AGrqBt+cbZAXevg2iJ6Y3zX4JhA/sZeGzLpxW9cXhmqAcE7KnJCisUmIUfnFQ==", "dev": true, + "license": "MIT", "dependencies": { "array-back": "^1.0.2" }, @@ -5378,8 +5191,8 @@ }, "node_modules/stream-connect/node_modules/array-back": { "version": "1.0.4", - "integrity": "sha512-1WxbZvrmyhkNoeYcizokbmh5oiOCIfyvGtcqbK3Ls1v1fKcquzxnQSceOx6tzq7jmai2kFLWIpGND2cLhH6TPw==", "dev": true, + "license": "MIT", "dependencies": { "typical": "^2.6.0" }, @@ -5389,16 +5202,16 @@ }, "node_modules/stream-via": { "version": "1.0.4", - "integrity": "sha512-DBp0lSvX5G9KGRDTkR/R+a29H+Wk2xItOF+MpZLLNDWbEV9tGPnqLPxHEYjmiz8xGtJHRIqmI+hCjmNzqoA4nQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/string-width": { "version": "4.2.3", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -5410,16 +5223,16 @@ }, "node_modules/string-width/node_modules/ansi-regex": { "version": "5.0.1", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/string-width/node_modules/strip-ansi": { "version": "6.0.1", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -5429,9 +5242,8 @@ }, "node_modules/strip-ansi": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^3.0.0" }, @@ -5441,8 +5253,8 @@ }, "node_modules/strip-bom": { "version": "3.0.0", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, + "license": "MIT", "optional": true, "engines": { "node": ">=4" @@ -5450,17 +5262,16 @@ }, "node_modules/strip-final-newline": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/strip-json-comments": { "version": "3.1.1", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -5470,8 +5281,8 @@ }, "node_modules/supports-color": { "version": "8.1.1", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -5484,8 +5295,8 @@ }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -5495,8 +5306,8 @@ }, "node_modules/table-layout": { "version": "0.4.5", - "integrity": "sha512-zTvf0mcggrGeTe/2jJ6ECkJHAQPIYEwDoqsiqBjI24mvRmQbInK5jq33fyypaCBxX08hMkfmdOqj6haT33EqWw==", "dev": true, + "license": "MIT", "dependencies": { "array-back": "^2.0.0", "deep-extend": "~0.6.0", @@ -5510,8 +5321,8 @@ }, "node_modules/table-layout/node_modules/array-back": { "version": "2.0.0", - "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", "dev": true, + "license": "MIT", "dependencies": { "typical": "^2.6.1" }, @@ -5521,21 +5332,20 @@ }, "node_modules/taffydb": { "version": "2.6.2", - "integrity": "sha512-y3JaeRSplks6NYQuCOj3ZFMO3j60rTwbuKCvZxsAraGYH2epusatvZ0baZYA01WsGqJBq/Dl6vOrMUJqyMj8kA==", "dev": true }, "node_modules/tapable": { "version": "2.2.1", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/tar": { "version": "6.1.11", - "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", "dev": true, + "license": "ISC", "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", @@ -5550,21 +5360,21 @@ }, "node_modules/tar/node_modules/chownr": { "version": "2.0.0", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", "dev": true, + "license": "ISC", "engines": { "node": ">=10" } }, "node_modules/temp-path": { "version": "1.0.0", - "integrity": "sha512-TvmyH7kC6ZVTYkqCODjJIbgvu0FKiwQpZ4D1aknE7xpcDf/qEOB8KZEK5ef2pfbVoiBhNWs3yx4y+ESMtNYmlg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/terser": { "version": "5.14.2", - "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.2", "acorn": "^8.5.0", @@ -5580,8 +5390,8 @@ }, "node_modules/terser-webpack-plugin": { "version": "5.3.3", - "integrity": "sha512-Fx60G5HNYknNTNQnzQ1VePRuu89ZVYWfjRAeT5rITuCY/1b08s49e5kSQwHDirKZWuoKOBRFS98EUUoZ9kLEwQ==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "^0.3.7", "jest-worker": "^27.4.5", @@ -5613,21 +5423,21 @@ }, "node_modules/terser-webpack-plugin/node_modules/serialize-javascript": { "version": "6.0.0", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "randombytes": "^2.1.0" } }, "node_modules/terser/node_modules/commander": { "version": "2.20.3", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/test-value": { "version": "3.0.0", - "integrity": "sha512-sVACdAWcZkSU9x7AOmJo5TqE+GyNJknHaHsMrR6ZnhjVlVN9Yx6FjHrsKZ3BjIpPCT68zYesPWkakrNupwfOTQ==", "dev": true, + "license": "MIT", "dependencies": { "array-back": "^2.0.0", "typical": "^2.6.1" @@ -5638,8 +5448,8 @@ }, "node_modules/test-value/node_modules/array-back": { "version": "2.0.0", - "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", "dev": true, + "license": "MIT", "dependencies": { "typical": "^2.6.1" }, @@ -5649,28 +5459,23 @@ }, "node_modules/text-encoding": { "version": "0.7.0", - "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.7.0.tgz", - "integrity": "sha512-oJQ3f1hrOnbRLOcwKz0Liq2IcrvDeZRHXhd9RgLrsT+DjWY/nty1Hi7v3dtkaEYbPYe0mUoOfzRrMwfXXwgPUA==", - "deprecated": "no longer maintained", + "license": "(Unlicense OR Apache-2.0)", "peer": true }, "node_modules/throttleit": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz", - "integrity": "sha512-rkTVqu6IjfQ/6+uNuuc3sZek4CEYxTJom3IktzgdSxcZqdARuebbA/f4QmAxMQIxqq9ZLEUkSYqvuk1I6VKq4g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/through": { "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/tmp": { "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", "dev": true, + "license": "MIT", "dependencies": { "rimraf": "^3.0.0" }, @@ -5680,8 +5485,8 @@ }, "node_modules/to-regex-range": { "version": "5.0.1", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -5691,9 +5496,8 @@ }, "node_modules/tough-cookie": { "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "psl": "^1.1.28", "punycode": "^2.1.1" @@ -5704,20 +5508,20 @@ }, "node_modules/tr46": { "version": "0.0.3", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + "license": "MIT" }, "node_modules/tree-kill": { "version": "1.2.2", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", "dev": true, + "license": "MIT", "bin": { "tree-kill": "cli.js" } }, "node_modules/trough": { "version": "2.1.0", - "integrity": "sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==", "dev": true, + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -5725,8 +5529,8 @@ }, "node_modules/ts-mocha": { "version": "9.0.2", - "integrity": "sha512-WyQjvnzwrrubl0JT7EC1yWmNpcsU3fOuBFfdps30zbmFBgKniSaSOyZMZx+Wq7kytUs5CY+pEbSYEbGfIKnXTw==", "dev": true, + "license": "MIT", "dependencies": { "ts-node": "7.0.1" }, @@ -5745,17 +5549,16 @@ }, "node_modules/ts-mocha/node_modules/diff": { "version": "3.5.0", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } }, "node_modules/ts-mocha/node_modules/json5": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "minimist": "^1.2.0" @@ -5766,8 +5569,8 @@ }, "node_modules/ts-mocha/node_modules/mkdirp": { "version": "0.5.6", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, + "license": "MIT", "dependencies": { "minimist": "^1.2.6" }, @@ -5777,8 +5580,8 @@ }, "node_modules/ts-mocha/node_modules/ts-node": { "version": "7.0.1", - "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==", "dev": true, + "license": "MIT", "dependencies": { "arrify": "^1.0.0", "buffer-from": "^1.1.0", @@ -5798,9 +5601,8 @@ }, "node_modules/ts-mocha/node_modules/tsconfig-paths": { "version": "3.14.1", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", - "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "@types/json5": "^0.0.29", @@ -5811,16 +5613,16 @@ }, "node_modules/ts-mocha/node_modules/yn": { "version": "2.0.0", - "integrity": "sha512-uTv8J/wiWTgUTg+9vLTi//leUl5vDQS6uii/emeTb2ssY7vl6QWf2fFbIIGjnhjvbdKlU0ed7QPgY1htTC86jQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/ts-node": { "version": "10.9.1", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", "dev": true, + "license": "MIT", "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -5861,17 +5663,16 @@ }, "node_modules/ts-node/node_modules/diff": { "version": "4.0.2", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } }, "node_modules/tsc-alias": { "version": "1.7.0", - "resolved": "https://registry.npmjs.org/tsc-alias/-/tsc-alias-1.7.0.tgz", - "integrity": "sha512-n/K6g8S7Ec7Y/A2Z77Ikp2Uv1S1ERtT63ni69XV4W1YPT4rnNmz8ItgIiJYvKfFnKfqcZQ81UPjoKpMTxaC/rg==", "dev": true, + "license": "MIT", "dependencies": { "chokidar": "^3.5.3", "commander": "^9.0.0", @@ -5886,23 +5687,21 @@ }, "node_modules/tsc-alias/node_modules/commander": { "version": "9.4.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.0.tgz", - "integrity": "sha512-sRPT+umqkz90UA8M1yqYfnHlZA7fF6nSphDtxeywPZ49ysjxDQybzk13CL+mXekDRG92skbcqCLVovuCusNmFw==", "dev": true, + "license": "MIT", "engines": { "node": "^12.20.0 || >=14" } }, "node_modules/tslib": { "version": "2.4.0", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", - "dev": true + "dev": true, + "license": "0BSD" }, "node_modules/tunnel-agent": { "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", "dev": true, + "license": "Apache-2.0", "dependencies": { "safe-buffer": "^5.0.1" }, @@ -5912,14 +5711,13 @@ }, "node_modules/tweetnacl": { "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", - "dev": true + "dev": true, + "license": "Unlicense" }, "node_modules/txm": { "version": "8.0.2", - "integrity": "sha512-X6gUOeUAXzIrUvxp0zvOiJBM0l6Zk8NexaYugac6c6PW7dbrjgz6zC/rfSEjtd29VejI3VKvAnc6pDdOfSNKug==", "dev": true, + "license": "ISC", "dependencies": { "async": "^3.2.1", "diff-match-patch": "^1.0.5", @@ -5937,8 +5735,8 @@ }, "node_modules/txm/node_modules/supports-color": { "version": "9.2.2", - "integrity": "sha512-XC6g/Kgux+rJXmwokjm9ECpD6k/smUoS5LKlUCcsYr4IY3rW0XyAympon2RmxGrlnZURMpg5T18gWDP9CsHXFA==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -5948,9 +5746,8 @@ }, "node_modules/type-fest": { "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -5960,8 +5757,8 @@ }, "node_modules/typescript": { "version": "4.7.4", - "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -5972,18 +5769,18 @@ }, "node_modules/typical": { "version": "2.6.1", - "integrity": "sha512-ofhi8kjIje6npGozTip9Fr8iecmYfEbS06i0JnIg+rh51KakryWF4+jX8lLKZVhy6N+ID45WYSFCxPOdTWCzNg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/uc.micro": { "version": "1.0.6", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/uglify-js": { "version": "3.16.3", - "integrity": "sha512-uVbFqx9vvLhQg0iBaau9Z75AxWJ8tqM9AV890dIZCLApF4rTcyHwmAvLeEdYRs+BzYWu8Iw81F79ah0EfTXbaw==", "dev": true, + "license": "BSD-2-Clause", "optional": true, "bin": { "uglifyjs": "bin/uglifyjs" @@ -5994,13 +5791,13 @@ }, "node_modules/underscore": { "version": "1.13.4", - "integrity": "sha512-BQFnUDuAQ4Yf/cYY5LNrK9NCJFKriaRbD9uR1fTeXnBeoa97W0i41qkZfGO9pSo8I5KzjAcSY2XYtdf0oKd7KQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/unified": { "version": "10.1.2", - "integrity": "sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==", "dev": true, + "license": "MIT", "dependencies": { "@types/unist": "^2.0.0", "bail": "^2.0.0", @@ -6017,7 +5814,6 @@ }, "node_modules/union": { "version": "0.5.0", - "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==", "dev": true, "dependencies": { "qs": "^6.4.0" @@ -6028,8 +5824,8 @@ }, "node_modules/unist-util-stringify-position": { "version": "3.0.2", - "integrity": "sha512-7A6eiDCs9UtjcwZOcCpM4aPII3bAAGv13E96IkawkOAW0OhH+yRxtY0lzo8KiHpzEMfH7Q+FizUmwp8Iqy5EWg==", "dev": true, + "license": "MIT", "dependencies": { "@types/unist": "^2.0.0" }, @@ -6040,24 +5836,22 @@ }, "node_modules/universalify": { "version": "2.0.0", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 10.0.0" } }, "node_modules/untildify": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", - "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/update-browserslist-db": { "version": "1.0.5", - "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==", "dev": true, "funding": [ { @@ -6069,6 +5863,7 @@ "url": "https://tidelift.com/funding/github/npm/browserslist" } ], + "license": "MIT", "dependencies": { "escalade": "^3.1.1", "picocolors": "^1.0.0" @@ -6082,30 +5877,29 @@ }, "node_modules/uri-js": { "version": "4.4.1", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" } }, "node_modules/url-join": { "version": "4.0.1", - "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/uuid": { "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true, + "license": "MIT", "bin": { "uuid": "dist/bin/uuid" } }, "node_modules/uvu": { "version": "0.5.6", - "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", "dev": true, + "license": "MIT", "dependencies": { "dequal": "^2.0.0", "diff": "^5.0.0", @@ -6121,17 +5915,16 @@ }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/verror": { "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", "dev": true, "engines": [ "node >=0.6.0" ], + "license": "MIT", "dependencies": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", @@ -6140,8 +5933,8 @@ }, "node_modules/vfile": { "version": "5.3.4", - "integrity": "sha512-KI+7cnst03KbEyN1+JE504zF5bJBZa+J+CrevLeyIMq0aPU681I2rQ5p4PlnQ6exFtWiUrg26QUdFMnAKR6PIw==", "dev": true, + "license": "MIT", "dependencies": { "@types/unist": "^2.0.0", "is-buffer": "^2.0.0", @@ -6155,8 +5948,8 @@ }, "node_modules/vfile-message": { "version": "3.1.2", - "integrity": "sha512-QjSNP6Yxzyycd4SVOtmKKyTsSvClqBPJcd00Z0zuPj3hOIjg0rUPG6DbFGPvUKRgYyaIWLPKpuEclcuvb3H8qA==", "dev": true, + "license": "MIT", "dependencies": { "@types/unist": "^2.0.0", "unist-util-stringify-position": "^3.0.0" @@ -6168,17 +5961,17 @@ }, "node_modules/walk-back": { "version": "5.1.0", - "integrity": "sha512-Uhxps5yZcVNbLEAnb+xaEEMdgTXl9qAQDzKYejG2AZ7qPwRQ81lozY9ECDbjLPNWm7YsO1IK5rsP1KoQzXAcGA==", "dev": true, + "license": "MIT", "engines": { "node": ">=12.17" } }, "node_modules/wasm-opt": { "version": "1.3.0", - "integrity": "sha512-24+IOboX4Sav0bI8Krwf0Y6dnpN4KxYtqpl0qWt86qVLsmayUqx1KMBrJTlQWNC+/dsqzQJjK6QvxNJsZYOgJg==", "dev": true, "hasInstallScript": true, + "license": "MIT", "dependencies": { "node-fetch": "^2.6.7", "tar": "^6.1.11" @@ -6189,9 +5982,9 @@ }, "node_modules/wasm-pack": { "version": "0.10.1", - "integrity": "sha512-bw480KaaJQhL6UX8wAm6YCO497uaULDrsvUey3EB86dKAfFc6qCGVN1kItcwUklEeufonwo9mwz9/fy9xLOPtQ==", "dev": true, "hasInstallScript": true, + "license": "MIT OR Apache-2.0", "dependencies": { "binary-install": "^0.1.0" }, @@ -6201,8 +5994,8 @@ }, "node_modules/watchpack": { "version": "2.4.0", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", "dev": true, + "license": "MIT", "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -6213,12 +6006,12 @@ }, "node_modules/webidl-conversions": { "version": "3.0.1", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + "license": "BSD-2-Clause" }, "node_modules/webpack": { "version": "5.74.0", - "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==", "dev": true, + "license": "MIT", "dependencies": { "@types/eslint-scope": "^3.7.3", "@types/estree": "^0.0.51", @@ -6263,8 +6056,8 @@ }, "node_modules/webpack-cli": { "version": "4.10.0", - "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", "dev": true, + "license": "MIT", "dependencies": { "@discoveryjs/json-ext": "^0.5.0", "@webpack-cli/configtest": "^1.2.0", @@ -6309,16 +6102,16 @@ }, "node_modules/webpack-cli/node_modules/commander": { "version": "7.2.0", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 10" } }, "node_modules/webpack-merge": { "version": "5.8.0", - "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", "dev": true, + "license": "MIT", "dependencies": { "clone-deep": "^4.0.1", "wildcard": "^2.0.0" @@ -6329,16 +6122,16 @@ }, "node_modules/webpack-sources": { "version": "3.2.3", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", "dev": true, + "license": "MIT", "engines": { "node": ">=10.13.0" } }, "node_modules/whatwg-encoding": { "version": "2.0.0", - "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", "dev": true, + "license": "MIT", "dependencies": { "iconv-lite": "0.6.3" }, @@ -6348,7 +6141,7 @@ }, "node_modules/whatwg-url": { "version": "5.0.0", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -6356,8 +6149,8 @@ }, "node_modules/which": { "version": "2.0.2", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, + "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -6370,33 +6163,29 @@ }, "node_modules/which-module": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/wide-align": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dev": true, + "license": "ISC", "dependencies": { "string-width": "^1.0.2 || 2" } }, "node_modules/wide-align/node_modules/is-fullwidth-code-point": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/wide-align/node_modules/string-width": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, + "license": "MIT", "dependencies": { "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^4.0.0" @@ -6407,18 +6196,18 @@ }, "node_modules/wildcard": { "version": "2.0.0", - "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/wordwrap": { "version": "1.0.0", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/wordwrapjs": { "version": "3.0.0", - "integrity": "sha512-mO8XtqyPvykVCsrwj5MlOVWvSnCdT+C+QVbm6blradR7JExAhbkZ7hZ9A+9NUtwzSqrlUo9a67ws0EiILrvRpw==", "dev": true, + "license": "MIT", "dependencies": { "reduce-flatten": "^1.0.1", "typical": "^2.6.1" @@ -6429,21 +6218,21 @@ }, "node_modules/wordwrapjs/node_modules/reduce-flatten": { "version": "1.0.1", - "integrity": "sha512-j5WfFJfc9CoXv/WbwVLHq74i/hdTUpy+iNC534LxczMRP67vJeK3V9JOdnL0N1cIRbn9mYhE2yVjvvKXDxvNXQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/workerpool": { "version": "6.2.0", - "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/wrap-ansi": { "version": "7.0.0", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -6458,16 +6247,16 @@ }, "node_modules/wrap-ansi/node_modules/ansi-regex": { "version": "5.0.1", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/wrap-ansi/node_modules/strip-ansi": { "version": "6.0.1", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -6477,31 +6266,31 @@ }, "node_modules/wrappy": { "version": "1.0.2", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/xmlcreate": { "version": "2.0.4", - "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/y18n": { "version": "5.0.8", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, + "license": "ISC", "engines": { "node": ">=10" } }, "node_modules/yallist": { "version": "4.0.0", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/yargs": { "version": "17.5.1", - "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", "dev": true, + "license": "MIT", "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", @@ -6517,16 +6306,16 @@ }, "node_modules/yargs-parser": { "version": "20.2.4", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", "dev": true, + "license": "ISC", "engines": { "node": ">=10" } }, "node_modules/yargs-unparser": { "version": "2.0.0", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", "dev": true, + "license": "MIT", "dependencies": { "camelcase": "^6.0.0", "decamelize": "^4.0.0", @@ -6539,25 +6328,24 @@ }, "node_modules/yargs-unparser/node_modules/is-plain-obj": { "version": "2.1.0", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/yargs/node_modules/yargs-parser": { "version": "21.1.1", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, + "license": "ISC", "engines": { "node": ">=12" } }, "node_modules/yauzl": { "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", "dev": true, + "license": "MIT", "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" @@ -6565,16 +6353,16 @@ }, "node_modules/yn": { "version": "3.1.1", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/yocto-queue": { "version": "0.1.0", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -6586,38 +6374,22 @@ "dependencies": { "@babel/parser": { "version": "7.18.11", - "integrity": "sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==", "dev": true }, "@colors/colors": { "version": "1.5.0", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", "dev": true, "optional": true }, "@cspotcode/source-map-support": { "version": "0.8.1", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, "requires": { "@jridgewell/trace-mapping": "0.3.9" } }, - "@cycraig/iota-client-wasm": { - "version": "0.5.0-alpha.2", - "resolved": "https://registry.npmjs.org/@cycraig/iota-client-wasm/-/iota-client-wasm-0.5.0-alpha.2.tgz", - "integrity": "sha512-KbA7YUzEf+A1KoucQaPkKHTm8SFAB9ZX9WKAvRjgI+qh40xVubykbaj1cZ+x1UNekMK3K8Oc7uOmRtzBnsZZkA==", - "peer": true, - "requires": { - "@iota/types": "^1.0.0-beta.11", - "node-fetch": "^2.6.7", - "text-encoding": "^0.7.0" - } - }, "@cypress/request": { "version": "2.88.10", - "resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.10.tgz", - "integrity": "sha512-Zp7F+R93N0yZyG34GutyTNr+okam7s/Fzc1+i3kcqOP8vk6OuajuE9qZJ6Rs+10/1JFtXFYMdyarnU1rZuJesg==", "dev": true, "requires": { "aws-sign2": "~0.7.0", @@ -6642,8 +6414,6 @@ "dependencies": { "form-data": { "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", "dev": true, "requires": { "asynckit": "^0.4.0", @@ -6655,8 +6425,6 @@ }, "@cypress/xvfb": { "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz", - "integrity": "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==", "dev": true, "requires": { "debug": "^3.1.0", @@ -6665,8 +6433,6 @@ "dependencies": { "debug": { "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "requires": { "ms": "^2.1.1" @@ -6676,22 +6442,26 @@ }, "@discoveryjs/json-ext": { "version": "0.5.7", - "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", "dev": true }, "@iota/crypto.js": { "version": "1.9.0-stardust.6", - "resolved": "https://registry.npmjs.org/@iota/crypto.js/-/crypto.js-1.9.0-stardust.6.tgz", - "integrity": "sha512-jbruSRXlEKW2dgfPFG7e42ODBp4dVu2/CAh/oZrzMjehPdyF2tgAeN1woOqOAmVONWoGVV9GViZti3nShhGF1g==", "requires": { "@iota/util.js": "^1.9.0-stardust.5", "big-integer": "^1.6.51" } }, + "@iota/iota-client-wasm": { + "version": "0.5.0-alpha.2", + "peer": true, + "requires": { + "@iota/types": "^1.0.0-beta.11", + "node-fetch": "^2.6.7", + "text-encoding": "^0.7.0" + } + }, "@iota/iota.js": { "version": "1.9.0-stardust.25", - "resolved": "https://registry.npmjs.org/@iota/iota.js/-/iota.js-1.9.0-stardust.25.tgz", - "integrity": "sha512-mx/ZpkrUEgPjd1FxVxDKGpmDF/aTuUcRXjzZWjBL8v2w9dn3/Bs2fZ3AJw7wIwMG6v48AMXWq2yPwndpwDx/qw==", "peer": true, "requires": { "@iota/crypto.js": "^1.9.0-stardust.6", @@ -6701,20 +6471,16 @@ } }, "@iota/types": { - "version": "1.0.0-beta.11", - "integrity": "sha512-jFma7n4dpf+AikSIvY6CzrDDQ/TVnlV3HBIeOrDUDJku3oCcOkm8Jp/i0uKSD/krP2BAcDrWrNUr5Tn9MIw7+A==" + "version": "1.0.0-beta.11" }, "@iota/util.js": { "version": "1.9.0-stardust.5", - "resolved": "https://registry.npmjs.org/@iota/util.js/-/util.js-1.9.0-stardust.5.tgz", - "integrity": "sha512-BGXzzjUQQYaV/H0bXJYKW+Zgd9PIN+HLPH8BcYhubI65X4G2ID23/osmpK/za+Ds6t/MkpdPKIiBwZPGXuCUBw==", "requires": { "big-integer": "^1.6.51" } }, "@jridgewell/gen-mapping": { "version": "0.3.2", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", "dev": true, "requires": { "@jridgewell/set-array": "^1.0.1", @@ -6724,17 +6490,14 @@ }, "@jridgewell/resolve-uri": { "version": "3.1.0", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", "dev": true }, "@jridgewell/set-array": { "version": "1.1.2", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", "dev": true }, "@jridgewell/source-map": { "version": "0.3.2", - "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", "dev": true, "requires": { "@jridgewell/gen-mapping": "^0.3.0", @@ -6743,12 +6506,10 @@ }, "@jridgewell/sourcemap-codec": { "version": "1.4.14", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", "dev": true }, "@jridgewell/trace-mapping": { "version": "0.3.9", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", "dev": true, "requires": { "@jridgewell/resolve-uri": "^3.0.3", @@ -6757,7 +6518,6 @@ }, "@nodelib/fs.scandir": { "version": "2.1.5", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "requires": { "@nodelib/fs.stat": "2.0.5", @@ -6766,12 +6526,10 @@ }, "@nodelib/fs.stat": { "version": "2.0.5", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true }, "@nodelib/fs.walk": { "version": "1.2.8", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "requires": { "@nodelib/fs.scandir": "2.1.5", @@ -6780,27 +6538,22 @@ }, "@tsconfig/node10": { "version": "1.0.9", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", "dev": true }, "@tsconfig/node12": { "version": "1.0.11", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", "dev": true }, "@tsconfig/node14": { "version": "1.0.3", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", "dev": true }, "@tsconfig/node16": { "version": "1.0.3", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", "dev": true }, "@types/debug": { "version": "4.1.7", - "integrity": "sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==", "dev": true, "requires": { "@types/ms": "*" @@ -6808,7 +6561,6 @@ }, "@types/eslint": { "version": "8.4.5", - "integrity": "sha512-dhsC09y1gpJWnK+Ff4SGvCuSnk9DaU0BJZSzOwa6GVSg65XtTugLBITDAAzRU5duGBoXBHpdR/9jHGxJjNflJQ==", "dev": true, "requires": { "@types/estree": "*", @@ -6817,7 +6569,6 @@ }, "@types/eslint-scope": { "version": "3.7.4", - "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", "dev": true, "requires": { "@types/eslint": "*", @@ -6826,29 +6577,23 @@ }, "@types/estree": { "version": "0.0.51", - "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", "dev": true }, "@types/json-schema": { "version": "7.0.11", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", "dev": true }, "@types/json5": { "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true, "optional": true }, "@types/linkify-it": { "version": "3.0.2", - "integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==", "dev": true }, "@types/markdown-it": { "version": "12.2.3", - "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", "dev": true, "requires": { "@types/linkify-it": "*", @@ -6857,7 +6602,6 @@ }, "@types/mdast": { "version": "3.0.10", - "integrity": "sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==", "dev": true, "requires": { "@types/unist": "*" @@ -6865,26 +6609,21 @@ }, "@types/mdurl": { "version": "1.0.2", - "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", "dev": true }, "@types/mocha": { "version": "9.1.1", - "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", "dev": true }, "@types/ms": { "version": "0.7.31", - "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==", "dev": true }, "@types/node": { - "version": "18.6.5", - "integrity": "sha512-Xjt5ZGUa5WusGZJ4WJPbOT8QOqp6nDynVFRKcUt32bOgvXEoc6o085WNkYTMO7ifAj2isEfQQ2cseE+wT6jsRw==" + "version": "18.6.5" }, "@types/node-fetch": { "version": "2.6.2", - "integrity": "sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==", "requires": { "@types/node": "*", "form-data": "^3.0.0" @@ -6892,25 +6631,18 @@ }, "@types/sinonjs__fake-timers": { "version": "8.1.1", - "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz", - "integrity": "sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==", "dev": true }, "@types/sizzle": { "version": "2.3.3", - "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz", - "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==", "dev": true }, "@types/unist": { "version": "2.0.6", - "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==", "dev": true }, "@types/yauzl": { "version": "2.10.0", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==", "dev": true, "optional": true, "requires": { @@ -6919,12 +6651,10 @@ }, "@ungap/promise-all-settled": { "version": "1.1.2", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", "dev": true }, "@webassemblyjs/ast": { "version": "1.11.1", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", "dev": true, "requires": { "@webassemblyjs/helper-numbers": "1.11.1", @@ -6933,22 +6663,18 @@ }, "@webassemblyjs/floating-point-hex-parser": { "version": "1.11.1", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", "dev": true }, "@webassemblyjs/helper-api-error": { "version": "1.11.1", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", "dev": true }, "@webassemblyjs/helper-buffer": { "version": "1.11.1", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", "dev": true }, "@webassemblyjs/helper-numbers": { "version": "1.11.1", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", "dev": true, "requires": { "@webassemblyjs/floating-point-hex-parser": "1.11.1", @@ -6958,12 +6684,10 @@ }, "@webassemblyjs/helper-wasm-bytecode": { "version": "1.11.1", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", "dev": true }, "@webassemblyjs/helper-wasm-section": { "version": "1.11.1", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", "dev": true, "requires": { "@webassemblyjs/ast": "1.11.1", @@ -6974,7 +6698,6 @@ }, "@webassemblyjs/ieee754": { "version": "1.11.1", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", "dev": true, "requires": { "@xtuc/ieee754": "^1.2.0" @@ -6982,7 +6705,6 @@ }, "@webassemblyjs/leb128": { "version": "1.11.1", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", "dev": true, "requires": { "@xtuc/long": "4.2.2" @@ -6990,12 +6712,10 @@ }, "@webassemblyjs/utf8": { "version": "1.11.1", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", "dev": true }, "@webassemblyjs/wasm-edit": { "version": "1.11.1", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", "dev": true, "requires": { "@webassemblyjs/ast": "1.11.1", @@ -7010,7 +6730,6 @@ }, "@webassemblyjs/wasm-gen": { "version": "1.11.1", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", "dev": true, "requires": { "@webassemblyjs/ast": "1.11.1", @@ -7022,7 +6741,6 @@ }, "@webassemblyjs/wasm-opt": { "version": "1.11.1", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", "dev": true, "requires": { "@webassemblyjs/ast": "1.11.1", @@ -7033,7 +6751,6 @@ }, "@webassemblyjs/wasm-parser": { "version": "1.11.1", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", "dev": true, "requires": { "@webassemblyjs/ast": "1.11.1", @@ -7046,7 +6763,6 @@ }, "@webassemblyjs/wast-printer": { "version": "1.11.1", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", "dev": true, "requires": { "@webassemblyjs/ast": "1.11.1", @@ -7055,13 +6771,11 @@ }, "@webpack-cli/configtest": { "version": "1.2.0", - "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", "dev": true, "requires": {} }, "@webpack-cli/info": { "version": "1.5.0", - "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", "dev": true, "requires": { "envinfo": "^7.7.3" @@ -7069,40 +6783,32 @@ }, "@webpack-cli/serve": { "version": "1.7.0", - "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", "dev": true, "requires": {} }, "@xtuc/ieee754": { "version": "1.2.0", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", "dev": true }, "@xtuc/long": { "version": "4.2.2", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "dev": true }, "acorn": { "version": "8.8.0", - "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", "dev": true }, "acorn-import-assertions": { "version": "1.8.0", - "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", "dev": true, "requires": {} }, "acorn-walk": { "version": "8.2.0", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", "dev": true }, "aggregate-error": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "dev": true, "requires": { "clean-stack": "^2.0.0", @@ -7111,7 +6817,6 @@ }, "ajv": { "version": "6.12.6", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -7122,19 +6827,15 @@ }, "ajv-keywords": { "version": "3.5.2", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "dev": true, "requires": {} }, "ansi-colors": { "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true }, "ansi-escape-sequences": { "version": "4.1.0", - "integrity": "sha512-dzW9kHxH011uBsidTXd14JXgzye/YLb2LzeKZ4bsgl/Knwx8AtbSFkkGxagdNOoh0DlqHCmfiEjWKBaqjOanVw==", "dev": true, "requires": { "array-back": "^3.0.1" @@ -7142,15 +6843,12 @@ "dependencies": { "array-back": { "version": "3.1.0", - "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", "dev": true } } }, "ansi-escapes": { "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, "requires": { "type-fest": "^0.21.3" @@ -7158,13 +6856,10 @@ }, "ansi-regex": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", "dev": true }, "ansi-styles": { "version": "4.3.0", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { "color-convert": "^2.0.1" @@ -7172,7 +6867,6 @@ }, "anymatch": { "version": "3.1.2", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, "requires": { "normalize-path": "^3.0.0", @@ -7181,39 +6875,30 @@ }, "arch": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", - "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", "dev": true }, "arg": { "version": "4.1.3", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", "dev": true }, "argparse": { "version": "2.0.1", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, "array-back": { "version": "6.2.2", - "integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==", "dev": true }, "array-union": { "version": "2.1.0", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, "arrify": { "version": "1.0.1", - "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", "dev": true }, "asn1": { "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", "dev": true, "requires": { "safer-buffer": "~2.1.0" @@ -7221,46 +6906,33 @@ }, "assert-plus": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", "dev": true }, "astral-regex": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true }, "async": { "version": "3.2.4", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", "dev": true }, "asynckit": { - "version": "0.4.0", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + "version": "0.4.0" }, "at-least-node": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", "dev": true }, "aws-sign2": { "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", "dev": true }, "aws4": { "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", "dev": true }, "axios": { "version": "0.21.4", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", "dev": true, "requires": { "follow-redirects": "^1.14.0" @@ -7268,23 +6940,18 @@ }, "bail": { "version": "2.0.2", - "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", "dev": true }, "balanced-match": { "version": "1.0.2", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, "base64-js": { "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "dev": true }, "basic-auth": { "version": "2.0.1", - "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", "dev": true, "requires": { "safe-buffer": "5.1.2" @@ -7292,38 +6959,30 @@ "dependencies": { "safe-buffer": { "version": "5.1.2", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true } } }, "bcrypt-pbkdf": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", "dev": true, "requires": { "tweetnacl": "^0.14.3" } }, "big-integer": { - "version": "1.6.51", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", - "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==" + "version": "1.6.51" }, "big.js": { "version": "5.2.2", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", "dev": true }, "binary-extensions": { "version": "2.2.0", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, "binary-install": { "version": "0.1.1", - "integrity": "sha512-DqED0D/6LrS+BHDkKn34vhRqOGjy5gTMgvYZsGK2TpNbdPuz4h+MRlNgGv5QBRd7pWq/jylM4eKNCizgAq3kNQ==", "dev": true, "requires": { "axios": "^0.21.1", @@ -7333,18 +6992,14 @@ }, "blob-util": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz", - "integrity": "sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==", "dev": true }, "bluebird": { "version": "3.7.2", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", "dev": true }, "brace-expansion": { "version": "1.1.11", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { "balanced-match": "^1.0.0", @@ -7353,7 +7008,6 @@ }, "braces": { "version": "3.0.2", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, "requires": { "fill-range": "^7.0.1" @@ -7361,12 +7015,10 @@ }, "browser-stdout": { "version": "1.3.1", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, "browserslist": { "version": "4.21.3", - "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", "dev": true, "requires": { "caniuse-lite": "^1.0.30001370", @@ -7377,8 +7029,6 @@ }, "buffer": { "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "dev": true, "requires": { "base64-js": "^1.3.1", @@ -7387,18 +7037,14 @@ }, "buffer-crc32": { "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "dev": true }, "buffer-from": { "version": "1.1.2", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, "cache-point": { "version": "2.0.0", - "integrity": "sha512-4gkeHlFpSKgm3vm2gJN5sPqfmijYRFYCQ6tv5cLw0xVmT6r1z1vd4FNnpuOREco3cBs1G709sZ72LdgddKvL5w==", "dev": true, "requires": { "array-back": "^4.0.1", @@ -7408,36 +7054,28 @@ "dependencies": { "array-back": { "version": "4.0.2", - "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==", "dev": true } } }, "cachedir": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz", - "integrity": "sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==", "dev": true }, "camelcase": { "version": "6.3.0", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true }, "caniuse-lite": { "version": "1.0.30001375", - "integrity": "sha512-kWIMkNzLYxSvnjy0hL8w1NOaWNr2rn39RTAVyIwcw8juu60bZDWiF1/loOYANzjtJmy6qPgNmn38ro5Pygagdw==", "dev": true }, "caseless": { "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", "dev": true }, "catharsis": { "version": "0.9.0", - "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", "dev": true, "requires": { "lodash": "^4.17.15" @@ -7445,7 +7083,6 @@ }, "chalk": { "version": "4.1.2", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -7454,7 +7091,6 @@ "dependencies": { "supports-color": { "version": "7.2.0", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { "has-flag": "^4.0.0" @@ -7464,18 +7100,14 @@ }, "character-entities": { "version": "2.0.2", - "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", "dev": true }, "check-more-types": { "version": "2.24.0", - "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", - "integrity": "sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==", "dev": true }, "chokidar": { "version": "3.5.3", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, "requires": { "anymatch": "~3.1.2", @@ -7490,25 +7122,18 @@ }, "chrome-trace-event": { "version": "1.0.3", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", "dev": true }, "ci-info": { "version": "3.3.2", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.2.tgz", - "integrity": "sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg==", "dev": true }, "clean-stack": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true }, "cli-cursor": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "dev": true, "requires": { "restore-cursor": "^3.1.0" @@ -7516,7 +7141,6 @@ }, "cli-table3": { "version": "0.6.2", - "integrity": "sha512-QyavHCaIC80cMivimWu4aWHilIpiDpfm3hGmqAmXVL1UsnbLuBSMd21hTX6VY4ZSDSM73ESLeF8TOYId3rBTbw==", "dev": true, "requires": { "@colors/colors": "1.5.0", @@ -7525,8 +7149,6 @@ }, "cli-truncate": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", - "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", "dev": true, "requires": { "slice-ansi": "^3.0.0", @@ -7535,7 +7157,6 @@ }, "cliui": { "version": "7.0.4", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, "requires": { "string-width": "^4.2.0", @@ -7545,12 +7166,10 @@ "dependencies": { "ansi-regex": { "version": "5.0.1", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "strip-ansi": { "version": "6.0.1", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { "ansi-regex": "^5.0.1" @@ -7560,7 +7179,6 @@ }, "clone-deep": { "version": "4.0.1", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", "dev": true, "requires": { "is-plain-object": "^2.0.4", @@ -7570,7 +7188,6 @@ }, "collect-all": { "version": "1.0.4", - "integrity": "sha512-RKZhRwJtJEP5FWul+gkSMEnaK6H3AGPTTWOiRimCcs+rc/OmQE3Yhy1Q7A7KsdkG3ZXVdZq68Y6ONSdvkeEcKA==", "dev": true, "requires": { "stream-connect": "^1.0.2", @@ -7579,7 +7196,6 @@ }, "color-convert": { "version": "2.0.1", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { "color-name": "~1.1.4" @@ -7587,30 +7203,24 @@ }, "color-name": { "version": "1.1.4", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, "colorette": { "version": "2.0.19", - "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", "dev": true }, "colors": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", "dev": true }, "combined-stream": { "version": "1.0.8", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "requires": { "delayed-stream": "~1.0.0" } }, "command-line-args": { "version": "5.2.1", - "integrity": "sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==", "dev": true, "requires": { "array-back": "^3.1.0", @@ -7621,19 +7231,16 @@ "dependencies": { "array-back": { "version": "3.1.0", - "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", "dev": true }, "typical": { "version": "4.0.0", - "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==", "dev": true } } }, "command-line-tool": { "version": "0.8.0", - "integrity": "sha512-Xw18HVx/QzQV3Sc5k1vy3kgtOeGmsKIqwtFFoyjI4bbcpSgnw2CWVULvtakyw4s6fhyAdI6soQQhXc2OzJy62g==", "dev": true, "requires": { "ansi-escape-sequences": "^4.0.0", @@ -7645,7 +7252,6 @@ "dependencies": { "array-back": { "version": "2.0.0", - "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", "dev": true, "requires": { "typical": "^2.6.1" @@ -7655,7 +7261,6 @@ }, "command-line-usage": { "version": "4.1.0", - "integrity": "sha512-MxS8Ad995KpdAC0Jopo/ovGIroV/m0KHwzKfXxKag6FHOkGsH8/lv5yjgablcRxCJJC0oJeUMuO/gmaq+Wq46g==", "dev": true, "requires": { "ansi-escape-sequences": "^4.0.0", @@ -7666,7 +7271,6 @@ "dependencies": { "array-back": { "version": "2.0.0", - "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", "dev": true, "requires": { "typical": "^2.6.1" @@ -7676,29 +7280,22 @@ }, "commander": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", "dev": true }, "common-sequence": { "version": "2.0.2", - "integrity": "sha512-jAg09gkdkrDO9EWTdXfv80WWH3yeZl5oT69fGfedBNS9pXUKYInVJ1bJ+/ht2+Moeei48TmSbQDYMc8EOx9G0g==", "dev": true }, "common-tags": { "version": "1.8.2", - "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", - "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", "dev": true }, "concat-map": { "version": "0.0.1", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, "concurrently": { "version": "7.3.0", - "integrity": "sha512-IiDwm+8DOcFEInca494A8V402tNTQlJaYq78RF2rijOrKEk/AOHTxhN4U1cp7GYKYX5Q6Ymh1dLTBlzIMN0ikA==", "dev": true, "requires": { "chalk": "^4.1.0", @@ -7714,7 +7311,6 @@ }, "config-master": { "version": "3.1.0", - "integrity": "sha512-n7LBL1zBzYdTpF1mx5DNcZnZn05CWIdsdvtPL4MosvqbBUK3Rq6VWEtGUuF3Y0s9/CIhMejezqlSkP6TnCJ/9g==", "dev": true, "requires": { "walk-back": "^2.0.1" @@ -7722,14 +7318,12 @@ "dependencies": { "walk-back": { "version": "2.0.1", - "integrity": "sha512-Nb6GvBR8UWX1D+Le+xUq0+Q1kFmRBIWVrfLnQAOmcpEzA9oAxwJ9gIr36t9TWYfzvWRvuMtjHiVsJYEkXWaTAQ==", "dev": true } } }, "copy-webpack-plugin": { "version": "7.0.0", - "integrity": "sha512-SLjQNa5iE3BoCP76ESU9qYo9ZkEWtXoZxDurHoqPchAFRblJ9g96xTeC560UXBMre1Nx6ixIIUfiY3VcjpJw3g==", "dev": true, "requires": { "fast-glob": "^3.2.4", @@ -7744,23 +7338,18 @@ }, "core-util-is": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", "dev": true }, "corser": { "version": "2.0.1", - "integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==", "dev": true }, "create-require": { "version": "1.1.1", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, "cross-spawn": { "version": "7.0.3", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "requires": { "path-key": "^3.1.0", @@ -7770,8 +7359,6 @@ }, "cypress": { "version": "10.6.0", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-10.6.0.tgz", - "integrity": "sha512-6sOpHjostp8gcLO34p6r/Ci342lBs8S5z9/eb3ZCQ22w2cIhMWGUoGKkosabPBfKcvRS9BE4UxybBtlIs8gTQA==", "dev": true, "requires": { "@cypress/request": "^2.88.10", @@ -7820,14 +7407,10 @@ "dependencies": { "@types/node": { "version": "14.18.25", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.25.tgz", - "integrity": "sha512-9pLfceRSrKIsv/MISN6RoFWTIzka36Uk2Uuf5a8cHyDYhEgl5Hm5dXoe621KULeBjt+cFsY18mILsWWtJeG80w==", "dev": true }, "fs-extra": { "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, "requires": { "at-least-node": "^1.0.0", @@ -7840,8 +7423,6 @@ }, "cypress-multi-reporters": { "version": "1.6.1", - "resolved": "https://registry.npmjs.org/cypress-multi-reporters/-/cypress-multi-reporters-1.6.1.tgz", - "integrity": "sha512-FPeC0xWF1N6Myrwc2m7KC0xxlrtG8+x4hlsPFBDRWP8u/veR2x90pGaH3BuJfweV7xoQ4Zo85Qjhu3fgZGrBQQ==", "dev": true, "peer": true, "requires": { @@ -7851,8 +7432,6 @@ }, "cypress-parallel": { "version": "0.9.1", - "resolved": "https://registry.npmjs.org/cypress-parallel/-/cypress-parallel-0.9.1.tgz", - "integrity": "sha512-7VSfFr8HEEN6zkgo6SkG7pPoHK7VakFhEH1jbM4+Ire/I+O2jNzyd1bRUA+O3V2DIMow64ECDJKf13YHBon+BQ==", "dev": true, "requires": { "cli-table3": "^0.6.0", @@ -7868,26 +7447,18 @@ "dependencies": { "ansi-colors": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true }, "ansi-regex": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "camelcase": { "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, "chokidar": { "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", "dev": true, "requires": { "anymatch": "~3.1.1", @@ -7902,8 +7473,6 @@ }, "debug": { "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, "requires": { "ms": "2.1.2" @@ -7911,20 +7480,14 @@ }, "decamelize": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", "dev": true }, "escape-string-regexp": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, "glob": { "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -7937,8 +7500,6 @@ }, "js-yaml": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", - "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", "dev": true, "requires": { "argparse": "^2.0.1" @@ -7946,8 +7507,6 @@ }, "locate-path": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "requires": { "p-locate": "^4.1.0" @@ -7955,8 +7514,6 @@ }, "log-symbols": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", - "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", "dev": true, "requires": { "chalk": "^4.0.0" @@ -7964,8 +7521,6 @@ }, "minimatch": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -7973,8 +7528,6 @@ }, "mocha": { "version": "8.4.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz", - "integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==", "dev": true, "requires": { "@ungap/promise-all-settled": "1.1.2", @@ -8006,14 +7559,10 @@ "dependencies": { "ms": { "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "yargs": { "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "requires": { "cliui": "^7.0.2", @@ -8029,14 +7578,10 @@ }, "nanoid": { "version": "3.1.20", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", - "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", "dev": true }, "p-limit": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -8044,8 +7589,6 @@ }, "p-locate": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "requires": { "p-limit": "^2.2.0" @@ -8053,8 +7596,6 @@ }, "readdirp": { "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", "dev": true, "requires": { "picomatch": "^2.2.1" @@ -8062,8 +7603,6 @@ }, "strip-ansi": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { "ansi-regex": "^5.0.1" @@ -8071,14 +7610,10 @@ }, "workerpool": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz", - "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==", "dev": true }, "wrap-ansi": { "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, "requires": { "ansi-styles": "^4.0.0", @@ -8088,8 +7623,6 @@ }, "yargs": { "version": "15.3.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz", - "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==", "dev": true, "requires": { "cliui": "^6.0.0", @@ -8107,8 +7640,6 @@ "dependencies": { "cliui": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", "dev": true, "requires": { "string-width": "^4.2.0", @@ -8118,8 +7649,6 @@ }, "find-up": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "requires": { "locate-path": "^5.0.0", @@ -8128,14 +7657,10 @@ }, "y18n": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", "dev": true }, "yargs-parser": { "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "dev": true, "requires": { "camelcase": "^5.0.0", @@ -8148,8 +7673,6 @@ }, "dashdash": { "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", "dev": true, "requires": { "assert-plus": "^1.0.0" @@ -8157,18 +7680,14 @@ }, "date-fns": { "version": "2.29.1", - "integrity": "sha512-dlLD5rKaKxpFdnjrs+5azHDFOPEu4ANy/LTh04A1DTzMM7qoajmKCBc8pkKRFT41CNzw+4gQh79X5C+Jq27HAw==", "dev": true }, "dayjs": { "version": "1.11.5", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.5.tgz", - "integrity": "sha512-CAdX5Q3YW3Gclyo5Vpqkgpj8fSdLQcRuzfX6mC6Phy0nfJ0eGYOeS7m4mt2plDWLAtA4TqTakvbboHvUxfe4iA==", "dev": true }, "debug": { "version": "4.3.4", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "requires": { "ms": "2.1.2" @@ -8176,12 +7695,10 @@ }, "decamelize": { "version": "4.0.0", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true }, "decode-named-character-reference": { "version": "1.0.2", - "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", "dev": true, "requires": { "character-entities": "^2.0.0" @@ -8189,31 +7706,25 @@ }, "deep-extend": { "version": "0.6.0", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true }, "delayed-stream": { - "version": "1.0.0", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + "version": "1.0.0" }, "dequal": { "version": "2.0.3", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", "dev": true }, "diff": { "version": "5.0.0", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", "dev": true }, "diff-match-patch": { "version": "1.0.5", - "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==", "dev": true }, "dir-glob": { "version": "3.0.1", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, "requires": { "path-type": "^4.0.0" @@ -8221,7 +7732,6 @@ }, "dmd": { "version": "6.1.0", - "integrity": "sha512-0zQIJ873gay1scCTFZvHPWM9mVJBnaylB2NQDI8O9u8O32m00Jb6uxDKexZm8hjTRM7RiWe0FJ32pExHoXdwoQ==", "dev": true, "requires": { "array-back": "^6.2.2", @@ -8240,8 +7750,6 @@ }, "ecc-jsbn": { "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", "dev": true, "requires": { "jsbn": "~0.1.0", @@ -8250,23 +7758,18 @@ }, "electron-to-chromium": { "version": "1.4.213", - "integrity": "sha512-+3DbGHGOCHTVB/Ms63bGqbyC1b8y7Fk86+7ltssB8NQrZtSCvZG6eooSl9U2Q0yw++fL2DpHKOdTU0NVEkFObg==", "dev": true }, "emoji-regex": { "version": "8.0.0", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, "emojis-list": { "version": "3.0.0", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", "dev": true }, "end-of-stream": { "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "dev": true, "requires": { "once": "^1.4.0" @@ -8274,7 +7777,6 @@ }, "enhanced-resolve": { "version": "5.10.0", - "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", "dev": true, "requires": { "graceful-fs": "^4.2.4", @@ -8283,8 +7785,6 @@ }, "enquirer": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", "dev": true, "requires": { "ansi-colors": "^4.1.1" @@ -8292,33 +7792,26 @@ }, "entities": { "version": "2.1.0", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", "dev": true }, "envinfo": { "version": "7.8.1", - "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", "dev": true }, "es-module-lexer": { "version": "0.9.3", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", "dev": true }, "escalade": { "version": "3.1.1", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true }, "escape-string-regexp": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true }, "eslint-scope": { "version": "5.1.1", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, "requires": { "esrecurse": "^4.3.0", @@ -8327,7 +7820,6 @@ }, "esrecurse": { "version": "4.3.0", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "requires": { "estraverse": "^5.2.0" @@ -8335,36 +7827,28 @@ "dependencies": { "estraverse": { "version": "5.3.0", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true } } }, "estraverse": { "version": "4.3.0", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true }, "eventemitter2": { "version": "6.4.7", - "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.7.tgz", - "integrity": "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==", "dev": true }, "eventemitter3": { "version": "4.0.7", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", "dev": true }, "events": { "version": "3.3.0", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true }, "execa": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", - "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", "dev": true, "requires": { "cross-spawn": "^7.0.0", @@ -8380,8 +7864,6 @@ }, "executable": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", - "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", "dev": true, "requires": { "pify": "^2.2.0" @@ -8389,13 +7871,10 @@ }, "extend": { "version": "3.0.2", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "dev": true }, "extract-zip": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", - "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", "dev": true, "requires": { "@types/yauzl": "^2.9.1", @@ -8406,18 +7885,14 @@ }, "extsprintf": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", "dev": true }, "fast-deep-equal": { "version": "3.1.3", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, "fast-glob": { "version": "3.2.11", - "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", @@ -8429,17 +7904,14 @@ }, "fast-json-stable-stringify": { "version": "2.1.0", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, "fastest-levenshtein": { "version": "1.0.16", - "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", "dev": true }, "fastq": { "version": "1.13.0", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", "dev": true, "requires": { "reusify": "^1.0.4" @@ -8447,8 +7919,6 @@ }, "fd-slicer": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", "dev": true, "requires": { "pend": "~1.2.0" @@ -8456,8 +7926,6 @@ }, "figures": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", "dev": true, "requires": { "escape-string-regexp": "^1.0.5" @@ -8465,7 +7933,6 @@ }, "file-set": { "version": "4.0.2", - "integrity": "sha512-fuxEgzk4L8waGXaAkd8cMr73Pm0FxOVkn8hztzUW7BAHhOGH90viQNXbiOsnecCWmfInqU6YmAMwxRMdKETceQ==", "dev": true, "requires": { "array-back": "^5.0.0", @@ -8474,14 +7941,12 @@ "dependencies": { "array-back": { "version": "5.0.0", - "integrity": "sha512-kgVWwJReZWmVuWOQKEOohXKJX+nD02JAZ54D1RRWlv8L0NebauKAaFxACKzB74RTclt1+WNz5KHaLRDAPZbDEw==", "dev": true } } }, "fill-range": { "version": "7.0.1", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, "requires": { "to-regex-range": "^5.0.1" @@ -8489,7 +7954,6 @@ }, "find-replace": { "version": "3.0.0", - "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", "dev": true, "requires": { "array-back": "^3.0.1" @@ -8497,14 +7961,12 @@ "dependencies": { "array-back": { "version": "3.1.0", - "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", "dev": true } } }, "find-up": { "version": "5.0.0", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "requires": { "locate-path": "^6.0.0", @@ -8513,23 +7975,18 @@ }, "flat": { "version": "5.0.2", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "dev": true }, "follow-redirects": { "version": "1.15.1", - "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", "dev": true }, "forever-agent": { "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", "dev": true }, "form-data": { "version": "3.0.1", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -8538,8 +7995,6 @@ }, "fs-extra": { "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, "requires": { "graceful-fs": "^4.2.0", @@ -8549,7 +8004,6 @@ }, "fs-minipass": { "version": "2.1.0", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", "dev": true, "requires": { "minipass": "^3.0.0" @@ -8557,35 +8011,22 @@ }, "fs-then-native": { "version": "2.0.0", - "integrity": "sha512-X712jAOaWXkemQCAmWeg5rOT2i+KOpWz1Z/txk/cW0qlOu2oQ9H61vc5w3X/iyuUEfq/OyaFJ78/cZAQD1/bgA==", "dev": true }, "fs.realpath": { "version": "1.0.0", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, "function-bind": { "version": "1.1.1", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, "get-caller-file": { "version": "2.0.5", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, "get-stream": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, "requires": { "pump": "^3.0.0" @@ -8593,8 +8034,6 @@ }, "getos": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz", - "integrity": "sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==", "dev": true, "requires": { "async": "^3.2.0" @@ -8602,8 +8041,6 @@ }, "getpass": { "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", "dev": true, "requires": { "assert-plus": "^1.0.0" @@ -8611,7 +8048,6 @@ }, "glob": { "version": "7.2.3", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -8624,13 +8060,10 @@ }, "glob-escape": { "version": "0.0.2", - "resolved": "https://registry.npmjs.org/glob-escape/-/glob-escape-0.0.2.tgz", - "integrity": "sha512-L/cXYz8x7qer1HAyUQ+mbjcUsJVdpRxpAf7CwqHoNBs9vTpABlGfNN4tzkDxt+u3Z7ZncVyKlCNPtzb0R/7WbA==", "dev": true }, "glob-parent": { "version": "5.1.2", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "requires": { "is-glob": "^4.0.1" @@ -8638,13 +8071,10 @@ }, "glob-to-regexp": { "version": "0.4.1", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", "dev": true }, "global-dirs": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz", - "integrity": "sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==", "dev": true, "requires": { "ini": "2.0.0" @@ -8652,7 +8082,6 @@ }, "globby": { "version": "11.1.0", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "requires": { "array-union": "^2.1.0", @@ -8665,17 +8094,14 @@ }, "graceful-fs": { "version": "4.2.10", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, "growl": { "version": "1.10.5", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", "dev": true }, "handlebars": { "version": "4.7.7", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", "dev": true, "requires": { "minimist": "^1.2.5", @@ -8687,7 +8113,6 @@ }, "has": { "version": "1.0.3", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, "requires": { "function-bind": "^1.1.1" @@ -8695,17 +8120,14 @@ }, "has-flag": { "version": "4.0.0", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, "he": { "version": "1.2.0", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, "html-encoding-sniffer": { "version": "3.0.0", - "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", "dev": true, "requires": { "whatwg-encoding": "^2.0.0" @@ -8713,7 +8135,6 @@ }, "http-proxy": { "version": "1.18.1", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", "dev": true, "requires": { "eventemitter3": "^4.0.0", @@ -8723,7 +8144,6 @@ }, "http-server": { "version": "14.1.1", - "integrity": "sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==", "dev": true, "requires": { "basic-auth": "^2.0.1", @@ -8743,8 +8163,6 @@ }, "http-signature": { "version": "1.3.6", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz", - "integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==", "dev": true, "requires": { "assert-plus": "^1.0.0", @@ -8754,13 +8172,10 @@ }, "human-signals": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", "dev": true }, "iconv-lite": { "version": "0.6.3", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, "requires": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -8768,18 +8183,14 @@ }, "ieee754": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "dev": true }, "ignore": { "version": "5.2.0", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "dev": true }, "import-local": { "version": "3.1.0", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", "dev": true, "requires": { "pkg-dir": "^4.2.0", @@ -8788,13 +8199,10 @@ }, "indent-string": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true }, "inflight": { "version": "1.0.6", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, "requires": { "once": "^1.3.0", @@ -8803,23 +8211,18 @@ }, "inherits": { "version": "2.0.4", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, "ini": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", - "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", "dev": true }, "interpret": { "version": "2.2.0", - "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", "dev": true }, "is-binary-path": { "version": "2.1.0", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, "requires": { "binary-extensions": "^2.0.0" @@ -8827,13 +8230,10 @@ }, "is-buffer": { "version": "2.0.5", - "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", "dev": true }, "is-ci": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", - "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", "dev": true, "requires": { "ci-info": "^3.2.0" @@ -8841,7 +8241,6 @@ }, "is-core-module": { "version": "2.10.0", - "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", "dev": true, "requires": { "has": "^1.0.3" @@ -8849,17 +8248,14 @@ }, "is-extglob": { "version": "2.1.1", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true }, "is-fullwidth-code-point": { "version": "3.0.0", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true }, "is-glob": { "version": "4.0.3", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "requires": { "is-extglob": "^2.1.1" @@ -8867,8 +8263,6 @@ }, "is-installed-globally": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", - "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", "dev": true, "requires": { "global-dirs": "^3.0.0", @@ -8877,29 +8271,22 @@ }, "is-npm": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz", - "integrity": "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==", "dev": true }, "is-number": { "version": "7.0.0", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, "is-path-inside": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true }, "is-plain-obj": { "version": "4.1.0", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", "dev": true }, "is-plain-object": { "version": "2.0.4", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, "requires": { "isobject": "^3.0.1" @@ -8907,40 +8294,30 @@ }, "is-stream": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true }, "is-typedarray": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", "dev": true }, "is-unicode-supported": { "version": "0.1.0", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true }, "isexe": { "version": "2.0.0", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "isobject": { "version": "3.0.1", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", "dev": true }, "isstream": { "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", "dev": true }, "jest-worker": { "version": "27.5.1", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "dev": true, "requires": { "@types/node": "*", @@ -8950,7 +8327,6 @@ }, "js-yaml": { "version": "4.1.0", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "requires": { "argparse": "^2.0.1" @@ -8958,7 +8334,6 @@ }, "js2xmlparser": { "version": "4.0.2", - "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", "dev": true, "requires": { "xmlcreate": "^2.0.4" @@ -8966,13 +8341,10 @@ }, "jsbn": { "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", "dev": true }, "jsdoc": { "version": "3.6.11", - "integrity": "sha512-8UCU0TYeIYD9KeLzEcAu2q8N/mx9O3phAGl32nmHlE0LpaJL71mMkP4d+QE5zWfNt50qheHtOZ0qoxVrsX5TUg==", "dev": true, "requires": { "@babel/parser": "^7.9.4", @@ -8994,14 +8366,12 @@ "dependencies": { "escape-string-regexp": { "version": "2.0.0", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", "dev": true } } }, "jsdoc-api": { "version": "7.1.1", - "integrity": "sha512-0pkuPCzVXiqsDAsVrNFXCkHzlyNepBIDVtwwehry4RJAnZmXtlAz7rh8F9FRz53u3NeynGbex+bpYWwi8lE66A==", "dev": true, "requires": { "array-back": "^6.2.2", @@ -9017,7 +8387,6 @@ }, "jsdoc-parse": { "version": "6.1.0", - "integrity": "sha512-n/hDGQJa69IBun1yZAjqzV4gVR41+flZ3bIlm9fKvNe2Xjsd1/+zCo2+R9ls8LxtePgIWbpA1jU7xkB2lRdLLg==", "dev": true, "requires": { "array-back": "^6.2.2", @@ -9030,7 +8399,6 @@ }, "jsdoc-to-markdown": { "version": "7.1.1", - "integrity": "sha512-CI86d63xAVNO+ENumWwmJ034lYe5iGU5GwjtTA11EuphP9tpnoi4hrKgR/J8uME0D+o4KUpVfwX1fjZhc8dEtg==", "dev": true, "requires": { "array-back": "^6.2.2", @@ -9044,34 +8412,26 @@ }, "json-parse-even-better-errors": { "version": "2.3.1", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, "json-schema": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", "dev": true }, "json-schema-traverse": { "version": "0.4.1", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, "json-stringify-safe": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", "dev": true }, "json5": { "version": "2.2.1", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", "dev": true }, "jsonfile": { "version": "6.1.0", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, "requires": { "graceful-fs": "^4.1.6", @@ -9080,8 +8440,6 @@ }, "jsprim": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", - "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==", "dev": true, "requires": { "assert-plus": "1.0.0", @@ -9092,12 +8450,10 @@ }, "kind-of": { "version": "6.0.3", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true }, "klaw": { "version": "3.0.0", - "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", "dev": true, "requires": { "graceful-fs": "^4.1.9" @@ -9105,18 +8461,14 @@ }, "kleur": { "version": "4.1.5", - "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", "dev": true }, "lazy-ass": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", - "integrity": "sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==", "dev": true }, "linkify-it": { "version": "3.0.3", - "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", "dev": true, "requires": { "uc.micro": "^1.0.1" @@ -9124,8 +8476,6 @@ }, "listr2": { "version": "3.14.0", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.14.0.tgz", - "integrity": "sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==", "dev": true, "requires": { "cli-truncate": "^2.1.0", @@ -9140,12 +8490,10 @@ }, "loader-runner": { "version": "4.3.0", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", "dev": true }, "loader-utils": { "version": "2.0.2", - "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", "dev": true, "requires": { "big.js": "^5.2.2", @@ -9155,7 +8503,6 @@ }, "locate-path": { "version": "6.0.0", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "requires": { "p-locate": "^5.0.0" @@ -9163,38 +8510,30 @@ }, "lodash": { "version": "4.17.21", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, "lodash.camelcase": { "version": "4.3.0", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", "dev": true }, "lodash.omit": { "version": "4.5.0", - "integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==", "dev": true }, "lodash.once": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", "dev": true }, "lodash.padend": { "version": "4.6.1", - "integrity": "sha512-sOQs2aqGpbl27tmCS1QNZA09Uqp01ZzWfDUoD+xzTii0E7dSQfRKcRetFwa+uXaxaqL+TKm7CgD2JdKP7aZBSw==", "dev": true }, "lodash.pick": { "version": "4.4.0", - "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==", "dev": true }, "log-symbols": { "version": "4.1.0", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "requires": { "chalk": "^4.1.0", @@ -9203,8 +8542,6 @@ }, "log-update": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", - "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", "dev": true, "requires": { "ansi-escapes": "^4.3.0", @@ -9215,14 +8552,10 @@ "dependencies": { "ansi-regex": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "slice-ansi": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", "dev": true, "requires": { "ansi-styles": "^4.0.0", @@ -9232,8 +8565,6 @@ }, "strip-ansi": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { "ansi-regex": "^5.0.1" @@ -9241,8 +8572,6 @@ }, "wrap-ansi": { "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, "requires": { "ansi-styles": "^4.0.0", @@ -9254,8 +8583,6 @@ }, "lru-cache": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "requires": { "yallist": "^4.0.0" @@ -9263,12 +8590,10 @@ }, "make-error": { "version": "1.3.6", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, "markdown-it": { "version": "12.3.2", - "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", "dev": true, "requires": { "argparse": "^2.0.1", @@ -9280,18 +8605,15 @@ }, "markdown-it-anchor": { "version": "8.6.4", - "integrity": "sha512-Ul4YVYZNxMJYALpKtu+ZRdrryYt/GlQ5CK+4l1bp/gWXOG2QWElt6AqF3Mih/wfUKdZbNAZVXGR73/n6U/8img==", "dev": true, "requires": {} }, "marked": { "version": "4.0.18", - "integrity": "sha512-wbLDJ7Zh0sqA0Vdg6aqlbT+yPxqLblpAZh1mK2+AO2twQkPywvvqQNfEPVwSSRjZ7dZcdeVBIAgiO7MMp3Dszw==", "dev": true }, "mdast-util-from-markdown": { "version": "1.2.0", - "integrity": "sha512-iZJyyvKD1+K7QX1b5jXdE7Sc5dtoTry1vzV28UZZe8Z1xVnB/czKntJ7ZAkG0tANqRnBF6p3p7GpU1y19DTf2Q==", "dev": true, "requires": { "@types/mdast": "^3.0.0", @@ -9310,27 +8632,22 @@ }, "mdast-util-to-string": { "version": "3.1.0", - "integrity": "sha512-n4Vypz/DZgwo0iMHLQL49dJzlp7YtAJP+N07MZHpjPf/5XJuHUWstviF4Mn2jEiR/GNmtnRRqnwsXExk3igfFA==", "dev": true }, "mdurl": { "version": "1.0.1", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", "dev": true }, "merge-stream": { "version": "2.0.0", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, "merge2": { "version": "1.4.1", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true }, "micromark": { "version": "3.0.10", - "integrity": "sha512-ryTDy6UUunOXy2HPjelppgJ2sNfcPz1pLlMdA6Rz9jPzhLikWXv/irpWV/I2jd68Uhmny7hHxAlAhk4+vWggpg==", "dev": true, "requires": { "@types/debug": "^4.0.0", @@ -9354,7 +8671,6 @@ }, "micromark-core-commonmark": { "version": "1.0.6", - "integrity": "sha512-K+PkJTxqjFfSNkfAhp4GB+cZPfQd6dxtTXnf+RjZOV7T4EEXnvgzOcnp+eSTmpGk9d1S9sL6/lqrgSNn/s0HZA==", "dev": true, "requires": { "decode-named-character-reference": "^1.0.0", @@ -9377,7 +8693,6 @@ }, "micromark-factory-destination": { "version": "1.0.0", - "integrity": "sha512-eUBA7Rs1/xtTVun9TmV3gjfPz2wEwgK5R5xcbIM5ZYAtvGF6JkyaDsj0agx8urXnO31tEO6Ug83iVH3tdedLnw==", "dev": true, "requires": { "micromark-util-character": "^1.0.0", @@ -9387,7 +8702,6 @@ }, "micromark-factory-label": { "version": "1.0.2", - "integrity": "sha512-CTIwxlOnU7dEshXDQ+dsr2n+yxpP0+fn271pu0bwDIS8uqfFcumXpj5mLn3hSC8iw2MUr6Gx8EcKng1dD7i6hg==", "dev": true, "requires": { "micromark-util-character": "^1.0.0", @@ -9398,7 +8712,6 @@ }, "micromark-factory-space": { "version": "1.0.0", - "integrity": "sha512-qUmqs4kj9a5yBnk3JMLyjtWYN6Mzfcx8uJfi5XAveBniDevmZasdGBba5b4QsvRcAkmvGo5ACmSUmyGiKTLZew==", "dev": true, "requires": { "micromark-util-character": "^1.0.0", @@ -9407,7 +8720,6 @@ }, "micromark-factory-title": { "version": "1.0.2", - "integrity": "sha512-zily+Nr4yFqgMGRKLpTVsNl5L4PMu485fGFDOQJQBl2NFpjGte1e86zC0da93wf97jrc4+2G2GQudFMHn3IX+A==", "dev": true, "requires": { "micromark-factory-space": "^1.0.0", @@ -9419,7 +8731,6 @@ }, "micromark-factory-whitespace": { "version": "1.0.0", - "integrity": "sha512-Qx7uEyahU1lt1RnsECBiuEbfr9INjQTGa6Err+gF3g0Tx4YEviPbqqGKNv/NrBaE7dVHdn1bVZKM/n5I/Bak7A==", "dev": true, "requires": { "micromark-factory-space": "^1.0.0", @@ -9430,7 +8741,6 @@ }, "micromark-util-character": { "version": "1.1.0", - "integrity": "sha512-agJ5B3unGNJ9rJvADMJ5ZiYjBRyDpzKAOk01Kpi1TKhlT1APx3XZk6eN7RtSz1erbWHC2L8T3xLZ81wdtGRZzg==", "dev": true, "requires": { "micromark-util-symbol": "^1.0.0", @@ -9439,7 +8749,6 @@ }, "micromark-util-chunked": { "version": "1.0.0", - "integrity": "sha512-5e8xTis5tEZKgesfbQMKRCyzvffRRUX+lK/y+DvsMFdabAicPkkZV6gO+FEWi9RfuKKoxxPwNL+dFF0SMImc1g==", "dev": true, "requires": { "micromark-util-symbol": "^1.0.0" @@ -9447,7 +8756,6 @@ }, "micromark-util-classify-character": { "version": "1.0.0", - "integrity": "sha512-F8oW2KKrQRb3vS5ud5HIqBVkCqQi224Nm55o5wYLzY/9PwHGXC01tr3d7+TqHHz6zrKQ72Okwtvm/xQm6OVNZA==", "dev": true, "requires": { "micromark-util-character": "^1.0.0", @@ -9457,7 +8765,6 @@ }, "micromark-util-combine-extensions": { "version": "1.0.0", - "integrity": "sha512-J8H058vFBdo/6+AsjHp2NF7AJ02SZtWaVUjsayNFeAiydTxUwViQPxN0Hf8dp4FmCQi0UUFovFsEyRSUmFH3MA==", "dev": true, "requires": { "micromark-util-chunked": "^1.0.0", @@ -9466,7 +8773,6 @@ }, "micromark-util-decode-numeric-character-reference": { "version": "1.0.0", - "integrity": "sha512-OzO9AI5VUtrTD7KSdagf4MWgHMtET17Ua1fIpXTpuhclCqD8egFWo85GxSGvxgkGS74bEahvtM0WP0HjvV0e4w==", "dev": true, "requires": { "micromark-util-symbol": "^1.0.0" @@ -9474,7 +8780,6 @@ }, "micromark-util-decode-string": { "version": "1.0.2", - "integrity": "sha512-DLT5Ho02qr6QWVNYbRZ3RYOSSWWFuH3tJexd3dgN1odEuPNxCngTCXJum7+ViRAd9BbdxCvMToPOD/IvVhzG6Q==", "dev": true, "requires": { "decode-named-character-reference": "^1.0.0", @@ -9485,17 +8790,14 @@ }, "micromark-util-encode": { "version": "1.0.1", - "integrity": "sha512-U2s5YdnAYexjKDel31SVMPbfi+eF8y1U4pfiRW/Y8EFVCy/vgxk/2wWTxzcqE71LHtCuCzlBDRU2a5CQ5j+mQA==", "dev": true }, "micromark-util-html-tag-name": { "version": "1.1.0", - "integrity": "sha512-BKlClMmYROy9UiV03SwNmckkjn8QHVaWkqoAqzivabvdGcwNGMMMH/5szAnywmsTBUzDsU57/mFi0sp4BQO6dA==", "dev": true }, "micromark-util-normalize-identifier": { "version": "1.0.0", - "integrity": "sha512-yg+zrL14bBTFrQ7n35CmByWUTFsgst5JhA4gJYoty4Dqzj4Z4Fr/DHekSS5aLfH9bdlfnSvKAWsAgJhIbogyBg==", "dev": true, "requires": { "micromark-util-symbol": "^1.0.0" @@ -9503,7 +8805,6 @@ }, "micromark-util-resolve-all": { "version": "1.0.0", - "integrity": "sha512-CB/AGk98u50k42kvgaMM94wzBqozSzDDaonKU7P7jwQIuH2RU0TeBqGYJz2WY1UdihhjweivStrJ2JdkdEmcfw==", "dev": true, "requires": { "micromark-util-types": "^1.0.0" @@ -9511,7 +8812,6 @@ }, "micromark-util-sanitize-uri": { "version": "1.0.0", - "integrity": "sha512-cCxvBKlmac4rxCGx6ejlIviRaMKZc0fWm5HdCHEeDWRSkn44l6NdYVRyU+0nT1XC72EQJMZV8IPHF+jTr56lAg==", "dev": true, "requires": { "micromark-util-character": "^1.0.0", @@ -9521,7 +8821,6 @@ }, "micromark-util-subtokenize": { "version": "1.0.2", - "integrity": "sha512-d90uqCnXp/cy4G881Ub4psE57Sf8YD0pim9QdjCRNjfas2M1u6Lbt+XZK9gnHL2XFhnozZiEdCa9CNfXSfQ6xA==", "dev": true, "requires": { "micromark-util-chunked": "^1.0.0", @@ -9532,17 +8831,14 @@ }, "micromark-util-symbol": { "version": "1.0.1", - "integrity": "sha512-oKDEMK2u5qqAptasDAwWDXq0tG9AssVwAx3E9bBF3t/shRIGsWIRG+cGafs2p/SnDSOecnt6hZPCE2o6lHfFmQ==", "dev": true }, "micromark-util-types": { "version": "1.0.2", - "integrity": "sha512-DCfg/T8fcrhrRKTPjRrw/5LLvdGV7BHySf/1LOZx7TzWZdYRjogNtyNq885z3nNallwr3QUKARjqvHqX1/7t+w==", "dev": true }, "micromatch": { "version": "4.0.5", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, "requires": { "braces": "^3.0.2", @@ -9551,29 +8847,23 @@ }, "mime": { "version": "1.6.0", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "dev": true }, "mime-db": { - "version": "1.52.0", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + "version": "1.52.0" }, "mime-types": { "version": "2.1.35", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "requires": { "mime-db": "1.52.0" } }, "mimic-fn": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true }, "minimatch": { "version": "3.1.2", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -9581,12 +8871,10 @@ }, "minimist": { "version": "1.2.6", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, "minipass": { "version": "3.3.4", - "integrity": "sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw==", "dev": true, "requires": { "yallist": "^4.0.0" @@ -9594,7 +8882,6 @@ }, "minizlib": { "version": "2.1.2", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", "dev": true, "requires": { "minipass": "^3.0.0", @@ -9603,17 +8890,14 @@ }, "mkdirp": { "version": "1.0.4", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true }, "mkdirp2": { "version": "1.0.5", - "integrity": "sha512-xOE9xbICroUDmG1ye2h4bZ8WBie9EGmACaco8K8cx6RlkJJrxGIqjGqztAI+NMhexXBcdGbSEzI6N3EJPevxZw==", "dev": true }, "mocha": { "version": "9.2.2", - "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", "dev": true, "requires": { "@ungap/promise-all-settled": "1.1.2", @@ -9644,12 +8928,10 @@ "dependencies": { "ansi-colors": { "version": "4.1.1", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true }, "debug": { "version": "4.3.3", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "dev": true, "requires": { "ms": "2.1.2" @@ -9657,19 +8939,16 @@ "dependencies": { "ms": { "version": "2.1.2", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true } } }, "escape-string-regexp": { "version": "4.0.0", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, "glob": { "version": "7.2.0", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -9682,7 +8961,6 @@ "dependencies": { "minimatch": { "version": "3.1.2", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -9692,7 +8970,6 @@ }, "minimatch": { "version": "4.2.1", - "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -9700,12 +8977,10 @@ }, "ms": { "version": "2.1.3", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "serialize-javascript": { "version": "6.0.0", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", "dev": true, "requires": { "randombytes": "^2.1.0" @@ -9713,7 +8988,6 @@ }, "yargs": { "version": "16.2.0", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "requires": { "cliui": "^7.0.2", @@ -9729,51 +9003,40 @@ }, "mri": { "version": "1.2.0", - "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", "dev": true }, "ms": { "version": "2.1.2", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, "mylas": { "version": "2.1.11", - "resolved": "https://registry.npmjs.org/mylas/-/mylas-2.1.11.tgz", - "integrity": "sha512-krnPUl3n9/k52FGCltWMYcqp9SttxjRJEy0sWLk+g7mIa7wnZrmNSZ40Acx7ghzRSOsxt2rEqMbaq4jWlnTDKg==", "dev": true }, "nanoid": { "version": "3.3.1", - "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", "dev": true }, "neo-async": { "version": "2.6.2", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, "node-fetch": { "version": "2.6.7", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", "requires": { "whatwg-url": "^5.0.0" } }, "node-releases": { "version": "2.0.6", - "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", "dev": true }, "normalize-path": { "version": "3.0.0", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, "npm-run-path": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, "requires": { "path-key": "^3.0.0" @@ -9781,17 +9044,14 @@ }, "object-get": { "version": "2.1.1", - "integrity": "sha512-7n4IpLMzGGcLEMiQKsNR7vCe+N5E9LORFrtNUVy4sO3dj9a3HedZCxEL2T7QuLhcHN1NBuBsMOKaOsAYI9IIvg==", "dev": true }, "object-to-spawn-args": { "version": "2.0.1", - "integrity": "sha512-6FuKFQ39cOID+BMZ3QaphcC8Y4cw6LXBLyIgPU+OhIYwviJamPAn+4mITapnSBQrejB+NNp+FMskhD8Cq+Ys3w==", "dev": true }, "once": { "version": "1.4.0", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "requires": { "wrappy": "1" @@ -9799,8 +9059,6 @@ }, "onetime": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, "requires": { "mimic-fn": "^2.1.0" @@ -9808,18 +9066,14 @@ }, "opener": { "version": "1.5.2", - "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", "dev": true }, "ospath": { "version": "1.2.2", - "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz", - "integrity": "sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==", "dev": true }, "p-limit": { "version": "3.1.0", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "requires": { "yocto-queue": "^0.1.0" @@ -9827,7 +9081,6 @@ }, "p-locate": { "version": "5.0.0", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "requires": { "p-limit": "^3.0.2" @@ -9835,8 +9088,6 @@ }, "p-map": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", "dev": true, "requires": { "aggregate-error": "^3.0.0" @@ -9844,65 +9095,50 @@ }, "p-try": { "version": "2.2.0", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, "path-exists": { "version": "4.0.0", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true }, "path-is-absolute": { "version": "1.0.1", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true }, "path-key": { "version": "3.1.1", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, "path-parse": { "version": "1.0.7", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, "path-type": { "version": "4.0.0", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true }, "pend": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", "dev": true }, "performance-now": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", "dev": true }, "picocolors": { "version": "1.0.0", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "dev": true }, "picomatch": { "version": "2.3.1", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, "pify": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true }, "pkg-dir": { "version": "4.2.0", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, "requires": { "find-up": "^4.0.0" @@ -9910,7 +9146,6 @@ "dependencies": { "find-up": { "version": "4.1.0", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "requires": { "locate-path": "^5.0.0", @@ -9919,7 +9154,6 @@ }, "locate-path": { "version": "5.0.0", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "requires": { "p-locate": "^4.1.0" @@ -9927,7 +9161,6 @@ }, "p-limit": { "version": "2.3.0", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -9935,7 +9168,6 @@ }, "p-locate": { "version": "4.1.0", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "requires": { "p-limit": "^2.2.0" @@ -9945,8 +9177,6 @@ }, "plimit-lit": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/plimit-lit/-/plimit-lit-1.3.0.tgz", - "integrity": "sha512-63qOoSzggsjBHPVbaKeEtsO8oWTeyJxUfVRLkoc59pRkDiCCLvqn2tCgAkfbejrx+V5zJtlG2wqkk/Db6cwlVQ==", "dev": true, "requires": { "queue-lit": "^1.3.0" @@ -9954,7 +9184,6 @@ }, "portfinder": { "version": "1.0.29", - "integrity": "sha512-Z5+DarHWCKlufshB9Z1pN95oLtANoY5Wn9X3JGELGyQ6VhEcBfT2t+1fGUBq7MwUant6g/mqowH+4HifByPbiQ==", "dev": true, "requires": { "async": "^2.6.4", @@ -9964,7 +9193,6 @@ "dependencies": { "async": { "version": "2.6.4", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", "dev": true, "requires": { "lodash": "^4.17.14" @@ -9972,7 +9200,6 @@ }, "debug": { "version": "3.2.7", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "requires": { "ms": "^2.1.1" @@ -9980,7 +9207,6 @@ }, "mkdirp": { "version": "0.5.6", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, "requires": { "minimist": "^1.2.6" @@ -9990,26 +9216,18 @@ }, "pretty-bytes": { "version": "5.6.0", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", - "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", "dev": true }, "proxy-from-env": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", - "integrity": "sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==", "dev": true }, "psl": { "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", "dev": true }, "pump": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, "requires": { "end-of-stream": "^1.1.0", @@ -10018,28 +9236,22 @@ }, "punycode": { "version": "2.1.1", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, "qs": { "version": "6.5.3", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", "dev": true }, "queue-lit": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/queue-lit/-/queue-lit-1.3.0.tgz", - "integrity": "sha512-3HpQ7bD2ct56qPithPr5IzH2Oij3WVPcgevCJ+mI9HAfVJKCqQ2yiFYgb4AUkLfsCmmbVDy9luvE97Ztzr5eBg==", "dev": true }, "queue-microtask": { "version": "1.2.3", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, "randombytes": { "version": "2.1.0", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, "requires": { "safe-buffer": "^5.1.0" @@ -10047,7 +9259,6 @@ }, "readdirp": { "version": "3.6.0", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "requires": { "picomatch": "^2.2.1" @@ -10055,7 +9266,6 @@ }, "rechoir": { "version": "0.7.1", - "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", "dev": true, "requires": { "resolve": "^1.9.0" @@ -10063,7 +9273,6 @@ }, "reduce-extract": { "version": "1.0.0", - "integrity": "sha512-QF8vjWx3wnRSL5uFMyCjDeDc5EBMiryoT9tz94VvgjKfzecHAVnqmXAwQDcr7X4JmLc2cjkjFGCVzhMqDjgR9g==", "dev": true, "requires": { "test-value": "^1.0.1" @@ -10071,7 +9280,6 @@ "dependencies": { "array-back": { "version": "1.0.4", - "integrity": "sha512-1WxbZvrmyhkNoeYcizokbmh5oiOCIfyvGtcqbK3Ls1v1fKcquzxnQSceOx6tzq7jmai2kFLWIpGND2cLhH6TPw==", "dev": true, "requires": { "typical": "^2.6.0" @@ -10079,7 +9287,6 @@ }, "test-value": { "version": "1.1.0", - "integrity": "sha512-wrsbRo7qP+2Je8x8DsK8ovCGyxe3sYfQwOraIY/09A2gFXU9DYKiTF14W4ki/01AEh56kMzAmlj9CaHGDDUBJA==", "dev": true, "requires": { "array-back": "^1.0.2", @@ -10090,17 +9297,14 @@ }, "reduce-flatten": { "version": "3.0.1", - "integrity": "sha512-bYo+97BmUUOzg09XwfkwALt4PQH1M5L0wzKerBt6WLm3Fhdd43mMS89HiT1B9pJIqko/6lWx3OnV4J9f2Kqp5Q==", "dev": true }, "reduce-unique": { "version": "2.0.1", - "integrity": "sha512-x4jH/8L1eyZGR785WY+ePtyMNhycl1N2XOLxhCbzZFaqF4AXjLzqSxa2UHgJ2ZVR/HHyPOvl1L7xRnW8ye5MdA==", "dev": true }, "reduce-without": { "version": "1.0.1", - "integrity": "sha512-zQv5y/cf85sxvdrKPlfcRzlDn/OqKFThNimYmsS3flmkioKvkUGn2Qg9cJVoQiEvdxFGLE0MQER/9fZ9sUqdxg==", "dev": true, "requires": { "test-value": "^2.0.0" @@ -10108,7 +9312,6 @@ "dependencies": { "array-back": { "version": "1.0.4", - "integrity": "sha512-1WxbZvrmyhkNoeYcizokbmh5oiOCIfyvGtcqbK3Ls1v1fKcquzxnQSceOx6tzq7jmai2kFLWIpGND2cLhH6TPw==", "dev": true, "requires": { "typical": "^2.6.0" @@ -10116,7 +9319,6 @@ }, "test-value": { "version": "2.1.0", - "integrity": "sha512-+1epbAxtKeXttkGFMTX9H42oqzOTufR1ceCF+GYA5aOmvaPq9wd4PUS8329fn2RRLGNeUkgRLnVpycjx8DsO2w==", "dev": true, "requires": { "array-back": "^1.0.3", @@ -10127,7 +9329,6 @@ }, "remark-parse": { "version": "10.0.1", - "integrity": "sha512-1fUyHr2jLsVOkhbvPRBJ5zTKZZyD6yZzYaWCS6BPBdQ8vEMBCH+9zNCDA6tET/zHCi/jLqjCWtlJZUPk+DbnFw==", "dev": true, "requires": { "@types/mdast": "^3.0.0", @@ -10137,8 +9338,6 @@ }, "request-progress": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", - "integrity": "sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==", "dev": true, "requires": { "throttleit": "^1.0.0" @@ -10146,23 +9345,18 @@ }, "require-directory": { "version": "2.1.1", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true }, "require-main-filename": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, "requires-port": { "version": "1.0.0", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "dev": true }, "requizzle": { "version": "0.2.3", - "integrity": "sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==", "dev": true, "requires": { "lodash": "^4.17.14" @@ -10170,7 +9364,6 @@ }, "resolve": { "version": "1.22.1", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", "dev": true, "requires": { "is-core-module": "^2.9.0", @@ -10180,7 +9373,6 @@ }, "resolve-cwd": { "version": "3.0.0", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, "requires": { "resolve-from": "^5.0.0" @@ -10188,13 +9380,10 @@ }, "resolve-from": { "version": "5.0.0", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true }, "restore-cursor": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "dev": true, "requires": { "onetime": "^5.1.0", @@ -10203,18 +9392,14 @@ }, "reusify": { "version": "1.0.4", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true }, "rfdc": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", - "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", "dev": true }, "rimraf": { "version": "3.0.2", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, "requires": { "glob": "^7.1.3" @@ -10222,7 +9407,6 @@ }, "run-parallel": { "version": "1.2.0", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, "requires": { "queue-microtask": "^1.2.2" @@ -10230,7 +9414,6 @@ }, "rxjs": { "version": "7.5.6", - "integrity": "sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw==", "dev": true, "requires": { "tslib": "^2.1.0" @@ -10238,7 +9421,6 @@ }, "sade": { "version": "1.8.1", - "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", "dev": true, "requires": { "mri": "^1.1.0" @@ -10246,17 +9428,14 @@ }, "safe-buffer": { "version": "5.2.1", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true }, "safer-buffer": { "version": "2.1.2", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, "schema-utils": { "version": "3.1.1", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", "dev": true, "requires": { "@types/json-schema": "^7.0.8", @@ -10266,13 +9445,10 @@ }, "secure-compare": { "version": "3.0.1", - "integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==", "dev": true }, "semver": { "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -10280,7 +9456,6 @@ }, "serialize-javascript": { "version": "5.0.1", - "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", "dev": true, "requires": { "randombytes": "^2.1.0" @@ -10288,13 +9463,10 @@ }, "set-blocking": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, "shallow-clone": { "version": "3.0.1", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", "dev": true, "requires": { "kind-of": "^6.0.2" @@ -10302,7 +9474,6 @@ }, "shebang-command": { "version": "2.0.0", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "requires": { "shebang-regex": "^3.0.0" @@ -10310,29 +9481,22 @@ }, "shebang-regex": { "version": "3.0.0", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, "shell-quote": { "version": "1.7.3", - "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==", "dev": true }, "signal-exit": { "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, "slash": { "version": "3.0.0", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, "slice-ansi": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", "dev": true, "requires": { "ansi-styles": "^4.0.0", @@ -10342,7 +9506,6 @@ }, "sort-array": { "version": "4.1.5", - "integrity": "sha512-Ya4peoS1fgFN42RN1REk2FgdNOeLIEMKFGJvs7VTP3OklF8+kl2SkpVliZ4tk/PurWsrWRsdNdU+tgyOBkB9sA==", "dev": true, "requires": { "array-back": "^5.0.0", @@ -10351,24 +9514,20 @@ "dependencies": { "array-back": { "version": "5.0.0", - "integrity": "sha512-kgVWwJReZWmVuWOQKEOohXKJX+nD02JAZ54D1RRWlv8L0NebauKAaFxACKzB74RTclt1+WNz5KHaLRDAPZbDEw==", "dev": true }, "typical": { "version": "6.0.1", - "integrity": "sha512-+g3NEp7fJLe9DPa1TArHm9QAA7YciZmWnfAqEaFrBihQ7epOv9i99rjtgb6Iz0wh3WuQDjsCTDfgRoGnmHN81A==", "dev": true } } }, "source-map": { "version": "0.6.1", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, "source-map-support": { "version": "0.5.21", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -10377,13 +9536,10 @@ }, "spawn-command": { "version": "0.0.2-1", - "integrity": "sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg==", "dev": true }, "sshpk": { "version": "1.17.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", - "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", "dev": true, "requires": { "asn1": "~0.2.3", @@ -10399,7 +9555,6 @@ }, "stream-connect": { "version": "1.0.2", - "integrity": "sha512-68Kl+79cE0RGKemKkhxTSg8+6AGrqBt+cbZAXevg2iJ6Y3zX4JhA/sZeGzLpxW9cXhmqAcE7KnJCisUmIUfnFQ==", "dev": true, "requires": { "array-back": "^1.0.2" @@ -10407,7 +9562,6 @@ "dependencies": { "array-back": { "version": "1.0.4", - "integrity": "sha512-1WxbZvrmyhkNoeYcizokbmh5oiOCIfyvGtcqbK3Ls1v1fKcquzxnQSceOx6tzq7jmai2kFLWIpGND2cLhH6TPw==", "dev": true, "requires": { "typical": "^2.6.0" @@ -10417,12 +9571,10 @@ }, "stream-via": { "version": "1.0.4", - "integrity": "sha512-DBp0lSvX5G9KGRDTkR/R+a29H+Wk2xItOF+MpZLLNDWbEV9tGPnqLPxHEYjmiz8xGtJHRIqmI+hCjmNzqoA4nQ==", "dev": true }, "string-width": { "version": "4.2.3", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "requires": { "emoji-regex": "^8.0.0", @@ -10432,12 +9584,10 @@ "dependencies": { "ansi-regex": { "version": "5.0.1", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "strip-ansi": { "version": "6.0.1", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { "ansi-regex": "^5.0.1" @@ -10447,8 +9597,6 @@ }, "strip-ansi": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", "dev": true, "requires": { "ansi-regex": "^3.0.0" @@ -10456,24 +9604,19 @@ }, "strip-bom": { "version": "3.0.0", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, "optional": true }, "strip-final-newline": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true }, "strip-json-comments": { "version": "3.1.1", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, "supports-color": { "version": "8.1.1", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "requires": { "has-flag": "^4.0.0" @@ -10481,12 +9624,10 @@ }, "supports-preserve-symlinks-flag": { "version": "1.0.0", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true }, "table-layout": { "version": "0.4.5", - "integrity": "sha512-zTvf0mcggrGeTe/2jJ6ECkJHAQPIYEwDoqsiqBjI24mvRmQbInK5jq33fyypaCBxX08hMkfmdOqj6haT33EqWw==", "dev": true, "requires": { "array-back": "^2.0.0", @@ -10498,7 +9639,6 @@ "dependencies": { "array-back": { "version": "2.0.0", - "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", "dev": true, "requires": { "typical": "^2.6.1" @@ -10508,17 +9648,14 @@ }, "taffydb": { "version": "2.6.2", - "integrity": "sha512-y3JaeRSplks6NYQuCOj3ZFMO3j60rTwbuKCvZxsAraGYH2epusatvZ0baZYA01WsGqJBq/Dl6vOrMUJqyMj8kA==", "dev": true }, "tapable": { "version": "2.2.1", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "dev": true }, "tar": { "version": "6.1.11", - "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", "dev": true, "requires": { "chownr": "^2.0.0", @@ -10531,19 +9668,16 @@ "dependencies": { "chownr": { "version": "2.0.0", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", "dev": true } } }, "temp-path": { "version": "1.0.0", - "integrity": "sha512-TvmyH7kC6ZVTYkqCODjJIbgvu0FKiwQpZ4D1aknE7xpcDf/qEOB8KZEK5ef2pfbVoiBhNWs3yx4y+ESMtNYmlg==", "dev": true }, "terser": { "version": "5.14.2", - "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", "dev": true, "requires": { "@jridgewell/source-map": "^0.3.2", @@ -10554,14 +9688,12 @@ "dependencies": { "commander": { "version": "2.20.3", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true } } }, "terser-webpack-plugin": { "version": "5.3.3", - "integrity": "sha512-Fx60G5HNYknNTNQnzQ1VePRuu89ZVYWfjRAeT5rITuCY/1b08s49e5kSQwHDirKZWuoKOBRFS98EUUoZ9kLEwQ==", "dev": true, "requires": { "@jridgewell/trace-mapping": "^0.3.7", @@ -10573,7 +9705,6 @@ "dependencies": { "serialize-javascript": { "version": "6.0.0", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", "dev": true, "requires": { "randombytes": "^2.1.0" @@ -10583,7 +9714,6 @@ }, "test-value": { "version": "3.0.0", - "integrity": "sha512-sVACdAWcZkSU9x7AOmJo5TqE+GyNJknHaHsMrR6ZnhjVlVN9Yx6FjHrsKZ3BjIpPCT68zYesPWkakrNupwfOTQ==", "dev": true, "requires": { "array-back": "^2.0.0", @@ -10592,7 +9722,6 @@ "dependencies": { "array-back": { "version": "2.0.0", - "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", "dev": true, "requires": { "typical": "^2.6.1" @@ -10602,26 +9731,18 @@ }, "text-encoding": { "version": "0.7.0", - "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.7.0.tgz", - "integrity": "sha512-oJQ3f1hrOnbRLOcwKz0Liq2IcrvDeZRHXhd9RgLrsT+DjWY/nty1Hi7v3dtkaEYbPYe0mUoOfzRrMwfXXwgPUA==", "peer": true }, "throttleit": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz", - "integrity": "sha512-rkTVqu6IjfQ/6+uNuuc3sZek4CEYxTJom3IktzgdSxcZqdARuebbA/f4QmAxMQIxqq9ZLEUkSYqvuk1I6VKq4g==", "dev": true }, "through": { "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, "tmp": { "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", "dev": true, "requires": { "rimraf": "^3.0.0" @@ -10629,7 +9750,6 @@ }, "to-regex-range": { "version": "5.0.1", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "requires": { "is-number": "^7.0.0" @@ -10637,8 +9757,6 @@ }, "tough-cookie": { "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", "dev": true, "requires": { "psl": "^1.1.28", @@ -10646,22 +9764,18 @@ } }, "tr46": { - "version": "0.0.3", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + "version": "0.0.3" }, "tree-kill": { "version": "1.2.2", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", "dev": true }, "trough": { "version": "2.1.0", - "integrity": "sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==", "dev": true }, "ts-mocha": { "version": "9.0.2", - "integrity": "sha512-WyQjvnzwrrubl0JT7EC1yWmNpcsU3fOuBFfdps30zbmFBgKniSaSOyZMZx+Wq7kytUs5CY+pEbSYEbGfIKnXTw==", "dev": true, "requires": { "ts-node": "7.0.1", @@ -10670,13 +9784,10 @@ "dependencies": { "diff": { "version": "3.5.0", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", "dev": true }, "json5": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", "dev": true, "optional": true, "requires": { @@ -10685,7 +9796,6 @@ }, "mkdirp": { "version": "0.5.6", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, "requires": { "minimist": "^1.2.6" @@ -10693,7 +9803,6 @@ }, "ts-node": { "version": "7.0.1", - "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==", "dev": true, "requires": { "arrify": "^1.0.0", @@ -10708,8 +9817,6 @@ }, "tsconfig-paths": { "version": "3.14.1", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", - "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", "dev": true, "optional": true, "requires": { @@ -10721,14 +9828,12 @@ }, "yn": { "version": "2.0.0", - "integrity": "sha512-uTv8J/wiWTgUTg+9vLTi//leUl5vDQS6uii/emeTb2ssY7vl6QWf2fFbIIGjnhjvbdKlU0ed7QPgY1htTC86jQ==", "dev": true } } }, "ts-node": { "version": "10.9.1", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", "dev": true, "requires": { "@cspotcode/source-map-support": "^0.8.0", @@ -10748,15 +9853,12 @@ "dependencies": { "diff": { "version": "4.0.2", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true } } }, "tsc-alias": { "version": "1.7.0", - "resolved": "https://registry.npmjs.org/tsc-alias/-/tsc-alias-1.7.0.tgz", - "integrity": "sha512-n/K6g8S7Ec7Y/A2Z77Ikp2Uv1S1ERtT63ni69XV4W1YPT4rnNmz8ItgIiJYvKfFnKfqcZQ81UPjoKpMTxaC/rg==", "dev": true, "requires": { "chokidar": "^3.5.3", @@ -10769,21 +9871,16 @@ "dependencies": { "commander": { "version": "9.4.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.0.tgz", - "integrity": "sha512-sRPT+umqkz90UA8M1yqYfnHlZA7fF6nSphDtxeywPZ49ysjxDQybzk13CL+mXekDRG92skbcqCLVovuCusNmFw==", "dev": true } } }, "tslib": { "version": "2.4.0", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", "dev": true }, "tunnel-agent": { "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", "dev": true, "requires": { "safe-buffer": "^5.0.1" @@ -10791,13 +9888,10 @@ }, "tweetnacl": { "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", "dev": true }, "txm": { "version": "8.0.2", - "integrity": "sha512-X6gUOeUAXzIrUvxp0zvOiJBM0l6Zk8NexaYugac6c6PW7dbrjgz6zC/rfSEjtd29VejI3VKvAnc6pDdOfSNKug==", "dev": true, "requires": { "async": "^3.2.1", @@ -10810,46 +9904,37 @@ "dependencies": { "supports-color": { "version": "9.2.2", - "integrity": "sha512-XC6g/Kgux+rJXmwokjm9ECpD6k/smUoS5LKlUCcsYr4IY3rW0XyAympon2RmxGrlnZURMpg5T18gWDP9CsHXFA==", "dev": true } } }, "type-fest": { "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true }, "typescript": { "version": "4.7.4", - "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", "dev": true }, "typical": { "version": "2.6.1", - "integrity": "sha512-ofhi8kjIje6npGozTip9Fr8iecmYfEbS06i0JnIg+rh51KakryWF4+jX8lLKZVhy6N+ID45WYSFCxPOdTWCzNg==", "dev": true }, "uc.micro": { "version": "1.0.6", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", "dev": true }, "uglify-js": { "version": "3.16.3", - "integrity": "sha512-uVbFqx9vvLhQg0iBaau9Z75AxWJ8tqM9AV890dIZCLApF4rTcyHwmAvLeEdYRs+BzYWu8Iw81F79ah0EfTXbaw==", "dev": true, "optional": true }, "underscore": { "version": "1.13.4", - "integrity": "sha512-BQFnUDuAQ4Yf/cYY5LNrK9NCJFKriaRbD9uR1fTeXnBeoa97W0i41qkZfGO9pSo8I5KzjAcSY2XYtdf0oKd7KQ==", "dev": true }, "unified": { "version": "10.1.2", - "integrity": "sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==", "dev": true, "requires": { "@types/unist": "^2.0.0", @@ -10863,7 +9948,6 @@ }, "union": { "version": "0.5.0", - "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==", "dev": true, "requires": { "qs": "^6.4.0" @@ -10871,7 +9955,6 @@ }, "unist-util-stringify-position": { "version": "3.0.2", - "integrity": "sha512-7A6eiDCs9UtjcwZOcCpM4aPII3bAAGv13E96IkawkOAW0OhH+yRxtY0lzo8KiHpzEMfH7Q+FizUmwp8Iqy5EWg==", "dev": true, "requires": { "@types/unist": "^2.0.0" @@ -10879,18 +9962,14 @@ }, "universalify": { "version": "2.0.0", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", "dev": true }, "untildify": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", - "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", "dev": true }, "update-browserslist-db": { "version": "1.0.5", - "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==", "dev": true, "requires": { "escalade": "^3.1.1", @@ -10899,7 +9978,6 @@ }, "uri-js": { "version": "4.4.1", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "requires": { "punycode": "^2.1.0" @@ -10907,18 +9985,14 @@ }, "url-join": { "version": "4.0.1", - "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", "dev": true }, "uuid": { "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true }, "uvu": { "version": "0.5.6", - "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", "dev": true, "requires": { "dequal": "^2.0.0", @@ -10929,13 +10003,10 @@ }, "v8-compile-cache-lib": { "version": "3.0.1", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true }, "verror": { "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", "dev": true, "requires": { "assert-plus": "^1.0.0", @@ -10945,7 +10016,6 @@ }, "vfile": { "version": "5.3.4", - "integrity": "sha512-KI+7cnst03KbEyN1+JE504zF5bJBZa+J+CrevLeyIMq0aPU681I2rQ5p4PlnQ6exFtWiUrg26QUdFMnAKR6PIw==", "dev": true, "requires": { "@types/unist": "^2.0.0", @@ -10956,7 +10026,6 @@ }, "vfile-message": { "version": "3.1.2", - "integrity": "sha512-QjSNP6Yxzyycd4SVOtmKKyTsSvClqBPJcd00Z0zuPj3hOIjg0rUPG6DbFGPvUKRgYyaIWLPKpuEclcuvb3H8qA==", "dev": true, "requires": { "@types/unist": "^2.0.0", @@ -10965,12 +10034,10 @@ }, "walk-back": { "version": "5.1.0", - "integrity": "sha512-Uhxps5yZcVNbLEAnb+xaEEMdgTXl9qAQDzKYejG2AZ7qPwRQ81lozY9ECDbjLPNWm7YsO1IK5rsP1KoQzXAcGA==", "dev": true }, "wasm-opt": { "version": "1.3.0", - "integrity": "sha512-24+IOboX4Sav0bI8Krwf0Y6dnpN4KxYtqpl0qWt86qVLsmayUqx1KMBrJTlQWNC+/dsqzQJjK6QvxNJsZYOgJg==", "dev": true, "requires": { "node-fetch": "^2.6.7", @@ -10979,7 +10046,6 @@ }, "wasm-pack": { "version": "0.10.1", - "integrity": "sha512-bw480KaaJQhL6UX8wAm6YCO497uaULDrsvUey3EB86dKAfFc6qCGVN1kItcwUklEeufonwo9mwz9/fy9xLOPtQ==", "dev": true, "requires": { "binary-install": "^0.1.0" @@ -10987,7 +10053,6 @@ }, "watchpack": { "version": "2.4.0", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", "dev": true, "requires": { "glob-to-regexp": "^0.4.1", @@ -10995,12 +10060,10 @@ } }, "webidl-conversions": { - "version": "3.0.1", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + "version": "3.0.1" }, "webpack": { "version": "5.74.0", - "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==", "dev": true, "requires": { "@types/eslint-scope": "^3.7.3", @@ -11031,7 +10094,6 @@ }, "webpack-cli": { "version": "4.10.0", - "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", "dev": true, "requires": { "@discoveryjs/json-ext": "^0.5.0", @@ -11050,14 +10112,12 @@ "dependencies": { "commander": { "version": "7.2.0", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", "dev": true } } }, "webpack-merge": { "version": "5.8.0", - "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", "dev": true, "requires": { "clone-deep": "^4.0.1", @@ -11066,12 +10126,10 @@ }, "webpack-sources": { "version": "3.2.3", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", "dev": true }, "whatwg-encoding": { "version": "2.0.0", - "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", "dev": true, "requires": { "iconv-lite": "0.6.3" @@ -11079,7 +10137,6 @@ }, "whatwg-url": { "version": "5.0.0", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "requires": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -11087,7 +10144,6 @@ }, "which": { "version": "2.0.2", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "requires": { "isexe": "^2.0.0" @@ -11095,14 +10151,10 @@ }, "which-module": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", "dev": true }, "wide-align": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dev": true, "requires": { "string-width": "^1.0.2 || 2" @@ -11110,14 +10162,10 @@ "dependencies": { "is-fullwidth-code-point": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", "dev": true }, "string-width": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { "is-fullwidth-code-point": "^2.0.0", @@ -11128,17 +10176,14 @@ }, "wildcard": { "version": "2.0.0", - "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", "dev": true }, "wordwrap": { "version": "1.0.0", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", "dev": true }, "wordwrapjs": { "version": "3.0.0", - "integrity": "sha512-mO8XtqyPvykVCsrwj5MlOVWvSnCdT+C+QVbm6blradR7JExAhbkZ7hZ9A+9NUtwzSqrlUo9a67ws0EiILrvRpw==", "dev": true, "requires": { "reduce-flatten": "^1.0.1", @@ -11147,19 +10192,16 @@ "dependencies": { "reduce-flatten": { "version": "1.0.1", - "integrity": "sha512-j5WfFJfc9CoXv/WbwVLHq74i/hdTUpy+iNC534LxczMRP67vJeK3V9JOdnL0N1cIRbn9mYhE2yVjvvKXDxvNXQ==", "dev": true } } }, "workerpool": { "version": "6.2.0", - "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", "dev": true }, "wrap-ansi": { "version": "7.0.0", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "requires": { "ansi-styles": "^4.0.0", @@ -11169,12 +10211,10 @@ "dependencies": { "ansi-regex": { "version": "5.0.1", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "strip-ansi": { "version": "6.0.1", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { "ansi-regex": "^5.0.1" @@ -11184,27 +10224,22 @@ }, "wrappy": { "version": "1.0.2", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, "xmlcreate": { "version": "2.0.4", - "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", "dev": true }, "y18n": { "version": "5.0.8", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true }, "yallist": { "version": "4.0.0", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, "yargs": { "version": "17.5.1", - "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", "dev": true, "requires": { "cliui": "^7.0.2", @@ -11218,19 +10253,16 @@ "dependencies": { "yargs-parser": { "version": "21.1.1", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true } } }, "yargs-parser": { "version": "20.2.4", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", "dev": true }, "yargs-unparser": { "version": "2.0.0", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", "dev": true, "requires": { "camelcase": "^6.0.0", @@ -11241,15 +10273,12 @@ "dependencies": { "is-plain-obj": { "version": "2.1.0", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true } } }, "yauzl": { "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", "dev": true, "requires": { "buffer-crc32": "~0.2.3", @@ -11258,12 +10287,10 @@ }, "yn": { "version": "3.1.1", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true }, "yocto-queue": { "version": "0.1.0", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true } } diff --git a/bindings/wasm/package.json b/bindings/wasm/package.json index 48ce064f7f..9cf935643f 100644 --- a/bindings/wasm/package.json +++ b/bindings/wasm/package.json @@ -80,7 +80,7 @@ "node-fetch": "^2.6.7" }, "peerDependencies": { - "@cycraig/iota-client-wasm": "^0.5.0-alpha.1", + "@iota/iota-client-wasm": "^0.5.0-alpha.1", "@iota/iota.js": "^1.9.0-stardust.25" }, "engines": { diff --git a/documentation/docs/concepts/decentralized_identifiers/resolve.mdx b/documentation/docs/concepts/decentralized_identifiers/resolve.mdx index e4ed526525..4926acb7d0 100644 --- a/documentation/docs/concepts/decentralized_identifiers/resolve.mdx +++ b/documentation/docs/concepts/decentralized_identifiers/resolve.mdx @@ -64,7 +64,7 @@ async fn main() { ```js const { MixedResolver, IotaDID, IotaIdentityClient } = require('@iota/identity-wasm/node'); -const { Client } = require('@cycraig/iota-client-wasm/node'); +const { Client } = require('@iota/iota-client-wasm/node'); // Configure a client for the Shimmer testnet "rms". const nodeUrl = "https://api.testnet.shimmer.network/"; @@ -121,7 +121,7 @@ async fn main() { ```js const { IotaDID, IotaIdentityClient } = require('@iota/identity-wasm/node'); -const { Client } = require('@cycraig/iota-client-wasm/node'); +const { Client } = require('@iota/iota-client-wasm/node'); // Configure a client for the Shimmer testnet "rms". const nodeUrl = "https://api.testnet.shimmer.network/"; From eb1d226e5e334a6ac0b11d9d6eb1b09835863a5c Mon Sep 17 00:00:00 2001 From: cycraig Date: Thu, 15 Sep 2022 15:48:07 +0200 Subject: [PATCH 63/89] Use cargo-license-template (#1022) * Use cargo-license-template * Fix cargo-license-template CI check * Fix license headers in Rust files --- .github/workflows/format.yml | 9 +++++++++ .license_template_ignore | 1 + bindings/wasm/src/iota/iota_verification_method.rs | 2 +- examples/utils/utils.rs | 3 +++ rustfmt.toml | 1 - 5 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 .license_template_ignore diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index 9e8894b595..a8d1bfa227 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -31,6 +31,12 @@ jobs: override: true components: rustfmt + - name: Install cargo-license-template + uses: actions-rs/cargo@v1 + with: + command: install + args: cargo-license-template + - name: Install dprint run: npm install -g dprint #run: cargo install dprint # installing from source is slow, ~5 minutes @@ -64,3 +70,6 @@ jobs: - name: Cargo.toml fmt check run: dprint check + + - name: cargo-license-template check + run: cargo license-template --template .license_template --ignore .license_template_ignore --verbose diff --git a/.license_template_ignore b/.license_template_ignore new file mode 100644 index 0000000000..79da5be4fe --- /dev/null +++ b/.license_template_ignore @@ -0,0 +1 @@ +libjose/tests/fixtures/*.rs diff --git a/bindings/wasm/src/iota/iota_verification_method.rs b/bindings/wasm/src/iota/iota_verification_method.rs index 554b2b4c44..bcbb882cf8 100644 --- a/bindings/wasm/src/iota/iota_verification_method.rs +++ b/bindings/wasm/src/iota/iota_verification_method.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2022 Stiftung +// Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 use identity_iota::crypto::PublicKey; diff --git a/examples/utils/utils.rs b/examples/utils/utils.rs index 55a047bebd..c24f402623 100644 --- a/examples/utils/utils.rs +++ b/examples/utils/utils.rs @@ -1,3 +1,6 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + use std::path::PathBuf; use anyhow::Context; diff --git a/rustfmt.toml b/rustfmt.toml index 84a8d76d8a..c0842c2114 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,6 +1,5 @@ comment_width = 120 format_code_in_doc_comments = true -license_template_path = ".license_template" max_width = 120 normalize_comments = false normalize_doc_attributes = false From 747e5490873f86c435340f6052d718e1b15694b7 Mon Sep 17 00:00:00 2001 From: "Oliver E. Anderson" Date: Fri, 16 Sep 2022 14:24:18 +0200 Subject: [PATCH 64/89] Chore/rename mixed resolver (#1026) --- bindings/wasm/docs/api-reference.md | 236 +- bindings/wasm/package-lock.json | 3677 ++++++++++++----- .../wasm/src/resolver/constructor_input.rs | 2 +- bindings/wasm/src/resolver/mod.rs | 3 +- .../{mixed_resolver.rs => wasm_resolver.rs} | 14 +- bindings/wasm/tests/resolver.ts | 6 +- identity_credential/Cargo.toml | 2 +- .../src/validator/credential_validator.rs | 35 +- 8 files changed, 2820 insertions(+), 1155 deletions(-) rename bindings/wasm/src/resolver/{mixed_resolver.rs => wasm_resolver.rs} (97%) diff --git a/bindings/wasm/docs/api-reference.md b/bindings/wasm/docs/api-reference.md index b05c6ec6f2..01e8420d1e 100644 --- a/bindings/wasm/docs/api-reference.md +++ b/bindings/wasm/docs/api-reference.md @@ -59,13 +59,6 @@ and resolution of DID documents in Alias Outputs.

MethodType

Supported verification method types.

-
MixedResolver
-

Convenience type for resolving DID documents from different DID methods.

-

Also provides methods for resolving DID Documents associated with -verifiable Credentials and Presentations.

-

Configuration

-

The resolver will only be able to resolve DID documents for methods it has been configured for in the constructor.

-
Presentation
PresentationValidationOptions
@@ -85,6 +78,13 @@ See IProofOptions.

Associates a purpose with a Proof.

See https://w3c-ccg.github.io/security-vocab/#proofPurpose

+
Resolver
+

Convenience type for resolving DID documents from different DID methods.

+

Also provides methods for resolving DID Documents associated with +verifiable Credentials and Presentations.

+

Configuration

+

The resolver will only be able to resolve DID documents for methods it has been configured for in the constructor.

+
RevocationBitmap

A compressed bitmap for managing credential revocation.

@@ -3093,117 +3093,6 @@ Deserializes an instance from a JSON object. | --- | --- | | json | any | - - -## MixedResolver -Convenience type for resolving DID documents from different DID methods. - -Also provides methods for resolving DID Documents associated with -verifiable `Credentials` and `Presentations`. - -# Configuration -The resolver will only be able to resolve DID documents for methods it has been configured for in the constructor. - -**Kind**: global class - -* [MixedResolver](#MixedResolver) - * [new MixedResolver(config)](#new_MixedResolver_new) - * [.resolvePresentationIssuers(presentation)](#MixedResolver+resolvePresentationIssuers) ⇒ Promise.<Array.<(IotaDocument\|CoreDocument)>> - * [.resolvePresentationHolder(presentation)](#MixedResolver+resolvePresentationHolder) ⇒ Promise.<(IotaDocument\|CoreDocument)> - * [.verifyPresentation(presentation, options, fail_fast, holder, issuers)](#MixedResolver+verifyPresentation) ⇒ Promise.<void> - * [.resolve(did)](#MixedResolver+resolve) ⇒ Promise.<(IotaDocument\|CoreDocument)> - - - -### new MixedResolver(config) -Constructs a new `MixedResolver`. - -# Errors -If both a `client` is given and the `handlers` map contains the "iota" key the construction process -will throw an error as it is then ambiguous what should be . - - -| Param | Type | -| --- | --- | -| config | ResolverConfig | - - - -### mixedResolver.resolvePresentationIssuers(presentation) ⇒ Promise.<Array.<(IotaDocument\|CoreDocument)>> -Fetches all DID Documents of `Credential` issuers contained in a `Presentation`. -Issuer documents are returned in arbitrary order. - -# Errors -Errors if any issuer URL cannot be parsed to a DID whose associated method is supported by this Resolver, or -resolution fails. - -**Kind**: instance method of [MixedResolver](#MixedResolver) - -| Param | Type | -| --- | --- | -| presentation | [Presentation](#Presentation) | - - - -### mixedResolver.resolvePresentationHolder(presentation) ⇒ Promise.<(IotaDocument\|CoreDocument)> -Fetches the DID Document of the holder of a `Presentation`. - -# Errors -Errors if the holder URL is missing, cannot be parsed to a valid DID whose method is supported by the resolver, or -DID resolution fails. - -**Kind**: instance method of [MixedResolver](#MixedResolver) - -| Param | Type | -| --- | --- | -| presentation | [Presentation](#Presentation) | - - - -### mixedResolver.verifyPresentation(presentation, options, fail_fast, holder, issuers) ⇒ Promise.<void> -Verifies a `Presentation`. - -### Important -See `PresentationValidator::validate` for information about which properties get -validated and what is expected of the optional arguments `holder` and `issuer`. - -### Resolution -The DID Documents for the `holder` and `issuers` are optionally resolved if not given. -If you already have up-to-date versions of these DID Documents, you may want -to use `PresentationValidator::validate`. -See also `Resolver::resolvePresentationIssuers` and `Resolver::resolvePresentationHolder`. - -### Errors -Errors from resolving the holder and issuer DID Documents, if not provided, will be returned immediately. -Otherwise, errors from validating the presentation and its credentials will be returned -according to the `fail_fast` parameter. - -**Kind**: instance method of [MixedResolver](#MixedResolver) - -| Param | Type | -| --- | --- | -| presentation | [Presentation](#Presentation) | -| options | [PresentationValidationOptions](#PresentationValidationOptions) | -| fail_fast | number | -| holder | [IotaDocument](#IotaDocument) \| [CoreDocument](#CoreDocument) \| undefined | -| issuers | Array.<(IotaDocument\|CoreDocument)> \| undefined | - - - -### mixedResolver.resolve(did) ⇒ Promise.<(IotaDocument\|CoreDocument)> -Fetches the DID Document of the given DID. - -### Errors - -Errors if the resolver has not been configured to handle the method -corresponding to the given DID or the resolution process itself fails. - -**Kind**: instance method of [MixedResolver](#MixedResolver) - -| Param | Type | -| --- | --- | -| did | string | - ## Presentation @@ -3689,6 +3578,117 @@ Deserializes an instance from a JSON object. | --- | --- | | json | any | + + +## Resolver +Convenience type for resolving DID documents from different DID methods. + +Also provides methods for resolving DID Documents associated with +verifiable `Credentials` and `Presentations`. + +# Configuration +The resolver will only be able to resolve DID documents for methods it has been configured for in the constructor. + +**Kind**: global class + +* [Resolver](#Resolver) + * [new Resolver(config)](#new_Resolver_new) + * [.resolvePresentationIssuers(presentation)](#Resolver+resolvePresentationIssuers) ⇒ Promise.<Array.<(IotaDocument\|CoreDocument)>> + * [.resolvePresentationHolder(presentation)](#Resolver+resolvePresentationHolder) ⇒ Promise.<(IotaDocument\|CoreDocument)> + * [.verifyPresentation(presentation, options, fail_fast, holder, issuers)](#Resolver+verifyPresentation) ⇒ Promise.<void> + * [.resolve(did)](#Resolver+resolve) ⇒ Promise.<(IotaDocument\|CoreDocument)> + + + +### new Resolver(config) +Constructs a new `Resolver`. + +# Errors +If both a `client` is given and the `handlers` map contains the "iota" key the construction process +will throw an error as it is then ambiguous what should be . + + +| Param | Type | +| --- | --- | +| config | ResolverConfig | + + + +### resolver.resolvePresentationIssuers(presentation) ⇒ Promise.<Array.<(IotaDocument\|CoreDocument)>> +Fetches all DID Documents of `Credential` issuers contained in a `Presentation`. +Issuer documents are returned in arbitrary order. + +# Errors +Errors if any issuer URL cannot be parsed to a DID whose associated method is supported by this Resolver, or +resolution fails. + +**Kind**: instance method of [Resolver](#Resolver) + +| Param | Type | +| --- | --- | +| presentation | [Presentation](#Presentation) | + + + +### resolver.resolvePresentationHolder(presentation) ⇒ Promise.<(IotaDocument\|CoreDocument)> +Fetches the DID Document of the holder of a `Presentation`. + +# Errors +Errors if the holder URL is missing, cannot be parsed to a valid DID whose method is supported by the resolver, or +DID resolution fails. + +**Kind**: instance method of [Resolver](#Resolver) + +| Param | Type | +| --- | --- | +| presentation | [Presentation](#Presentation) | + + + +### resolver.verifyPresentation(presentation, options, fail_fast, holder, issuers) ⇒ Promise.<void> +Verifies a `Presentation`. + +### Important +See `PresentationValidator::validate` for information about which properties get +validated and what is expected of the optional arguments `holder` and `issuer`. + +### Resolution +The DID Documents for the `holder` and `issuers` are optionally resolved if not given. +If you already have up-to-date versions of these DID Documents, you may want +to use `PresentationValidator::validate`. +See also `Resolver::resolvePresentationIssuers` and `Resolver::resolvePresentationHolder`. + +### Errors +Errors from resolving the holder and issuer DID Documents, if not provided, will be returned immediately. +Otherwise, errors from validating the presentation and its credentials will be returned +according to the `fail_fast` parameter. + +**Kind**: instance method of [Resolver](#Resolver) + +| Param | Type | +| --- | --- | +| presentation | [Presentation](#Presentation) | +| options | [PresentationValidationOptions](#PresentationValidationOptions) | +| fail_fast | number | +| holder | [IotaDocument](#IotaDocument) \| [CoreDocument](#CoreDocument) \| undefined | +| issuers | Array.<(IotaDocument\|CoreDocument)> \| undefined | + + + +### resolver.resolve(did) ⇒ Promise.<(IotaDocument\|CoreDocument)> +Fetches the DID Document of the given DID. + +### Errors + +Errors if the resolver has not been configured to handle the method +corresponding to the given DID or the resolution process itself fails. + +**Kind**: instance method of [Resolver](#Resolver) + +| Param | Type | +| --- | --- | +| did | string | + ## RevocationBitmap diff --git a/bindings/wasm/package-lock.json b/bindings/wasm/package-lock.json index 431ac1306b..6abfdd1a68 100644 --- a/bindings/wasm/package-lock.json +++ b/bindings/wasm/package-lock.json @@ -43,9 +43,10 @@ } }, "node_modules/@babel/parser": { - "version": "7.18.11", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.1.tgz", + "integrity": "sha512-h7RCSorm1DdTVGJf3P2Mhj3kdnkmF/EiysUkzS2TdgAYqyjFdMQJbVuXOBej2SBJaXan/lIVtT6KkGbyyq753A==", "dev": true, - "license": "MIT", "bin": { "parser": "bin/babel-parser.js" }, @@ -55,8 +56,9 @@ }, "node_modules/@colors/colors": { "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", "dev": true, - "license": "MIT", "optional": true, "engines": { "node": ">=0.1.90" @@ -64,8 +66,9 @@ }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, - "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -75,8 +78,9 @@ }, "node_modules/@cypress/request": { "version": "2.88.10", + "resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.10.tgz", + "integrity": "sha512-Zp7F+R93N0yZyG34GutyTNr+okam7s/Fzc1+i3kcqOP8vk6OuajuE9qZJ6Rs+10/1JFtXFYMdyarnU1rZuJesg==", "dev": true, - "license": "Apache-2.0", "dependencies": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", @@ -103,8 +107,9 @@ }, "node_modules/@cypress/request/node_modules/form-data": { "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", "dev": true, - "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.6", @@ -116,8 +121,9 @@ }, "node_modules/@cypress/xvfb": { "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz", + "integrity": "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==", "dev": true, - "license": "MIT", "dependencies": { "debug": "^3.1.0", "lodash.once": "^4.1.1" @@ -125,23 +131,26 @@ }, "node_modules/@cypress/xvfb/node_modules/debug": { "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, - "license": "MIT", "dependencies": { "ms": "^2.1.1" } }, "node_modules/@discoveryjs/json-ext": { "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", "dev": true, - "license": "MIT", "engines": { "node": ">=10.0.0" } }, "node_modules/@iota/crypto.js": { - "version": "1.9.0-stardust.6", - "license": "Apache-2.0", + "version": "1.9.0-stardust.7", + "resolved": "https://registry.npmjs.org/@iota/crypto.js/-/crypto.js-1.9.0-stardust.7.tgz", + "integrity": "sha512-GiUByLYpLuf9i+Cpm5lFPQ9CnO0t54FXWKxf0iDPkqaI5smpBMGKHpqPuk4tAiSvUsIcZ4QIVIgBLgYuOssSCQ==", "dependencies": { "@iota/util.js": "^1.9.0-stardust.5", "big-integer": "^1.6.51" @@ -152,7 +161,8 @@ }, "node_modules/@iota/iota-client-wasm": { "version": "0.5.0-alpha.2", - "license": "Apache-2.0", + "resolved": "https://registry.npmjs.org/@iota/iota-client-wasm/-/iota-client-wasm-0.5.0-alpha.2.tgz", + "integrity": "sha512-mRlrFwTabOZznvamM0I7Ux+dfaGLOEJ5v1197Ni5tH0qbNtiL4h/HdtQIClEtFcZQ+MQ+Pp1rz7qwD3s7nJh1A==", "peer": true, "dependencies": { "@iota/types": "^1.0.0-beta.11", @@ -165,7 +175,8 @@ }, "node_modules/@iota/iota.js": { "version": "1.9.0-stardust.25", - "license": "Apache-2.0", + "resolved": "https://registry.npmjs.org/@iota/iota.js/-/iota.js-1.9.0-stardust.25.tgz", + "integrity": "sha512-mx/ZpkrUEgPjd1FxVxDKGpmDF/aTuUcRXjzZWjBL8v2w9dn3/Bs2fZ3AJw7wIwMG6v48AMXWq2yPwndpwDx/qw==", "peer": true, "dependencies": { "@iota/crypto.js": "^1.9.0-stardust.6", @@ -178,12 +189,14 @@ } }, "node_modules/@iota/types": { - "version": "1.0.0-beta.11", - "license": "Apache-2.0" + "version": "1.0.0-beta.12", + "resolved": "https://registry.npmjs.org/@iota/types/-/types-1.0.0-beta.12.tgz", + "integrity": "sha512-GNIvPBauq8jAuOG7Xyb0W08KV9J0jeZ08igcl6sj3vSSiDb5360tON1NShdCCLfql4vr7n6U5f9Gmv0uSRuTYA==" }, "node_modules/@iota/util.js": { "version": "1.9.0-stardust.5", - "license": "Apache-2.0", + "resolved": "https://registry.npmjs.org/@iota/util.js/-/util.js-1.9.0-stardust.5.tgz", + "integrity": "sha512-BGXzzjUQQYaV/H0bXJYKW+Zgd9PIN+HLPH8BcYhubI65X4G2ID23/osmpK/za+Ds6t/MkpdPKIiBwZPGXuCUBw==", "dependencies": { "big-integer": "^1.6.51" }, @@ -193,8 +206,9 @@ }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", "dev": true, - "license": "MIT", "dependencies": { "@jridgewell/set-array": "^1.0.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -206,24 +220,27 @@ }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/set-array": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/source-map": { "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", "dev": true, - "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" @@ -231,13 +248,15 @@ }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.14", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", "dev": true, - "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -245,8 +264,9 @@ }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, - "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -257,16 +277,18 @@ }, "node_modules/@nodelib/fs.stat": { "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, - "license": "MIT", "engines": { "node": ">= 8" } }, "node_modules/@nodelib/fs.walk": { "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, - "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -277,36 +299,42 @@ }, "node_modules/@tsconfig/node10": { "version": "1.0.9", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true }, "node_modules/@tsconfig/node12": { "version": "1.0.11", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true }, "node_modules/@tsconfig/node14": { "version": "1.0.3", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true }, "node_modules/@tsconfig/node16": { "version": "1.0.3", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "dev": true }, "node_modules/@types/debug": { "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", + "integrity": "sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==", "dev": true, - "license": "MIT", "dependencies": { "@types/ms": "*" } }, "node_modules/@types/eslint": { - "version": "8.4.5", + "version": "8.4.6", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.6.tgz", + "integrity": "sha512-/fqTbjxyFUaYNO7VcW5g+4npmqVACz1bB7RTHYuLj+PRjw9hrCwrUXVQFpChUS0JsyEFvMZ7U/PfmvWgxJhI9g==", "dev": true, - "license": "MIT", "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -314,8 +342,9 @@ }, "node_modules/@types/eslint-scope": { "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", "dev": true, - "license": "MIT", "dependencies": { "@types/eslint": "*", "@types/estree": "*" @@ -323,29 +352,34 @@ }, "node_modules/@types/estree": { "version": "0.0.51", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "dev": true }, "node_modules/@types/json-schema": { "version": "7.0.11", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true }, "node_modules/@types/json5": { "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true, - "license": "MIT", "optional": true }, "node_modules/@types/linkify-it": { "version": "3.0.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.2.tgz", + "integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==", + "dev": true }, "node_modules/@types/markdown-it": { "version": "12.2.3", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", + "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", "dev": true, - "license": "MIT", "dependencies": { "@types/linkify-it": "*", "@types/mdurl": "*" @@ -353,34 +387,40 @@ }, "node_modules/@types/mdast": { "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz", + "integrity": "sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==", "dev": true, - "license": "MIT", "dependencies": { "@types/unist": "*" } }, "node_modules/@types/mdurl": { "version": "1.0.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", + "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", + "dev": true }, "node_modules/@types/mocha": { "version": "9.1.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", + "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", + "dev": true }, "node_modules/@types/ms": { "version": "0.7.31", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", + "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==", + "dev": true }, "node_modules/@types/node": { - "version": "18.6.5", - "license": "MIT" + "version": "18.7.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.18.tgz", + "integrity": "sha512-m+6nTEOadJZuTPkKR/SYK3A2d7FZrgElol9UP1Kae90VVU4a6mxnPuLiIW1m4Cq4gZ/nWb9GrdVXJCoCazDAbg==" }, "node_modules/@types/node-fetch": { "version": "2.6.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.2.tgz", + "integrity": "sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==", "dependencies": { "@types/node": "*", "form-data": "^3.0.0" @@ -388,23 +428,27 @@ }, "node_modules/@types/sinonjs__fake-timers": { "version": "8.1.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz", + "integrity": "sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==", + "dev": true }, "node_modules/@types/sizzle": { "version": "2.3.3", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz", + "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==", + "dev": true }, "node_modules/@types/unist": { "version": "2.0.6", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", + "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==", + "dev": true }, "node_modules/@types/yauzl": { "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { "@types/node": "*" @@ -412,13 +456,15 @@ }, "node_modules/@ungap/promise-all-settled": { "version": "1.1.2", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true }, "node_modules/@webassemblyjs/ast": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", "dev": true, - "license": "MIT", "dependencies": { "@webassemblyjs/helper-numbers": "1.11.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.1" @@ -426,23 +472,27 @@ }, "node_modules/@webassemblyjs/floating-point-hex-parser": { "version": "1.11.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "dev": true }, "node_modules/@webassemblyjs/helper-api-error": { "version": "1.11.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "dev": true }, "node_modules/@webassemblyjs/helper-buffer": { "version": "1.11.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "dev": true }, "node_modules/@webassemblyjs/helper-numbers": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", "dev": true, - "license": "MIT", "dependencies": { "@webassemblyjs/floating-point-hex-parser": "1.11.1", "@webassemblyjs/helper-api-error": "1.11.1", @@ -451,13 +501,15 @@ }, "node_modules/@webassemblyjs/helper-wasm-bytecode": { "version": "1.11.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "dev": true }, "node_modules/@webassemblyjs/helper-wasm-section": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", "dev": true, - "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.11.1", "@webassemblyjs/helper-buffer": "1.11.1", @@ -467,29 +519,33 @@ }, "node_modules/@webassemblyjs/ieee754": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", "dev": true, - "license": "MIT", "dependencies": { "@xtuc/ieee754": "^1.2.0" } }, "node_modules/@webassemblyjs/leb128": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", "dev": true, - "license": "Apache-2.0", "dependencies": { "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/utf8": { "version": "1.11.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "dev": true }, "node_modules/@webassemblyjs/wasm-edit": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", "dev": true, - "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.11.1", "@webassemblyjs/helper-buffer": "1.11.1", @@ -503,8 +559,9 @@ }, "node_modules/@webassemblyjs/wasm-gen": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", "dev": true, - "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.11.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.1", @@ -515,8 +572,9 @@ }, "node_modules/@webassemblyjs/wasm-opt": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", "dev": true, - "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.11.1", "@webassemblyjs/helper-buffer": "1.11.1", @@ -526,8 +584,9 @@ }, "node_modules/@webassemblyjs/wasm-parser": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", "dev": true, - "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.11.1", "@webassemblyjs/helper-api-error": "1.11.1", @@ -539,8 +598,9 @@ }, "node_modules/@webassemblyjs/wast-printer": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", "dev": true, - "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.11.1", "@xtuc/long": "4.2.2" @@ -548,8 +608,9 @@ }, "node_modules/@webpack-cli/configtest": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", + "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", "dev": true, - "license": "MIT", "peerDependencies": { "webpack": "4.x.x || 5.x.x", "webpack-cli": "4.x.x" @@ -557,8 +618,9 @@ }, "node_modules/@webpack-cli/info": { "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz", + "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", "dev": true, - "license": "MIT", "dependencies": { "envinfo": "^7.7.3" }, @@ -568,8 +630,9 @@ }, "node_modules/@webpack-cli/serve": { "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz", + "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", "dev": true, - "license": "MIT", "peerDependencies": { "webpack-cli": "4.x.x" }, @@ -581,18 +644,21 @@ }, "node_modules/@xtuc/ieee754": { "version": "1.2.0", - "dev": true, - "license": "BSD-3-Clause" + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true }, "node_modules/@xtuc/long": { "version": "4.2.2", - "dev": true, - "license": "Apache-2.0" + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true }, "node_modules/acorn": { "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", "dev": true, - "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -602,24 +668,27 @@ }, "node_modules/acorn-import-assertions": { "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", "dev": true, - "license": "MIT", "peerDependencies": { "acorn": "^8" } }, "node_modules/acorn-walk": { "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.4.0" } }, "node_modules/aggregate-error": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "dev": true, - "license": "MIT", "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" @@ -630,8 +699,9 @@ }, "node_modules/ajv": { "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -645,24 +715,27 @@ }, "node_modules/ajv-keywords": { "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "dev": true, - "license": "MIT", "peerDependencies": { "ajv": "^6.9.1" } }, "node_modules/ansi-colors": { "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/ansi-escape-sequences": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-escape-sequences/-/ansi-escape-sequences-4.1.0.tgz", + "integrity": "sha512-dzW9kHxH011uBsidTXd14JXgzye/YLb2LzeKZ4bsgl/Knwx8AtbSFkkGxagdNOoh0DlqHCmfiEjWKBaqjOanVw==", "dev": true, - "license": "MIT", "dependencies": { "array-back": "^3.0.1" }, @@ -672,16 +745,18 @@ }, "node_modules/ansi-escape-sequences/node_modules/array-back": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", + "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/ansi-escapes": { "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, - "license": "MIT", "dependencies": { "type-fest": "^0.21.3" }, @@ -693,17 +768,19 @@ } }, "node_modules/ansi-regex": { - "version": "3.0.1", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, - "license": "MIT", "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -716,8 +793,9 @@ }, "node_modules/anymatch": { "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, - "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -728,6 +806,8 @@ }, "node_modules/arch": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", + "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", "dev": true, "funding": [ { @@ -742,109 +822,123 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "MIT" + ] }, "node_modules/arg": { "version": "4.1.3", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true }, "node_modules/argparse": { "version": "2.0.1", - "dev": true, - "license": "Python-2.0" + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true }, "node_modules/array-back": { "version": "6.2.2", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.2.tgz", + "integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==", "dev": true, - "license": "MIT", "engines": { "node": ">=12.17" } }, "node_modules/array-union": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/arrify": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/asn1": { "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", "dev": true, - "license": "MIT", "dependencies": { "safer-buffer": "~2.1.0" } }, "node_modules/assert-plus": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.8" } }, "node_modules/astral-regex": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/async": { "version": "3.2.4", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "dev": true }, "node_modules/asynckit": { "version": "0.4.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/at-least-node": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", "dev": true, - "license": "ISC", "engines": { "node": ">= 4.0.0" } }, "node_modules/aws-sign2": { "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", "dev": true, - "license": "Apache-2.0", "engines": { "node": "*" } }, "node_modules/aws4": { "version": "1.11.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "dev": true }, "node_modules/axios": { "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", "dev": true, - "license": "MIT", "dependencies": { "follow-redirects": "^1.14.0" } }, "node_modules/bail": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", "dev": true, - "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -852,11 +946,14 @@ }, "node_modules/balanced-match": { "version": "1.0.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true }, "node_modules/base64-js": { "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "dev": true, "funding": [ { @@ -871,13 +968,13 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "MIT" + ] }, "node_modules/basic-auth": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", "dev": true, - "license": "MIT", "dependencies": { "safe-buffer": "5.1.2" }, @@ -887,44 +984,50 @@ }, "node_modules/basic-auth/node_modules/safe-buffer": { "version": "5.1.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true }, "node_modules/bcrypt-pbkdf": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "tweetnacl": "^0.14.3" } }, "node_modules/big-integer": { "version": "1.6.51", - "license": "Unlicense", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", "engines": { "node": ">=0.6" } }, "node_modules/big.js": { "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", "dev": true, - "license": "MIT", "engines": { "node": "*" } }, "node_modules/binary-extensions": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/binary-install": { "version": "0.1.1", + "resolved": "https://registry.npmjs.org/binary-install/-/binary-install-0.1.1.tgz", + "integrity": "sha512-DqED0D/6LrS+BHDkKn34vhRqOGjy5gTMgvYZsGK2TpNbdPuz4h+MRlNgGv5QBRd7pWq/jylM4eKNCizgAq3kNQ==", "dev": true, - "license": "MIT", "dependencies": { "axios": "^0.21.1", "rimraf": "^3.0.2", @@ -936,18 +1039,21 @@ }, "node_modules/blob-util": { "version": "2.0.2", - "dev": true, - "license": "Apache-2.0" + "resolved": "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz", + "integrity": "sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==", + "dev": true }, "node_modules/bluebird": { "version": "3.7.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true }, "node_modules/brace-expansion": { "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -955,8 +1061,9 @@ }, "node_modules/braces": { "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, - "license": "MIT", "dependencies": { "fill-range": "^7.0.1" }, @@ -966,11 +1073,14 @@ }, "node_modules/browser-stdout": { "version": "1.3.1", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true }, "node_modules/browserslist": { - "version": "4.21.3", + "version": "4.21.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", "dev": true, "funding": [ { @@ -982,12 +1092,11 @@ "url": "https://tidelift.com/funding/github/npm/browserslist" } ], - "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001370", - "electron-to-chromium": "^1.4.202", + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.5" + "update-browserslist-db": "^1.0.9" }, "bin": { "browserslist": "cli.js" @@ -998,6 +1107,8 @@ }, "node_modules/buffer": { "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "dev": true, "funding": [ { @@ -1013,7 +1124,6 @@ "url": "https://feross.org/support" } ], - "license": "MIT", "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" @@ -1021,21 +1131,24 @@ }, "node_modules/buffer-crc32": { "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "dev": true, - "license": "MIT", "engines": { "node": "*" } }, "node_modules/buffer-from": { "version": "1.1.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true }, "node_modules/cache-point": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cache-point/-/cache-point-2.0.0.tgz", + "integrity": "sha512-4gkeHlFpSKgm3vm2gJN5sPqfmijYRFYCQ6tv5cLw0xVmT6r1z1vd4FNnpuOREco3cBs1G709sZ72LdgddKvL5w==", "dev": true, - "license": "MIT", "dependencies": { "array-back": "^4.0.1", "fs-then-native": "^2.0.0", @@ -1047,24 +1160,27 @@ }, "node_modules/cache-point/node_modules/array-back": { "version": "4.0.2", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz", + "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/cachedir": { "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz", + "integrity": "sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/camelcase": { "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -1073,7 +1189,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001375", + "version": "1.0.30001402", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001402.tgz", + "integrity": "sha512-Mx4MlhXO5NwuvXGgVb+hg65HZ+bhUYsz8QtDGDo2QmaJS2GBX47Xfi2koL86lc8K+l+htXeTEB/Aeqvezoo6Ew==", "dev": true, "funding": [ { @@ -1084,18 +1202,19 @@ "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/caniuse-lite" } - ], - "license": "CC-BY-4.0" + ] }, "node_modules/caseless": { "version": "0.12.0", - "dev": true, - "license": "Apache-2.0" + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", + "dev": true }, "node_modules/catharsis": { "version": "0.9.0", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", + "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", "dev": true, - "license": "MIT", "dependencies": { "lodash": "^4.17.15" }, @@ -1105,8 +1224,9 @@ }, "node_modules/chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -1120,8 +1240,9 @@ }, "node_modules/chalk/node_modules/supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -1131,8 +1252,9 @@ }, "node_modules/character-entities": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", "dev": true, - "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -1140,14 +1262,17 @@ }, "node_modules/check-more-types": { "version": "2.24.0", + "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", + "integrity": "sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.8.0" } }, "node_modules/chokidar": { "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, "funding": [ { @@ -1155,7 +1280,6 @@ "url": "https://paulmillr.com/funding/" } ], - "license": "MIT", "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -1172,31 +1296,44 @@ "fsevents": "~2.3.2" } }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/chrome-trace-event": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.0" } }, "node_modules/ci-info": { - "version": "3.3.2", - "dev": true, - "license": "MIT" + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.4.0.tgz", + "integrity": "sha512-t5QdPT5jq3o262DOQ8zA6E1tlH2upmUc4Hlvrbx1pGYJuiiHl7O7rvVNI+l8HTVhd/q3Qc9vqimkNk5yiXsAug==", + "dev": true }, "node_modules/clean-stack": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/cli-cursor": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "dev": true, - "license": "MIT", "dependencies": { "restore-cursor": "^3.1.0" }, @@ -1205,9 +1342,10 @@ } }, "node_modules/cli-table3": { - "version": "0.6.2", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", + "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==", "dev": true, - "license": "MIT", "dependencies": { "string-width": "^4.2.0" }, @@ -1220,8 +1358,9 @@ }, "node_modules/cli-truncate": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", "dev": true, - "license": "MIT", "dependencies": { "slice-ansi": "^3.0.0", "string-width": "^4.2.0" @@ -1235,37 +1374,20 @@ }, "node_modules/cliui": { "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, - "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^7.0.0" } }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/clone-deep": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", "dev": true, - "license": "MIT", "dependencies": { "is-plain-object": "^2.0.4", "kind-of": "^6.0.2", @@ -1277,8 +1399,9 @@ }, "node_modules/collect-all": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/collect-all/-/collect-all-1.0.4.tgz", + "integrity": "sha512-RKZhRwJtJEP5FWul+gkSMEnaK6H3AGPTTWOiRimCcs+rc/OmQE3Yhy1Q7A7KsdkG3ZXVdZq68Y6ONSdvkeEcKA==", "dev": true, - "license": "MIT", "dependencies": { "stream-connect": "^1.0.2", "stream-via": "^1.0.4" @@ -1289,8 +1412,9 @@ }, "node_modules/color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -1300,25 +1424,29 @@ }, "node_modules/color-name": { "version": "1.1.4", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/colorette": { "version": "2.0.19", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", + "dev": true }, "node_modules/colors": { "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.1.90" } }, "node_modules/combined-stream": { "version": "1.0.8", - "license": "MIT", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dependencies": { "delayed-stream": "~1.0.0" }, @@ -1328,8 +1456,9 @@ }, "node_modules/command-line-args": { "version": "5.2.1", + "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz", + "integrity": "sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==", "dev": true, - "license": "MIT", "dependencies": { "array-back": "^3.1.0", "find-replace": "^3.0.0", @@ -1342,24 +1471,27 @@ }, "node_modules/command-line-args/node_modules/array-back": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", + "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/command-line-args/node_modules/typical": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", + "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/command-line-tool": { "version": "0.8.0", + "resolved": "https://registry.npmjs.org/command-line-tool/-/command-line-tool-0.8.0.tgz", + "integrity": "sha512-Xw18HVx/QzQV3Sc5k1vy3kgtOeGmsKIqwtFFoyjI4bbcpSgnw2CWVULvtakyw4s6fhyAdI6soQQhXc2OzJy62g==", "dev": true, - "license": "MIT", "dependencies": { "ansi-escape-sequences": "^4.0.0", "array-back": "^2.0.0", @@ -1373,8 +1505,9 @@ }, "node_modules/command-line-tool/node_modules/array-back": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", + "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", "dev": true, - "license": "MIT", "dependencies": { "typical": "^2.6.1" }, @@ -1384,8 +1517,9 @@ }, "node_modules/command-line-usage": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-4.1.0.tgz", + "integrity": "sha512-MxS8Ad995KpdAC0Jopo/ovGIroV/m0KHwzKfXxKag6FHOkGsH8/lv5yjgablcRxCJJC0oJeUMuO/gmaq+Wq46g==", "dev": true, - "license": "MIT", "dependencies": { "ansi-escape-sequences": "^4.0.0", "array-back": "^2.0.0", @@ -1398,8 +1532,9 @@ }, "node_modules/command-line-usage/node_modules/array-back": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", + "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", "dev": true, - "license": "MIT", "dependencies": { "typical": "^2.6.1" }, @@ -1409,40 +1544,45 @@ }, "node_modules/commander": { "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 6" } }, "node_modules/common-sequence": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/common-sequence/-/common-sequence-2.0.2.tgz", + "integrity": "sha512-jAg09gkdkrDO9EWTdXfv80WWH3yeZl5oT69fGfedBNS9pXUKYInVJ1bJ+/ht2+Moeei48TmSbQDYMc8EOx9G0g==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/common-tags": { "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", "dev": true, - "license": "MIT", "engines": { "node": ">=4.0.0" } }, "node_modules/concat-map": { "version": "0.0.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true }, "node_modules/concurrently": { - "version": "7.3.0", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-7.4.0.tgz", + "integrity": "sha512-M6AfrueDt/GEna/Vg9BqQ+93yuvzkSKmoTixnwEJkH0LlcGrRC2eCmjeG1tLLHIYfpYJABokqSGyMcXjm96AFA==", "dev": true, - "license": "MIT", "dependencies": { "chalk": "^4.1.0", - "date-fns": "^2.16.1", + "date-fns": "^2.29.1", "lodash": "^4.17.21", "rxjs": "^7.0.0", "shell-quote": "^1.7.3", @@ -1452,32 +1592,39 @@ "yargs": "^17.3.1" }, "bin": { + "conc": "dist/bin/concurrently.js", "concurrently": "dist/bin/concurrently.js" }, "engines": { "node": "^12.20.0 || ^14.13.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" } }, "node_modules/config-master": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/config-master/-/config-master-3.1.0.tgz", + "integrity": "sha512-n7LBL1zBzYdTpF1mx5DNcZnZn05CWIdsdvtPL4MosvqbBUK3Rq6VWEtGUuF3Y0s9/CIhMejezqlSkP6TnCJ/9g==", "dev": true, - "license": "MIT", "dependencies": { "walk-back": "^2.0.1" } }, "node_modules/config-master/node_modules/walk-back": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/walk-back/-/walk-back-2.0.1.tgz", + "integrity": "sha512-Nb6GvBR8UWX1D+Le+xUq0+Q1kFmRBIWVrfLnQAOmcpEzA9oAxwJ9gIr36t9TWYfzvWRvuMtjHiVsJYEkXWaTAQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/copy-webpack-plugin": { "version": "7.0.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-7.0.0.tgz", + "integrity": "sha512-SLjQNa5iE3BoCP76ESU9qYo9ZkEWtXoZxDurHoqPchAFRblJ9g96xTeC560UXBMre1Nx6ixIIUfiY3VcjpJw3g==", "dev": true, - "license": "MIT", "dependencies": { "fast-glob": "^3.2.4", "glob-parent": "^5.1.1", @@ -1501,26 +1648,30 @@ }, "node_modules/core-util-is": { "version": "1.0.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true }, "node_modules/corser": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", + "integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4.0" } }, "node_modules/create-require": { "version": "1.1.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true }, "node_modules/cross-spawn": { "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, - "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -1531,10 +1682,11 @@ } }, "node_modules/cypress": { - "version": "10.6.0", + "version": "10.8.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-10.8.0.tgz", + "integrity": "sha512-QVse0dnLm018hgti2enKMVZR9qbIO488YGX06nH5j3Dg1isL38DwrBtyrax02CANU6y8F4EJUuyW6HJKw1jsFA==", "dev": true, "hasInstallScript": true, - "license": "MIT", "dependencies": { "@cypress/request": "^2.88.10", "@cypress/xvfb": "^1.2.4", @@ -1555,7 +1707,7 @@ "dayjs": "^1.10.4", "debug": "^4.3.2", "enquirer": "^2.3.6", - "eventemitter2": "^6.4.3", + "eventemitter2": "6.4.7", "execa": "4.1.0", "executable": "^4.1.1", "extract-zip": "2.0.1", @@ -1588,8 +1740,9 @@ }, "node_modules/cypress-multi-reporters": { "version": "1.6.1", + "resolved": "https://registry.npmjs.org/cypress-multi-reporters/-/cypress-multi-reporters-1.6.1.tgz", + "integrity": "sha512-FPeC0xWF1N6Myrwc2m7KC0xxlrtG8+x4hlsPFBDRWP8u/veR2x90pGaH3BuJfweV7xoQ4Zo85Qjhu3fgZGrBQQ==", "dev": true, - "license": "MIT", "peer": true, "dependencies": { "debug": "^4.1.1", @@ -1604,8 +1757,9 @@ }, "node_modules/cypress-parallel": { "version": "0.9.1", + "resolved": "https://registry.npmjs.org/cypress-parallel/-/cypress-parallel-0.9.1.tgz", + "integrity": "sha512-7VSfFr8HEEN6zkgo6SkG7pPoHK7VakFhEH1jbM4+Ire/I+O2jNzyd1bRUA+O3V2DIMow64ECDJKf13YHBon+BQ==", "dev": true, - "license": "MIT", "dependencies": { "cli-table3": "^0.6.0", "colors": "^1.4.0", @@ -1626,32 +1780,27 @@ }, "node_modules/cypress-parallel/node_modules/ansi-colors": { "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, - "node_modules/cypress-parallel/node_modules/ansi-regex": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/cypress-parallel/node_modules/camelcase": { "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/cypress-parallel/node_modules/chokidar": { "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", "dev": true, - "license": "MIT", "dependencies": { "anymatch": "~3.1.1", "braces": "~3.0.2", @@ -1670,8 +1819,9 @@ }, "node_modules/cypress-parallel/node_modules/debug": { "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, - "license": "MIT", "dependencies": { "ms": "2.1.2" }, @@ -1686,16 +1836,18 @@ }, "node_modules/cypress-parallel/node_modules/decamelize": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/cypress-parallel/node_modules/escape-string-regexp": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -1705,8 +1857,9 @@ }, "node_modules/cypress-parallel/node_modules/glob": { "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "dev": true, - "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -1724,8 +1877,9 @@ }, "node_modules/cypress-parallel/node_modules/js-yaml": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", + "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", "dev": true, - "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -1735,8 +1889,9 @@ }, "node_modules/cypress-parallel/node_modules/locate-path": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, - "license": "MIT", "dependencies": { "p-locate": "^4.1.0" }, @@ -1746,8 +1901,9 @@ }, "node_modules/cypress-parallel/node_modules/log-symbols": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", + "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", "dev": true, - "license": "MIT", "dependencies": { "chalk": "^4.0.0" }, @@ -1757,8 +1913,9 @@ }, "node_modules/cypress-parallel/node_modules/minimatch": { "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -1768,8 +1925,9 @@ }, "node_modules/cypress-parallel/node_modules/mocha": { "version": "8.4.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz", + "integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==", "dev": true, - "license": "MIT", "dependencies": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", @@ -1811,13 +1969,15 @@ }, "node_modules/cypress-parallel/node_modules/mocha/node_modules/ms": { "version": "2.1.3", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true }, "node_modules/cypress-parallel/node_modules/mocha/node_modules/yargs": { "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, - "license": "MIT", "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", @@ -1833,8 +1993,9 @@ }, "node_modules/cypress-parallel/node_modules/nanoid": { "version": "3.1.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", + "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", "dev": true, - "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -1844,8 +2005,9 @@ }, "node_modules/cypress-parallel/node_modules/p-limit": { "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, - "license": "MIT", "dependencies": { "p-try": "^2.0.0" }, @@ -1858,8 +2020,9 @@ }, "node_modules/cypress-parallel/node_modules/p-locate": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, - "license": "MIT", "dependencies": { "p-limit": "^2.2.0" }, @@ -1869,8 +2032,9 @@ }, "node_modules/cypress-parallel/node_modules/readdirp": { "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", "dev": true, - "license": "MIT", "dependencies": { "picomatch": "^2.2.1" }, @@ -1878,26 +2042,17 @@ "node": ">=8.10.0" } }, - "node_modules/cypress-parallel/node_modules/strip-ansi": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/cypress-parallel/node_modules/workerpool": { "version": "6.1.0", - "dev": true, - "license": "Apache-2.0" + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz", + "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==", + "dev": true }, "node_modules/cypress-parallel/node_modules/wrap-ansi": { "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -1909,8 +2064,9 @@ }, "node_modules/cypress-parallel/node_modules/yargs": { "version": "15.3.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz", + "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==", "dev": true, - "license": "MIT", "dependencies": { "cliui": "^6.0.0", "decamelize": "^1.2.0", @@ -1930,8 +2086,9 @@ }, "node_modules/cypress-parallel/node_modules/yargs/node_modules/cliui": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", "dev": true, - "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", @@ -1940,8 +2097,9 @@ }, "node_modules/cypress-parallel/node_modules/yargs/node_modules/find-up": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, - "license": "MIT", "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -1952,13 +2110,15 @@ }, "node_modules/cypress-parallel/node_modules/yargs/node_modules/y18n": { "version": "4.0.3", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true }, "node_modules/cypress-parallel/node_modules/yargs/node_modules/yargs-parser": { "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "dev": true, - "license": "ISC", "dependencies": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" @@ -1968,14 +2128,16 @@ } }, "node_modules/cypress/node_modules/@types/node": { - "version": "14.18.25", - "dev": true, - "license": "MIT" + "version": "14.18.29", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.29.tgz", + "integrity": "sha512-LhF+9fbIX4iPzhsRLpK5H7iPdvW8L4IwGciXQIOEcuF62+9nw/VQVsOViAOOGxY3OlOKGLFv0sWwJXdwQeTn6A==", + "dev": true }, "node_modules/cypress/node_modules/fs-extra": { "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, - "license": "MIT", "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", @@ -1988,8 +2150,9 @@ }, "node_modules/dashdash": { "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", "dev": true, - "license": "MIT", "dependencies": { "assert-plus": "^1.0.0" }, @@ -1998,9 +2161,10 @@ } }, "node_modules/date-fns": { - "version": "2.29.1", + "version": "2.29.3", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.3.tgz", + "integrity": "sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.11" }, @@ -2011,13 +2175,15 @@ }, "node_modules/dayjs": { "version": "1.11.5", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.5.tgz", + "integrity": "sha512-CAdX5Q3YW3Gclyo5Vpqkgpj8fSdLQcRuzfX6mC6Phy0nfJ0eGYOeS7m4mt2plDWLAtA4TqTakvbboHvUxfe4iA==", + "dev": true }, "node_modules/debug": { "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, - "license": "MIT", "dependencies": { "ms": "2.1.2" }, @@ -2032,8 +2198,9 @@ }, "node_modules/decamelize": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -2043,8 +2210,9 @@ }, "node_modules/decode-named-character-reference": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", + "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", "dev": true, - "license": "MIT", "dependencies": { "character-entities": "^2.0.0" }, @@ -2055,44 +2223,50 @@ }, "node_modules/deep-extend": { "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true, - "license": "MIT", "engines": { "node": ">=4.0.0" } }, "node_modules/delayed-stream": { "version": "1.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "engines": { "node": ">=0.4.0" } }, "node_modules/dequal": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/diff": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", "dev": true, - "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } }, "node_modules/diff-match-patch": { "version": "1.0.5", - "dev": true, - "license": "Apache-2.0" + "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", + "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==", + "dev": true }, "node_modules/dir-glob": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, - "license": "MIT", "dependencies": { "path-type": "^4.0.0" }, @@ -2102,8 +2276,9 @@ }, "node_modules/dmd": { "version": "6.1.0", + "resolved": "https://registry.npmjs.org/dmd/-/dmd-6.1.0.tgz", + "integrity": "sha512-0zQIJ873gay1scCTFZvHPWM9mVJBnaylB2NQDI8O9u8O32m00Jb6uxDKexZm8hjTRM7RiWe0FJ32pExHoXdwoQ==", "dev": true, - "license": "MIT", "dependencies": { "array-back": "^6.2.2", "cache-point": "^2.0.0", @@ -2124,43 +2299,49 @@ }, "node_modules/ecc-jsbn": { "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", "dev": true, - "license": "MIT", "dependencies": { "jsbn": "~0.1.0", "safer-buffer": "^2.1.0" } }, "node_modules/electron-to-chromium": { - "version": "1.4.213", - "dev": true, - "license": "ISC" + "version": "1.4.253", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.253.tgz", + "integrity": "sha512-1pezJ2E1UyBTGbA7fUlHdPSXQw1k+82VhTFLG5G0AUqLGvsZqFzleOblceqegZzxYX4kC7hGEEdzIQI9RZ1Cuw==", + "dev": true }, "node_modules/emoji-regex": { "version": "8.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true }, "node_modules/emojis-list": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", "dev": true, - "license": "MIT", "engines": { "node": ">= 4" } }, "node_modules/end-of-stream": { "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "dev": true, - "license": "MIT", "dependencies": { "once": "^1.4.0" } }, "node_modules/enhanced-resolve": { "version": "5.10.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", + "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", "dev": true, - "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -2171,8 +2352,9 @@ }, "node_modules/enquirer": { "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", "dev": true, - "license": "MIT", "dependencies": { "ansi-colors": "^4.1.1" }, @@ -2182,16 +2364,18 @@ }, "node_modules/entities": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", "dev": true, - "license": "BSD-2-Clause", "funding": { "url": "https://github.com/fb55/entities?sponsor=1" } }, "node_modules/envinfo": { "version": "7.8.1", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", + "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", "dev": true, - "license": "MIT", "bin": { "envinfo": "dist/cli.js" }, @@ -2201,29 +2385,33 @@ }, "node_modules/es-module-lexer": { "version": "0.9.3", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "dev": true }, "node_modules/escalade": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/escape-string-regexp": { "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.8.0" } }, "node_modules/eslint-scope": { "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" @@ -2234,8 +2422,9 @@ }, "node_modules/esrecurse": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" }, @@ -2245,42 +2434,48 @@ }, "node_modules/esrecurse/node_modules/estraverse": { "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, - "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } }, "node_modules/estraverse": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, - "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } }, "node_modules/eventemitter2": { "version": "6.4.7", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.7.tgz", + "integrity": "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==", + "dev": true }, "node_modules/eventemitter3": { "version": "4.0.7", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true }, "node_modules/events": { "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.8.x" } }, "node_modules/execa": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", "dev": true, - "license": "MIT", "dependencies": { "cross-spawn": "^7.0.0", "get-stream": "^5.0.0", @@ -2301,8 +2496,9 @@ }, "node_modules/executable": { "version": "4.1.1", + "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", + "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", "dev": true, - "license": "MIT", "dependencies": { "pify": "^2.2.0" }, @@ -2312,13 +2508,15 @@ }, "node_modules/extend": { "version": "3.0.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true }, "node_modules/extract-zip": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "debug": "^4.1.1", "get-stream": "^5.1.0", @@ -2336,21 +2534,24 @@ }, "node_modules/extsprintf": { "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", "dev": true, "engines": [ "node >=0.6.0" - ], - "license": "MIT" + ] }, "node_modules/fast-deep-equal": { "version": "3.1.3", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true }, "node_modules/fast-glob": { - "version": "3.2.11", + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", "dev": true, - "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -2364,37 +2565,42 @@ }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true }, "node_modules/fastest-levenshtein": { "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 4.9.1" } }, "node_modules/fastq": { "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", "dev": true, - "license": "ISC", "dependencies": { "reusify": "^1.0.4" } }, "node_modules/fd-slicer": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", "dev": true, - "license": "MIT", "dependencies": { "pend": "~1.2.0" } }, "node_modules/figures": { "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", "dev": true, - "license": "MIT", "dependencies": { "escape-string-regexp": "^1.0.5" }, @@ -2407,8 +2613,9 @@ }, "node_modules/file-set": { "version": "4.0.2", + "resolved": "https://registry.npmjs.org/file-set/-/file-set-4.0.2.tgz", + "integrity": "sha512-fuxEgzk4L8waGXaAkd8cMr73Pm0FxOVkn8hztzUW7BAHhOGH90viQNXbiOsnecCWmfInqU6YmAMwxRMdKETceQ==", "dev": true, - "license": "MIT", "dependencies": { "array-back": "^5.0.0", "glob": "^7.1.6" @@ -2419,16 +2626,18 @@ }, "node_modules/file-set/node_modules/array-back": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-5.0.0.tgz", + "integrity": "sha512-kgVWwJReZWmVuWOQKEOohXKJX+nD02JAZ54D1RRWlv8L0NebauKAaFxACKzB74RTclt1+WNz5KHaLRDAPZbDEw==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" } }, "node_modules/fill-range": { "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, - "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -2438,8 +2647,9 @@ }, "node_modules/find-replace": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", + "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", "dev": true, - "license": "MIT", "dependencies": { "array-back": "^3.0.1" }, @@ -2449,16 +2659,18 @@ }, "node_modules/find-replace/node_modules/array-back": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", + "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/find-up": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, - "license": "MIT", "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -2472,14 +2684,17 @@ }, "node_modules/flat": { "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "dev": true, - "license": "BSD-3-Clause", "bin": { "flat": "cli.js" } }, "node_modules/follow-redirects": { - "version": "1.15.1", + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", "dev": true, "funding": [ { @@ -2487,7 +2702,6 @@ "url": "https://github.com/sponsors/RubenVerborgh" } ], - "license": "MIT", "engines": { "node": ">=4.0" }, @@ -2499,15 +2713,17 @@ }, "node_modules/forever-agent": { "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", "dev": true, - "license": "Apache-2.0", "engines": { "node": "*" } }, "node_modules/form-data": { "version": "3.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -2519,8 +2735,9 @@ }, "node_modules/fs-extra": { "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, - "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -2532,8 +2749,9 @@ }, "node_modules/fs-minipass": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", "dev": true, - "license": "ISC", "dependencies": { "minipass": "^3.0.0" }, @@ -2543,34 +2761,53 @@ }, "node_modules/fs-then-native": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fs-then-native/-/fs-then-native-2.0.0.tgz", + "integrity": "sha512-X712jAOaWXkemQCAmWeg5rOT2i+KOpWz1Z/txk/cW0qlOu2oQ9H61vc5w3X/iyuUEfq/OyaFJ78/cZAQD1/bgA==", "dev": true, - "license": "MIT", "engines": { "node": ">=4.0.0" } }, "node_modules/fs.realpath": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, - "license": "ISC" + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } }, "node_modules/function-bind": { "version": "1.1.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true }, "node_modules/get-caller-file": { "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, - "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" } }, "node_modules/get-stream": { "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, - "license": "MIT", "dependencies": { "pump": "^3.0.0" }, @@ -2583,24 +2820,27 @@ }, "node_modules/getos": { "version": "3.2.1", + "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz", + "integrity": "sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==", "dev": true, - "license": "MIT", "dependencies": { "async": "^3.2.0" } }, "node_modules/getpass": { "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", "dev": true, - "license": "MIT", "dependencies": { "assert-plus": "^1.0.0" } }, "node_modules/glob": { "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, - "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -2618,16 +2858,18 @@ }, "node_modules/glob-escape": { "version": "0.0.2", + "resolved": "https://registry.npmjs.org/glob-escape/-/glob-escape-0.0.2.tgz", + "integrity": "sha512-L/cXYz8x7qer1HAyUQ+mbjcUsJVdpRxpAf7CwqHoNBs9vTpABlGfNN4tzkDxt+u3Z7ZncVyKlCNPtzb0R/7WbA==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.10" } }, "node_modules/glob-parent": { "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, - "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, @@ -2637,13 +2879,15 @@ }, "node_modules/glob-to-regexp": { "version": "0.4.1", - "dev": true, - "license": "BSD-2-Clause" + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true }, "node_modules/global-dirs": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz", + "integrity": "sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==", "dev": true, - "license": "MIT", "dependencies": { "ini": "2.0.0" }, @@ -2656,8 +2900,9 @@ }, "node_modules/globby": { "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, - "license": "MIT", "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", @@ -2675,21 +2920,24 @@ }, "node_modules/graceful-fs": { "version": "4.2.10", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true }, "node_modules/growl": { "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", "dev": true, - "license": "MIT", "engines": { "node": ">=4.x" } }, "node_modules/handlebars": { "version": "4.7.7", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", + "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", "dev": true, - "license": "MIT", "dependencies": { "minimist": "^1.2.5", "neo-async": "^2.6.0", @@ -2708,8 +2956,9 @@ }, "node_modules/has": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, - "license": "MIT", "dependencies": { "function-bind": "^1.1.1" }, @@ -2719,24 +2968,27 @@ }, "node_modules/has-flag": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/he": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true, - "license": "MIT", "bin": { "he": "bin/he" } }, "node_modules/html-encoding-sniffer": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", "dev": true, - "license": "MIT", "dependencies": { "whatwg-encoding": "^2.0.0" }, @@ -2746,8 +2998,9 @@ }, "node_modules/http-proxy": { "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", "dev": true, - "license": "MIT", "dependencies": { "eventemitter3": "^4.0.0", "follow-redirects": "^1.0.0", @@ -2759,8 +3012,9 @@ }, "node_modules/http-server": { "version": "14.1.1", + "resolved": "https://registry.npmjs.org/http-server/-/http-server-14.1.1.tgz", + "integrity": "sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==", "dev": true, - "license": "MIT", "dependencies": { "basic-auth": "^2.0.1", "chalk": "^4.1.2", @@ -2785,8 +3039,9 @@ }, "node_modules/http-signature": { "version": "1.3.6", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz", + "integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==", "dev": true, - "license": "MIT", "dependencies": { "assert-plus": "^1.0.0", "jsprim": "^2.0.2", @@ -2798,16 +3053,18 @@ }, "node_modules/human-signals": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", "dev": true, - "license": "Apache-2.0", "engines": { "node": ">=8.12.0" } }, "node_modules/iconv-lite": { "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, - "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -2817,6 +3074,8 @@ }, "node_modules/ieee754": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "dev": true, "funding": [ { @@ -2831,21 +3090,22 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "BSD-3-Clause" + ] }, "node_modules/ignore": { "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "dev": true, - "license": "MIT", "engines": { "node": ">= 4" } }, "node_modules/import-local": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", "dev": true, - "license": "MIT", "dependencies": { "pkg-dir": "^4.2.0", "resolve-cwd": "^3.0.0" @@ -2862,16 +3122,18 @@ }, "node_modules/indent-string": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/inflight": { "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, - "license": "ISC", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -2879,29 +3141,33 @@ }, "node_modules/inherits": { "version": "2.0.4", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true }, "node_modules/ini": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", "dev": true, - "license": "ISC", "engines": { "node": ">=10" } }, "node_modules/interpret": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.10" } }, "node_modules/is-binary-path": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, - "license": "MIT", "dependencies": { "binary-extensions": "^2.0.0" }, @@ -2911,6 +3177,8 @@ }, "node_modules/is-buffer": { "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", "dev": true, "funding": [ { @@ -2926,15 +3194,15 @@ "url": "https://feross.org/support" } ], - "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/is-ci": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", + "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", "dev": true, - "license": "MIT", "dependencies": { "ci-info": "^3.2.0" }, @@ -2944,8 +3212,9 @@ }, "node_modules/is-core-module": { "version": "2.10.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", + "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", "dev": true, - "license": "MIT", "dependencies": { "has": "^1.0.3" }, @@ -2955,24 +3224,27 @@ }, "node_modules/is-extglob": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/is-glob": { "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, - "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -2982,8 +3254,9 @@ }, "node_modules/is-installed-globally": { "version": "0.4.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", + "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", "dev": true, - "license": "MIT", "dependencies": { "global-dirs": "^3.0.0", "is-path-inside": "^3.0.2" @@ -2997,8 +3270,9 @@ }, "node_modules/is-npm": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz", + "integrity": "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -3008,24 +3282,27 @@ }, "node_modules/is-number": { "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.12.0" } }, "node_modules/is-path-inside": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/is-plain-obj": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", "dev": true, - "license": "MIT", "engines": { "node": ">=12" }, @@ -3035,8 +3312,9 @@ }, "node_modules/is-plain-object": { "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, - "license": "MIT", "dependencies": { "isobject": "^3.0.1" }, @@ -3046,8 +3324,9 @@ }, "node_modules/is-stream": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" }, @@ -3057,13 +3336,15 @@ }, "node_modules/is-typedarray": { "version": "1.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true }, "node_modules/is-unicode-supported": { "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -3073,26 +3354,30 @@ }, "node_modules/isexe": { "version": "2.0.0", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true }, "node_modules/isobject": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/isstream": { "version": "0.1.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", + "dev": true }, "node_modules/jest-worker": { "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "dev": true, - "license": "MIT", "dependencies": { "@types/node": "*", "merge-stream": "^2.0.0", @@ -3104,8 +3389,9 @@ }, "node_modules/js-yaml": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, - "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -3115,21 +3401,24 @@ }, "node_modules/js2xmlparser": { "version": "4.0.2", + "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", + "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", "dev": true, - "license": "Apache-2.0", "dependencies": { "xmlcreate": "^2.0.4" } }, "node_modules/jsbn": { "version": "0.1.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "dev": true }, "node_modules/jsdoc": { "version": "3.6.11", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.11.tgz", + "integrity": "sha512-8UCU0TYeIYD9KeLzEcAu2q8N/mx9O3phAGl32nmHlE0LpaJL71mMkP4d+QE5zWfNt50qheHtOZ0qoxVrsX5TUg==", "dev": true, - "license": "Apache-2.0", "dependencies": { "@babel/parser": "^7.9.4", "@types/markdown-it": "^12.2.3", @@ -3156,8 +3445,9 @@ }, "node_modules/jsdoc-api": { "version": "7.1.1", + "resolved": "https://registry.npmjs.org/jsdoc-api/-/jsdoc-api-7.1.1.tgz", + "integrity": "sha512-0pkuPCzVXiqsDAsVrNFXCkHzlyNepBIDVtwwehry4RJAnZmXtlAz7rh8F9FRz53u3NeynGbex+bpYWwi8lE66A==", "dev": true, - "license": "MIT", "dependencies": { "array-back": "^6.2.2", "cache-point": "^2.0.0", @@ -3175,8 +3465,9 @@ }, "node_modules/jsdoc-parse": { "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsdoc-parse/-/jsdoc-parse-6.1.0.tgz", + "integrity": "sha512-n/hDGQJa69IBun1yZAjqzV4gVR41+flZ3bIlm9fKvNe2Xjsd1/+zCo2+R9ls8LxtePgIWbpA1jU7xkB2lRdLLg==", "dev": true, - "license": "MIT", "dependencies": { "array-back": "^6.2.2", "lodash.omit": "^4.5.0", @@ -3191,8 +3482,9 @@ }, "node_modules/jsdoc-to-markdown": { "version": "7.1.1", + "resolved": "https://registry.npmjs.org/jsdoc-to-markdown/-/jsdoc-to-markdown-7.1.1.tgz", + "integrity": "sha512-CI86d63xAVNO+ENumWwmJ034lYe5iGU5GwjtTA11EuphP9tpnoi4hrKgR/J8uME0D+o4KUpVfwX1fjZhc8dEtg==", "dev": true, - "license": "MIT", "dependencies": { "array-back": "^6.2.2", "command-line-tool": "^0.8.0", @@ -3211,36 +3503,42 @@ }, "node_modules/jsdoc/node_modules/escape-string-regexp": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true }, "node_modules/json-schema": { "version": "0.4.0", - "dev": true, - "license": "(AFL-2.1 OR BSD-3-Clause)" + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "dev": true }, "node_modules/json-schema-traverse": { "version": "0.4.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true }, "node_modules/json-stringify-safe": { "version": "5.0.1", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true }, "node_modules/json5": { "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", "dev": true, - "license": "MIT", "bin": { "json5": "lib/cli.js" }, @@ -3250,8 +3548,9 @@ }, "node_modules/jsonfile": { "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, - "license": "MIT", "dependencies": { "universalify": "^2.0.0" }, @@ -3261,11 +3560,12 @@ }, "node_modules/jsprim": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", + "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==", "dev": true, "engines": [ "node >=0.6.0" ], - "license": "MIT", "dependencies": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", @@ -3275,48 +3575,54 @@ }, "node_modules/kind-of": { "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/klaw": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", + "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", "dev": true, - "license": "MIT", "dependencies": { "graceful-fs": "^4.1.9" } }, "node_modules/kleur": { "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/lazy-ass": { "version": "1.6.0", + "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", + "integrity": "sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==", "dev": true, - "license": "MIT", "engines": { "node": "> 0.8" } }, "node_modules/linkify-it": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", + "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", "dev": true, - "license": "MIT", "dependencies": { "uc.micro": "^1.0.1" } }, "node_modules/listr2": { "version": "3.14.0", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.14.0.tgz", + "integrity": "sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==", "dev": true, - "license": "MIT", "dependencies": { "cli-truncate": "^2.1.0", "colorette": "^2.0.16", @@ -3341,16 +3647,18 @@ }, "node_modules/loader-runner": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.11.5" } }, "node_modules/loader-utils": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", "dev": true, - "license": "MIT", "dependencies": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", @@ -3362,8 +3670,9 @@ }, "node_modules/locate-path": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, - "license": "MIT", "dependencies": { "p-locate": "^5.0.0" }, @@ -3376,38 +3685,45 @@ }, "node_modules/lodash": { "version": "4.17.21", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true }, "node_modules/lodash.camelcase": { "version": "4.3.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true }, "node_modules/lodash.omit": { "version": "4.5.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", + "integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==", + "dev": true }, "node_modules/lodash.once": { "version": "4.1.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "dev": true }, "node_modules/lodash.padend": { "version": "4.6.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz", + "integrity": "sha512-sOQs2aqGpbl27tmCS1QNZA09Uqp01ZzWfDUoD+xzTii0E7dSQfRKcRetFwa+uXaxaqL+TKm7CgD2JdKP7aZBSw==", + "dev": true }, "node_modules/lodash.pick": { "version": "4.4.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", + "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==", + "dev": true }, "node_modules/log-symbols": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, - "license": "MIT", "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" @@ -3421,8 +3737,9 @@ }, "node_modules/log-update": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", "dev": true, - "license": "MIT", "dependencies": { "ansi-escapes": "^4.3.0", "cli-cursor": "^3.1.0", @@ -3436,18 +3753,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/log-update/node_modules/ansi-regex": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/log-update/node_modules/slice-ansi": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", @@ -3460,21 +3770,11 @@ "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/log-update/node_modules/strip-ansi": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/log-update/node_modules/wrap-ansi": { "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -3486,8 +3786,9 @@ }, "node_modules/lru-cache": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, - "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, @@ -3497,13 +3798,15 @@ }, "node_modules/make-error": { "version": "1.3.6", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true }, "node_modules/markdown-it": { "version": "12.3.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", + "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", "dev": true, - "license": "MIT", "dependencies": { "argparse": "^2.0.1", "entities": "~2.1.0", @@ -3516,18 +3819,20 @@ } }, "node_modules/markdown-it-anchor": { - "version": "8.6.4", + "version": "8.6.5", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.5.tgz", + "integrity": "sha512-PI1qEHHkTNWT+X6Ip9w+paonfIQ+QZP9sCeMYi47oqhH+EsW8CrJ8J7CzV19QVOj6il8ATGbK2nTECj22ZHGvQ==", "dev": true, - "license": "Unlicense", "peerDependencies": { "@types/markdown-it": "*", "markdown-it": "*" } }, "node_modules/marked": { - "version": "4.0.18", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.1.0.tgz", + "integrity": "sha512-+Z6KDjSPa6/723PQYyc1axYZpYYpDnECDaU6hkaf5gqBieBkMKYReL5hteF2QizhlMbgbo8umXl/clZ67+GlsA==", "dev": true, - "license": "MIT", "bin": { "marked": "bin/marked.js" }, @@ -3537,8 +3842,9 @@ }, "node_modules/mdast-util-from-markdown": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.2.0.tgz", + "integrity": "sha512-iZJyyvKD1+K7QX1b5jXdE7Sc5dtoTry1vzV28UZZe8Z1xVnB/czKntJ7ZAkG0tANqRnBF6p3p7GpU1y19DTf2Q==", "dev": true, - "license": "MIT", "dependencies": { "@types/mdast": "^3.0.0", "@types/unist": "^2.0.0", @@ -3560,8 +3866,9 @@ }, "node_modules/mdast-util-to-string": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.1.0.tgz", + "integrity": "sha512-n4Vypz/DZgwo0iMHLQL49dJzlp7YtAJP+N07MZHpjPf/5XJuHUWstviF4Mn2jEiR/GNmtnRRqnwsXExk3igfFA==", "dev": true, - "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" @@ -3569,24 +3876,29 @@ }, "node_modules/mdurl": { "version": "1.0.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", + "dev": true }, "node_modules/merge-stream": { "version": "2.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true }, "node_modules/merge2": { "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 8" } }, "node_modules/micromark": { "version": "3.0.10", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.0.10.tgz", + "integrity": "sha512-ryTDy6UUunOXy2HPjelppgJ2sNfcPz1pLlMdA6Rz9jPzhLikWXv/irpWV/I2jd68Uhmny7hHxAlAhk4+vWggpg==", "dev": true, "funding": [ { @@ -3598,7 +3910,6 @@ "url": "https://opencollective.com/unified" } ], - "license": "MIT", "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", @@ -3621,6 +3932,8 @@ }, "node_modules/micromark-core-commonmark": { "version": "1.0.6", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.0.6.tgz", + "integrity": "sha512-K+PkJTxqjFfSNkfAhp4GB+cZPfQd6dxtTXnf+RjZOV7T4EEXnvgzOcnp+eSTmpGk9d1S9sL6/lqrgSNn/s0HZA==", "dev": true, "funding": [ { @@ -3632,7 +3945,6 @@ "url": "https://opencollective.com/unified" } ], - "license": "MIT", "dependencies": { "decode-named-character-reference": "^1.0.0", "micromark-factory-destination": "^1.0.0", @@ -3654,6 +3966,8 @@ }, "node_modules/micromark-factory-destination": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.0.0.tgz", + "integrity": "sha512-eUBA7Rs1/xtTVun9TmV3gjfPz2wEwgK5R5xcbIM5ZYAtvGF6JkyaDsj0agx8urXnO31tEO6Ug83iVH3tdedLnw==", "dev": true, "funding": [ { @@ -3665,7 +3979,6 @@ "url": "https://opencollective.com/unified" } ], - "license": "MIT", "dependencies": { "micromark-util-character": "^1.0.0", "micromark-util-symbol": "^1.0.0", @@ -3674,6 +3987,8 @@ }, "node_modules/micromark-factory-label": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.0.2.tgz", + "integrity": "sha512-CTIwxlOnU7dEshXDQ+dsr2n+yxpP0+fn271pu0bwDIS8uqfFcumXpj5mLn3hSC8iw2MUr6Gx8EcKng1dD7i6hg==", "dev": true, "funding": [ { @@ -3685,7 +4000,6 @@ "url": "https://opencollective.com/unified" } ], - "license": "MIT", "dependencies": { "micromark-util-character": "^1.0.0", "micromark-util-symbol": "^1.0.0", @@ -3695,6 +4009,8 @@ }, "node_modules/micromark-factory-space": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.0.0.tgz", + "integrity": "sha512-qUmqs4kj9a5yBnk3JMLyjtWYN6Mzfcx8uJfi5XAveBniDevmZasdGBba5b4QsvRcAkmvGo5ACmSUmyGiKTLZew==", "dev": true, "funding": [ { @@ -3706,7 +4022,6 @@ "url": "https://opencollective.com/unified" } ], - "license": "MIT", "dependencies": { "micromark-util-character": "^1.0.0", "micromark-util-types": "^1.0.0" @@ -3714,6 +4029,8 @@ }, "node_modules/micromark-factory-title": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.0.2.tgz", + "integrity": "sha512-zily+Nr4yFqgMGRKLpTVsNl5L4PMu485fGFDOQJQBl2NFpjGte1e86zC0da93wf97jrc4+2G2GQudFMHn3IX+A==", "dev": true, "funding": [ { @@ -3725,7 +4042,6 @@ "url": "https://opencollective.com/unified" } ], - "license": "MIT", "dependencies": { "micromark-factory-space": "^1.0.0", "micromark-util-character": "^1.0.0", @@ -3736,6 +4052,8 @@ }, "node_modules/micromark-factory-whitespace": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.0.0.tgz", + "integrity": "sha512-Qx7uEyahU1lt1RnsECBiuEbfr9INjQTGa6Err+gF3g0Tx4YEviPbqqGKNv/NrBaE7dVHdn1bVZKM/n5I/Bak7A==", "dev": true, "funding": [ { @@ -3747,7 +4065,6 @@ "url": "https://opencollective.com/unified" } ], - "license": "MIT", "dependencies": { "micromark-factory-space": "^1.0.0", "micromark-util-character": "^1.0.0", @@ -3757,6 +4074,8 @@ }, "node_modules/micromark-util-character": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.1.0.tgz", + "integrity": "sha512-agJ5B3unGNJ9rJvADMJ5ZiYjBRyDpzKAOk01Kpi1TKhlT1APx3XZk6eN7RtSz1erbWHC2L8T3xLZ81wdtGRZzg==", "dev": true, "funding": [ { @@ -3768,7 +4087,6 @@ "url": "https://opencollective.com/unified" } ], - "license": "MIT", "dependencies": { "micromark-util-symbol": "^1.0.0", "micromark-util-types": "^1.0.0" @@ -3776,6 +4094,8 @@ }, "node_modules/micromark-util-chunked": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.0.0.tgz", + "integrity": "sha512-5e8xTis5tEZKgesfbQMKRCyzvffRRUX+lK/y+DvsMFdabAicPkkZV6gO+FEWi9RfuKKoxxPwNL+dFF0SMImc1g==", "dev": true, "funding": [ { @@ -3787,13 +4107,14 @@ "url": "https://opencollective.com/unified" } ], - "license": "MIT", "dependencies": { "micromark-util-symbol": "^1.0.0" } }, "node_modules/micromark-util-classify-character": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.0.0.tgz", + "integrity": "sha512-F8oW2KKrQRb3vS5ud5HIqBVkCqQi224Nm55o5wYLzY/9PwHGXC01tr3d7+TqHHz6zrKQ72Okwtvm/xQm6OVNZA==", "dev": true, "funding": [ { @@ -3805,7 +4126,6 @@ "url": "https://opencollective.com/unified" } ], - "license": "MIT", "dependencies": { "micromark-util-character": "^1.0.0", "micromark-util-symbol": "^1.0.0", @@ -3814,6 +4134,8 @@ }, "node_modules/micromark-util-combine-extensions": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.0.0.tgz", + "integrity": "sha512-J8H058vFBdo/6+AsjHp2NF7AJ02SZtWaVUjsayNFeAiydTxUwViQPxN0Hf8dp4FmCQi0UUFovFsEyRSUmFH3MA==", "dev": true, "funding": [ { @@ -3825,7 +4147,6 @@ "url": "https://opencollective.com/unified" } ], - "license": "MIT", "dependencies": { "micromark-util-chunked": "^1.0.0", "micromark-util-types": "^1.0.0" @@ -3833,6 +4154,8 @@ }, "node_modules/micromark-util-decode-numeric-character-reference": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.0.0.tgz", + "integrity": "sha512-OzO9AI5VUtrTD7KSdagf4MWgHMtET17Ua1fIpXTpuhclCqD8egFWo85GxSGvxgkGS74bEahvtM0WP0HjvV0e4w==", "dev": true, "funding": [ { @@ -3844,13 +4167,14 @@ "url": "https://opencollective.com/unified" } ], - "license": "MIT", "dependencies": { "micromark-util-symbol": "^1.0.0" } }, "node_modules/micromark-util-decode-string": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.0.2.tgz", + "integrity": "sha512-DLT5Ho02qr6QWVNYbRZ3RYOSSWWFuH3tJexd3dgN1odEuPNxCngTCXJum7+ViRAd9BbdxCvMToPOD/IvVhzG6Q==", "dev": true, "funding": [ { @@ -3862,7 +4186,6 @@ "url": "https://opencollective.com/unified" } ], - "license": "MIT", "dependencies": { "decode-named-character-reference": "^1.0.0", "micromark-util-character": "^1.0.0", @@ -3872,6 +4195,8 @@ }, "node_modules/micromark-util-encode": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.0.1.tgz", + "integrity": "sha512-U2s5YdnAYexjKDel31SVMPbfi+eF8y1U4pfiRW/Y8EFVCy/vgxk/2wWTxzcqE71LHtCuCzlBDRU2a5CQ5j+mQA==", "dev": true, "funding": [ { @@ -3882,11 +4207,12 @@ "type": "OpenCollective", "url": "https://opencollective.com/unified" } - ], - "license": "MIT" + ] }, "node_modules/micromark-util-html-tag-name": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.1.0.tgz", + "integrity": "sha512-BKlClMmYROy9UiV03SwNmckkjn8QHVaWkqoAqzivabvdGcwNGMMMH/5szAnywmsTBUzDsU57/mFi0sp4BQO6dA==", "dev": true, "funding": [ { @@ -3897,11 +4223,12 @@ "type": "OpenCollective", "url": "https://opencollective.com/unified" } - ], - "license": "MIT" + ] }, "node_modules/micromark-util-normalize-identifier": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.0.0.tgz", + "integrity": "sha512-yg+zrL14bBTFrQ7n35CmByWUTFsgst5JhA4gJYoty4Dqzj4Z4Fr/DHekSS5aLfH9bdlfnSvKAWsAgJhIbogyBg==", "dev": true, "funding": [ { @@ -3913,13 +4240,14 @@ "url": "https://opencollective.com/unified" } ], - "license": "MIT", "dependencies": { "micromark-util-symbol": "^1.0.0" } }, "node_modules/micromark-util-resolve-all": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.0.0.tgz", + "integrity": "sha512-CB/AGk98u50k42kvgaMM94wzBqozSzDDaonKU7P7jwQIuH2RU0TeBqGYJz2WY1UdihhjweivStrJ2JdkdEmcfw==", "dev": true, "funding": [ { @@ -3931,13 +4259,14 @@ "url": "https://opencollective.com/unified" } ], - "license": "MIT", "dependencies": { "micromark-util-types": "^1.0.0" } }, "node_modules/micromark-util-sanitize-uri": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.0.0.tgz", + "integrity": "sha512-cCxvBKlmac4rxCGx6ejlIviRaMKZc0fWm5HdCHEeDWRSkn44l6NdYVRyU+0nT1XC72EQJMZV8IPHF+jTr56lAg==", "dev": true, "funding": [ { @@ -3949,7 +4278,6 @@ "url": "https://opencollective.com/unified" } ], - "license": "MIT", "dependencies": { "micromark-util-character": "^1.0.0", "micromark-util-encode": "^1.0.0", @@ -3958,6 +4286,8 @@ }, "node_modules/micromark-util-subtokenize": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.0.2.tgz", + "integrity": "sha512-d90uqCnXp/cy4G881Ub4psE57Sf8YD0pim9QdjCRNjfas2M1u6Lbt+XZK9gnHL2XFhnozZiEdCa9CNfXSfQ6xA==", "dev": true, "funding": [ { @@ -3969,7 +4299,6 @@ "url": "https://opencollective.com/unified" } ], - "license": "MIT", "dependencies": { "micromark-util-chunked": "^1.0.0", "micromark-util-symbol": "^1.0.0", @@ -3979,6 +4308,8 @@ }, "node_modules/micromark-util-symbol": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.0.1.tgz", + "integrity": "sha512-oKDEMK2u5qqAptasDAwWDXq0tG9AssVwAx3E9bBF3t/shRIGsWIRG+cGafs2p/SnDSOecnt6hZPCE2o6lHfFmQ==", "dev": true, "funding": [ { @@ -3989,11 +4320,12 @@ "type": "OpenCollective", "url": "https://opencollective.com/unified" } - ], - "license": "MIT" + ] }, "node_modules/micromark-util-types": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.0.2.tgz", + "integrity": "sha512-DCfg/T8fcrhrRKTPjRrw/5LLvdGV7BHySf/1LOZx7TzWZdYRjogNtyNq885z3nNallwr3QUKARjqvHqX1/7t+w==", "dev": true, "funding": [ { @@ -4004,13 +4336,13 @@ "type": "OpenCollective", "url": "https://opencollective.com/unified" } - ], - "license": "MIT" + ] }, "node_modules/micromatch": { "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, - "license": "MIT", "dependencies": { "braces": "^3.0.2", "picomatch": "^2.3.1" @@ -4021,8 +4353,9 @@ }, "node_modules/mime": { "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "dev": true, - "license": "MIT", "bin": { "mime": "cli.js" }, @@ -4032,14 +4365,16 @@ }, "node_modules/mime-db": { "version": "1.52.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { "version": "2.1.35", - "license": "MIT", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dependencies": { "mime-db": "1.52.0" }, @@ -4049,16 +4384,18 @@ }, "node_modules/mimic-fn": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/minimatch": { "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -4068,13 +4405,15 @@ }, "node_modules/minimist": { "version": "1.2.6", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true }, "node_modules/minipass": { "version": "3.3.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.4.tgz", + "integrity": "sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw==", "dev": true, - "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, @@ -4084,8 +4423,9 @@ }, "node_modules/minizlib": { "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", "dev": true, - "license": "MIT", "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" @@ -4096,8 +4436,9 @@ }, "node_modules/mkdirp": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true, - "license": "MIT", "bin": { "mkdirp": "bin/cmd.js" }, @@ -4107,13 +4448,15 @@ }, "node_modules/mkdirp2": { "version": "1.0.5", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/mkdirp2/-/mkdirp2-1.0.5.tgz", + "integrity": "sha512-xOE9xbICroUDmG1ye2h4bZ8WBie9EGmACaco8K8cx6RlkJJrxGIqjGqztAI+NMhexXBcdGbSEzI6N3EJPevxZw==", + "dev": true }, "node_modules/mocha": { "version": "9.2.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", + "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", "dev": true, - "license": "MIT", "dependencies": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", @@ -4154,16 +4497,18 @@ }, "node_modules/mocha/node_modules/ansi-colors": { "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/mocha/node_modules/debug": { "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "dev": true, - "license": "MIT", "dependencies": { "ms": "2.1.2" }, @@ -4178,13 +4523,15 @@ }, "node_modules/mocha/node_modules/debug/node_modules/ms": { "version": "2.1.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true }, "node_modules/mocha/node_modules/escape-string-regexp": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -4194,8 +4541,9 @@ }, "node_modules/mocha/node_modules/glob": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, - "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -4213,8 +4561,9 @@ }, "node_modules/mocha/node_modules/glob/node_modules/minimatch": { "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -4224,8 +4573,9 @@ }, "node_modules/mocha/node_modules/minimatch": { "version": "4.2.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", + "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -4235,21 +4585,24 @@ }, "node_modules/mocha/node_modules/ms": { "version": "2.1.3", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true }, "node_modules/mocha/node_modules/serialize-javascript": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "randombytes": "^2.1.0" } }, "node_modules/mocha/node_modules/yargs": { "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, - "license": "MIT", "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", @@ -4265,21 +4618,24 @@ }, "node_modules/mri": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/ms": { "version": "2.1.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true }, "node_modules/mylas": { "version": "2.1.11", + "resolved": "https://registry.npmjs.org/mylas/-/mylas-2.1.11.tgz", + "integrity": "sha512-krnPUl3n9/k52FGCltWMYcqp9SttxjRJEy0sWLk+g7mIa7wnZrmNSZ40Acx7ghzRSOsxt2rEqMbaq4jWlnTDKg==", "dev": true, - "license": "MIT", "engines": { "node": ">=12.0.0" }, @@ -4290,8 +4646,9 @@ }, "node_modules/nanoid": { "version": "3.3.1", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", + "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", "dev": true, - "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -4301,12 +4658,14 @@ }, "node_modules/neo-async": { "version": "2.6.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true }, "node_modules/node-fetch": { "version": "2.6.7", - "license": "MIT", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", "dependencies": { "whatwg-url": "^5.0.0" }, @@ -4324,21 +4683,24 @@ }, "node_modules/node-releases": { "version": "2.0.6", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "dev": true }, "node_modules/normalize-path": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/npm-run-path": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, - "license": "MIT", "dependencies": { "path-key": "^3.0.0" }, @@ -4348,29 +4710,33 @@ }, "node_modules/object-get": { "version": "2.1.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/object-get/-/object-get-2.1.1.tgz", + "integrity": "sha512-7n4IpLMzGGcLEMiQKsNR7vCe+N5E9LORFrtNUVy4sO3dj9a3HedZCxEL2T7QuLhcHN1NBuBsMOKaOsAYI9IIvg==", + "dev": true }, "node_modules/object-to-spawn-args": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object-to-spawn-args/-/object-to-spawn-args-2.0.1.tgz", + "integrity": "sha512-6FuKFQ39cOID+BMZ3QaphcC8Y4cw6LXBLyIgPU+OhIYwviJamPAn+4mITapnSBQrejB+NNp+FMskhD8Cq+Ys3w==", "dev": true, - "license": "MIT", "engines": { "node": ">=8.0.0" } }, "node_modules/once": { "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, - "license": "ISC", "dependencies": { "wrappy": "1" } }, "node_modules/onetime": { "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, - "license": "MIT", "dependencies": { "mimic-fn": "^2.1.0" }, @@ -4383,21 +4749,24 @@ }, "node_modules/opener": { "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", "dev": true, - "license": "(WTFPL OR MIT)", "bin": { "opener": "bin/opener-bin.js" } }, "node_modules/ospath": { "version": "1.2.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz", + "integrity": "sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==", + "dev": true }, "node_modules/p-limit": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, - "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" }, @@ -4410,8 +4779,9 @@ }, "node_modules/p-locate": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, - "license": "MIT", "dependencies": { "p-limit": "^3.0.2" }, @@ -4424,8 +4794,9 @@ }, "node_modules/p-map": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", "dev": true, - "license": "MIT", "dependencies": { "aggregate-error": "^3.0.0" }, @@ -4438,68 +4809,78 @@ }, "node_modules/p-try": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/path-exists": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/path-is-absolute": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/path-key": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/path-parse": { "version": "1.0.7", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true }, "node_modules/path-type": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/pend": { "version": "1.2.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true }, "node_modules/performance-now": { "version": "2.1.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "dev": true }, "node_modules/picocolors": { "version": "1.0.0", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true }, "node_modules/picomatch": { "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, - "license": "MIT", "engines": { "node": ">=8.6" }, @@ -4509,16 +4890,18 @@ }, "node_modules/pify": { "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/pkg-dir": { "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, - "license": "MIT", "dependencies": { "find-up": "^4.0.0" }, @@ -4528,8 +4911,9 @@ }, "node_modules/pkg-dir/node_modules/find-up": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, - "license": "MIT", "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -4540,8 +4924,9 @@ }, "node_modules/pkg-dir/node_modules/locate-path": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, - "license": "MIT", "dependencies": { "p-locate": "^4.1.0" }, @@ -4551,8 +4936,9 @@ }, "node_modules/pkg-dir/node_modules/p-limit": { "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, - "license": "MIT", "dependencies": { "p-try": "^2.0.0" }, @@ -4565,8 +4951,9 @@ }, "node_modules/pkg-dir/node_modules/p-locate": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, - "license": "MIT", "dependencies": { "p-limit": "^2.2.0" }, @@ -4575,17 +4962,19 @@ } }, "node_modules/plimit-lit": { - "version": "1.3.0", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/plimit-lit/-/plimit-lit-1.4.0.tgz", + "integrity": "sha512-e4VKIdzc5+vvTuNZeWDTWHCNPLZ1+jXeQ9HqZhAS+vlAodNr8A26IcTA9OudjLXj8fV+KRW/ZzsoyrTZzoWD/A==", "dev": true, - "license": "MIT", "dependencies": { - "queue-lit": "^1.3.0" + "queue-lit": "^1.4.0" } }, "node_modules/portfinder": { - "version": "1.0.29", + "version": "1.0.32", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz", + "integrity": "sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==", "dev": true, - "license": "MIT", "dependencies": { "async": "^2.6.4", "debug": "^3.2.7", @@ -4597,24 +4986,27 @@ }, "node_modules/portfinder/node_modules/async": { "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", "dev": true, - "license": "MIT", "dependencies": { "lodash": "^4.17.14" } }, "node_modules/portfinder/node_modules/debug": { "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, - "license": "MIT", "dependencies": { "ms": "^2.1.1" } }, "node_modules/portfinder/node_modules/mkdirp": { "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, - "license": "MIT", "dependencies": { "minimist": "^1.2.6" }, @@ -4624,8 +5016,9 @@ }, "node_modules/pretty-bytes": { "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" }, @@ -4635,18 +5028,21 @@ }, "node_modules/proxy-from-env": { "version": "1.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", + "integrity": "sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==", + "dev": true }, "node_modules/psl": { "version": "1.9.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", + "dev": true }, "node_modules/pump": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, - "license": "MIT", "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -4654,27 +5050,32 @@ }, "node_modules/punycode": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/qs": { "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", "dev": true, - "license": "BSD-3-Clause", "engines": { "node": ">=0.6" } }, "node_modules/queue-lit": { - "version": "1.3.0", - "dev": true, - "license": "MIT" + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/queue-lit/-/queue-lit-1.4.0.tgz", + "integrity": "sha512-l1+4YHm4vHWpCnvTg8JMsnPETmPvLGWhqjvNOc8TSbqscGplHVSWXOxybA3vYeMNNIR9Z1PQt85U+S3wFJX2uQ==", + "dev": true }, "node_modules/queue-microtask": { "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true, "funding": [ { @@ -4689,21 +5090,22 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "MIT" + ] }, "node_modules/randombytes": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, - "license": "MIT", "dependencies": { "safe-buffer": "^5.1.0" } }, "node_modules/readdirp": { "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, - "license": "MIT", "dependencies": { "picomatch": "^2.2.1" }, @@ -4713,8 +5115,9 @@ }, "node_modules/rechoir": { "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", + "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", "dev": true, - "license": "MIT", "dependencies": { "resolve": "^1.9.0" }, @@ -4724,8 +5127,9 @@ }, "node_modules/reduce-extract": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/reduce-extract/-/reduce-extract-1.0.0.tgz", + "integrity": "sha512-QF8vjWx3wnRSL5uFMyCjDeDc5EBMiryoT9tz94VvgjKfzecHAVnqmXAwQDcr7X4JmLc2cjkjFGCVzhMqDjgR9g==", "dev": true, - "license": "MIT", "dependencies": { "test-value": "^1.0.1" }, @@ -4735,8 +5139,9 @@ }, "node_modules/reduce-extract/node_modules/array-back": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz", + "integrity": "sha512-1WxbZvrmyhkNoeYcizokbmh5oiOCIfyvGtcqbK3Ls1v1fKcquzxnQSceOx6tzq7jmai2kFLWIpGND2cLhH6TPw==", "dev": true, - "license": "MIT", "dependencies": { "typical": "^2.6.0" }, @@ -4746,8 +5151,9 @@ }, "node_modules/reduce-extract/node_modules/test-value": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/test-value/-/test-value-1.1.0.tgz", + "integrity": "sha512-wrsbRo7qP+2Je8x8DsK8ovCGyxe3sYfQwOraIY/09A2gFXU9DYKiTF14W4ki/01AEh56kMzAmlj9CaHGDDUBJA==", "dev": true, - "license": "MIT", "dependencies": { "array-back": "^1.0.2", "typical": "^2.4.2" @@ -4758,24 +5164,27 @@ }, "node_modules/reduce-flatten": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-3.0.1.tgz", + "integrity": "sha512-bYo+97BmUUOzg09XwfkwALt4PQH1M5L0wzKerBt6WLm3Fhdd43mMS89HiT1B9pJIqko/6lWx3OnV4J9f2Kqp5Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/reduce-unique": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/reduce-unique/-/reduce-unique-2.0.1.tgz", + "integrity": "sha512-x4jH/8L1eyZGR785WY+ePtyMNhycl1N2XOLxhCbzZFaqF4AXjLzqSxa2UHgJ2ZVR/HHyPOvl1L7xRnW8ye5MdA==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/reduce-without": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/reduce-without/-/reduce-without-1.0.1.tgz", + "integrity": "sha512-zQv5y/cf85sxvdrKPlfcRzlDn/OqKFThNimYmsS3flmkioKvkUGn2Qg9cJVoQiEvdxFGLE0MQER/9fZ9sUqdxg==", "dev": true, - "license": "MIT", "dependencies": { "test-value": "^2.0.0" }, @@ -4785,8 +5194,9 @@ }, "node_modules/reduce-without/node_modules/array-back": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz", + "integrity": "sha512-1WxbZvrmyhkNoeYcizokbmh5oiOCIfyvGtcqbK3Ls1v1fKcquzxnQSceOx6tzq7jmai2kFLWIpGND2cLhH6TPw==", "dev": true, - "license": "MIT", "dependencies": { "typical": "^2.6.0" }, @@ -4796,8 +5206,9 @@ }, "node_modules/reduce-without/node_modules/test-value": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/test-value/-/test-value-2.1.0.tgz", + "integrity": "sha512-+1epbAxtKeXttkGFMTX9H42oqzOTufR1ceCF+GYA5aOmvaPq9wd4PUS8329fn2RRLGNeUkgRLnVpycjx8DsO2w==", "dev": true, - "license": "MIT", "dependencies": { "array-back": "^1.0.3", "typical": "^2.6.0" @@ -4808,8 +5219,9 @@ }, "node_modules/remark-parse": { "version": "10.0.1", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-10.0.1.tgz", + "integrity": "sha512-1fUyHr2jLsVOkhbvPRBJ5zTKZZyD6yZzYaWCS6BPBdQ8vEMBCH+9zNCDA6tET/zHCi/jLqjCWtlJZUPk+DbnFw==", "dev": true, - "license": "MIT", "dependencies": { "@types/mdast": "^3.0.0", "mdast-util-from-markdown": "^1.0.0", @@ -4822,42 +5234,48 @@ }, "node_modules/request-progress": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", + "integrity": "sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==", "dev": true, - "license": "MIT", "dependencies": { "throttleit": "^1.0.0" } }, "node_modules/require-directory": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/require-main-filename": { "version": "2.0.0", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true }, "node_modules/requires-port": { "version": "1.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true }, "node_modules/requizzle": { "version": "0.2.3", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.3.tgz", + "integrity": "sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==", "dev": true, - "license": "MIT", "dependencies": { "lodash": "^4.17.14" } }, "node_modules/resolve": { "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", "dev": true, - "license": "MIT", "dependencies": { "is-core-module": "^2.9.0", "path-parse": "^1.0.7", @@ -4872,8 +5290,9 @@ }, "node_modules/resolve-cwd": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, - "license": "MIT", "dependencies": { "resolve-from": "^5.0.0" }, @@ -4883,16 +5302,18 @@ }, "node_modules/resolve-from": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/restore-cursor": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "dev": true, - "license": "MIT", "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" @@ -4903,8 +5324,9 @@ }, "node_modules/reusify": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true, - "license": "MIT", "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -4912,13 +5334,15 @@ }, "node_modules/rfdc": { "version": "1.3.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", + "dev": true }, "node_modules/rimraf": { "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, - "license": "ISC", "dependencies": { "glob": "^7.1.3" }, @@ -4931,6 +5355,8 @@ }, "node_modules/run-parallel": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, "funding": [ { @@ -4946,23 +5372,24 @@ "url": "https://feross.org/support" } ], - "license": "MIT", "dependencies": { "queue-microtask": "^1.2.2" } }, "node_modules/rxjs": { "version": "7.5.6", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.6.tgz", + "integrity": "sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw==", "dev": true, - "license": "Apache-2.0", "dependencies": { "tslib": "^2.1.0" } }, "node_modules/sade": { "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", "dev": true, - "license": "MIT", "dependencies": { "mri": "^1.1.0" }, @@ -4972,6 +5399,8 @@ }, "node_modules/safe-buffer": { "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true, "funding": [ { @@ -4986,18 +5415,19 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "MIT" + ] }, "node_modules/safer-buffer": { "version": "2.1.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true }, "node_modules/schema-utils": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", "dev": true, - "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", @@ -5013,13 +5443,15 @@ }, "node_modules/secure-compare": { "version": "3.0.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", + "integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==", + "dev": true }, "node_modules/semver": { "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, - "license": "ISC", "dependencies": { "lru-cache": "^6.0.0" }, @@ -5032,21 +5464,24 @@ }, "node_modules/serialize-javascript": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", + "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "randombytes": "^2.1.0" } }, "node_modules/set-blocking": { "version": "2.0.0", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true }, "node_modules/shallow-clone": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", "dev": true, - "license": "MIT", "dependencies": { "kind-of": "^6.0.2" }, @@ -5056,8 +5491,9 @@ }, "node_modules/shebang-command": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, - "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, @@ -5067,34 +5503,39 @@ }, "node_modules/shebang-regex": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/shell-quote": { "version": "1.7.3", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz", + "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==", + "dev": true }, "node_modules/signal-exit": { "version": "3.0.7", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true }, "node_modules/slash": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/slice-ansi": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", @@ -5106,8 +5547,9 @@ }, "node_modules/sort-array": { "version": "4.1.5", + "resolved": "https://registry.npmjs.org/sort-array/-/sort-array-4.1.5.tgz", + "integrity": "sha512-Ya4peoS1fgFN42RN1REk2FgdNOeLIEMKFGJvs7VTP3OklF8+kl2SkpVliZ4tk/PurWsrWRsdNdU+tgyOBkB9sA==", "dev": true, - "license": "MIT", "dependencies": { "array-back": "^5.0.0", "typical": "^6.0.1" @@ -5118,32 +5560,36 @@ }, "node_modules/sort-array/node_modules/array-back": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-5.0.0.tgz", + "integrity": "sha512-kgVWwJReZWmVuWOQKEOohXKJX+nD02JAZ54D1RRWlv8L0NebauKAaFxACKzB74RTclt1+WNz5KHaLRDAPZbDEw==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" } }, "node_modules/sort-array/node_modules/typical": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/typical/-/typical-6.0.1.tgz", + "integrity": "sha512-+g3NEp7fJLe9DPa1TArHm9QAA7YciZmWnfAqEaFrBihQ7epOv9i99rjtgb6Iz0wh3WuQDjsCTDfgRoGnmHN81A==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" } }, "node_modules/source-map": { "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, - "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, "node_modules/source-map-support": { "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, - "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -5151,13 +5597,15 @@ }, "node_modules/spawn-command": { "version": "0.0.2-1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz", + "integrity": "sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg==", + "dev": true }, "node_modules/sshpk": { "version": "1.17.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", + "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", "dev": true, - "license": "MIT", "dependencies": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", @@ -5180,8 +5628,9 @@ }, "node_modules/stream-connect": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stream-connect/-/stream-connect-1.0.2.tgz", + "integrity": "sha512-68Kl+79cE0RGKemKkhxTSg8+6AGrqBt+cbZAXevg2iJ6Y3zX4JhA/sZeGzLpxW9cXhmqAcE7KnJCisUmIUfnFQ==", "dev": true, - "license": "MIT", "dependencies": { "array-back": "^1.0.2" }, @@ -5191,8 +5640,9 @@ }, "node_modules/stream-connect/node_modules/array-back": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz", + "integrity": "sha512-1WxbZvrmyhkNoeYcizokbmh5oiOCIfyvGtcqbK3Ls1v1fKcquzxnQSceOx6tzq7jmai2kFLWIpGND2cLhH6TPw==", "dev": true, - "license": "MIT", "dependencies": { "typical": "^2.6.0" }, @@ -5202,16 +5652,18 @@ }, "node_modules/stream-via": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/stream-via/-/stream-via-1.0.4.tgz", + "integrity": "sha512-DBp0lSvX5G9KGRDTkR/R+a29H+Wk2xItOF+MpZLLNDWbEV9tGPnqLPxHEYjmiz8xGtJHRIqmI+hCjmNzqoA4nQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/string-width": { "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, - "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -5221,40 +5673,23 @@ "node": ">=8" } }, - "node_modules/string-width/node_modules/ansi-regex": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width/node_modules/strip-ansi": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/strip-ansi": { - "version": "4.0.0", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, - "license": "MIT", "dependencies": { - "ansi-regex": "^3.0.0" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/strip-bom": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, - "license": "MIT", "optional": true, "engines": { "node": ">=4" @@ -5262,16 +5697,18 @@ }, "node_modules/strip-final-newline": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/strip-json-comments": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" }, @@ -5281,8 +5718,9 @@ }, "node_modules/supports-color": { "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -5295,8 +5733,9 @@ }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -5306,8 +5745,9 @@ }, "node_modules/table-layout": { "version": "0.4.5", + "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-0.4.5.tgz", + "integrity": "sha512-zTvf0mcggrGeTe/2jJ6ECkJHAQPIYEwDoqsiqBjI24mvRmQbInK5jq33fyypaCBxX08hMkfmdOqj6haT33EqWw==", "dev": true, - "license": "MIT", "dependencies": { "array-back": "^2.0.0", "deep-extend": "~0.6.0", @@ -5321,8 +5761,9 @@ }, "node_modules/table-layout/node_modules/array-back": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", + "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", "dev": true, - "license": "MIT", "dependencies": { "typical": "^2.6.1" }, @@ -5332,20 +5773,24 @@ }, "node_modules/taffydb": { "version": "2.6.2", + "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", + "integrity": "sha512-y3JaeRSplks6NYQuCOj3ZFMO3j60rTwbuKCvZxsAraGYH2epusatvZ0baZYA01WsGqJBq/Dl6vOrMUJqyMj8kA==", "dev": true }, "node_modules/tapable": { "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/tar": { "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", "dev": true, - "license": "ISC", "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", @@ -5358,23 +5803,17 @@ "node": ">= 10" } }, - "node_modules/tar/node_modules/chownr": { - "version": "2.0.0", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, "node_modules/temp-path": { "version": "1.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/temp-path/-/temp-path-1.0.0.tgz", + "integrity": "sha512-TvmyH7kC6ZVTYkqCODjJIbgvu0FKiwQpZ4D1aknE7xpcDf/qEOB8KZEK5ef2pfbVoiBhNWs3yx4y+ESMtNYmlg==", + "dev": true }, "node_modules/terser": { - "version": "5.14.2", + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.15.0.tgz", + "integrity": "sha512-L1BJiXVmheAQQy+as0oF3Pwtlo4s3Wi1X2zNZ2NxOB4wx9bdS9Vk67XQENLFdLYGCK/Z2di53mTj/hBafR+dTA==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.2", "acorn": "^8.5.0", @@ -5389,15 +5828,16 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.3", + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz", + "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==", "dev": true, - "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.7", + "@jridgewell/trace-mapping": "^0.3.14", "jest-worker": "^27.4.5", "schema-utils": "^3.1.1", "serialize-javascript": "^6.0.0", - "terser": "^5.7.2" + "terser": "^5.14.1" }, "engines": { "node": ">= 10.13.0" @@ -5421,23 +5861,36 @@ } } }, + "node_modules/terser-webpack-plugin/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.15", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", + "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/terser-webpack-plugin/node_modules/serialize-javascript": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "randombytes": "^2.1.0" } }, "node_modules/terser/node_modules/commander": { "version": "2.20.3", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true }, "node_modules/test-value": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/test-value/-/test-value-3.0.0.tgz", + "integrity": "sha512-sVACdAWcZkSU9x7AOmJo5TqE+GyNJknHaHsMrR6ZnhjVlVN9Yx6FjHrsKZ3BjIpPCT68zYesPWkakrNupwfOTQ==", "dev": true, - "license": "MIT", "dependencies": { "array-back": "^2.0.0", "typical": "^2.6.1" @@ -5448,8 +5901,9 @@ }, "node_modules/test-value/node_modules/array-back": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", + "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", "dev": true, - "license": "MIT", "dependencies": { "typical": "^2.6.1" }, @@ -5459,23 +5913,28 @@ }, "node_modules/text-encoding": { "version": "0.7.0", - "license": "(Unlicense OR Apache-2.0)", + "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.7.0.tgz", + "integrity": "sha512-oJQ3f1hrOnbRLOcwKz0Liq2IcrvDeZRHXhd9RgLrsT+DjWY/nty1Hi7v3dtkaEYbPYe0mUoOfzRrMwfXXwgPUA==", + "deprecated": "no longer maintained", "peer": true }, "node_modules/throttleit": { "version": "1.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz", + "integrity": "sha512-rkTVqu6IjfQ/6+uNuuc3sZek4CEYxTJom3IktzgdSxcZqdARuebbA/f4QmAxMQIxqq9ZLEUkSYqvuk1I6VKq4g==", + "dev": true }, "node_modules/through": { "version": "2.3.8", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true }, "node_modules/tmp": { "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", "dev": true, - "license": "MIT", "dependencies": { "rimraf": "^3.0.0" }, @@ -5485,8 +5944,9 @@ }, "node_modules/to-regex-range": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, - "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -5496,8 +5956,9 @@ }, "node_modules/tough-cookie": { "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "psl": "^1.1.28", "punycode": "^2.1.1" @@ -5508,20 +5969,23 @@ }, "node_modules/tr46": { "version": "0.0.3", - "license": "MIT" + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, "node_modules/tree-kill": { "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", "dev": true, - "license": "MIT", "bin": { "tree-kill": "cli.js" } }, "node_modules/trough": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.1.0.tgz", + "integrity": "sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==", "dev": true, - "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -5529,8 +5993,9 @@ }, "node_modules/ts-mocha": { "version": "9.0.2", + "resolved": "https://registry.npmjs.org/ts-mocha/-/ts-mocha-9.0.2.tgz", + "integrity": "sha512-WyQjvnzwrrubl0JT7EC1yWmNpcsU3fOuBFfdps30zbmFBgKniSaSOyZMZx+Wq7kytUs5CY+pEbSYEbGfIKnXTw==", "dev": true, - "license": "MIT", "dependencies": { "ts-node": "7.0.1" }, @@ -5549,28 +6014,18 @@ }, "node_modules/ts-mocha/node_modules/diff": { "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", "dev": true, - "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } }, - "node_modules/ts-mocha/node_modules/json5": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, "node_modules/ts-mocha/node_modules/mkdirp": { "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, - "license": "MIT", "dependencies": { "minimist": "^1.2.6" }, @@ -5580,8 +6035,9 @@ }, "node_modules/ts-mocha/node_modules/ts-node": { "version": "7.0.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz", + "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==", "dev": true, - "license": "MIT", "dependencies": { "arrify": "^1.0.0", "buffer-from": "^1.1.0", @@ -5599,30 +6055,20 @@ "node": ">=4.2.0" } }, - "node_modules/ts-mocha/node_modules/tsconfig-paths": { - "version": "3.14.1", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, "node_modules/ts-mocha/node_modules/yn": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", + "integrity": "sha512-uTv8J/wiWTgUTg+9vLTi//leUl5vDQS6uii/emeTb2ssY7vl6QWf2fFbIIGjnhjvbdKlU0ed7QPgY1htTC86jQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/ts-node": { "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", "dev": true, - "license": "MIT", "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -5663,16 +6109,18 @@ }, "node_modules/ts-node/node_modules/diff": { "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true, - "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } }, "node_modules/tsc-alias": { "version": "1.7.0", + "resolved": "https://registry.npmjs.org/tsc-alias/-/tsc-alias-1.7.0.tgz", + "integrity": "sha512-n/K6g8S7Ec7Y/A2Z77Ikp2Uv1S1ERtT63ni69XV4W1YPT4rnNmz8ItgIiJYvKfFnKfqcZQ81UPjoKpMTxaC/rg==", "dev": true, - "license": "MIT", "dependencies": { "chokidar": "^3.5.3", "commander": "^9.0.0", @@ -5687,21 +6135,50 @@ }, "node_modules/tsc-alias/node_modules/commander": { "version": "9.4.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.0.tgz", + "integrity": "sha512-sRPT+umqkz90UA8M1yqYfnHlZA7fF6nSphDtxeywPZ49ysjxDQybzk13CL+mXekDRG92skbcqCLVovuCusNmFw==", "dev": true, - "license": "MIT", "engines": { "node": "^12.20.0 || >=14" } }, + "node_modules/tsconfig-paths": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "dev": true, + "optional": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "optional": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, "node_modules/tslib": { "version": "2.4.0", - "dev": true, - "license": "0BSD" + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true }, "node_modules/tunnel-agent": { "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", "dev": true, - "license": "Apache-2.0", "dependencies": { "safe-buffer": "^5.0.1" }, @@ -5711,13 +6188,15 @@ }, "node_modules/tweetnacl": { "version": "0.14.5", - "dev": true, - "license": "Unlicense" + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "dev": true }, "node_modules/txm": { "version": "8.0.2", + "resolved": "https://registry.npmjs.org/txm/-/txm-8.0.2.tgz", + "integrity": "sha512-X6gUOeUAXzIrUvxp0zvOiJBM0l6Zk8NexaYugac6c6PW7dbrjgz6zC/rfSEjtd29VejI3VKvAnc6pDdOfSNKug==", "dev": true, - "license": "ISC", "dependencies": { "async": "^3.2.1", "diff-match-patch": "^1.0.5", @@ -5734,9 +6213,10 @@ } }, "node_modules/txm/node_modules/supports-color": { - "version": "9.2.2", + "version": "9.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.2.3.tgz", + "integrity": "sha512-aszYUX/DVK/ed5rFLb/dDinVJrQjG/vmU433wtqVSD800rYsJNWxh2R3USV90aLSU+UsyQkbNeffVLzc6B6foA==", "dev": true, - "license": "MIT", "engines": { "node": ">=12" }, @@ -5746,8 +6226,9 @@ }, "node_modules/type-fest": { "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true, - "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -5756,9 +6237,10 @@ } }, "node_modules/typescript": { - "version": "4.7.4", + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.3.tgz", + "integrity": "sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==", "dev": true, - "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -5769,18 +6251,21 @@ }, "node_modules/typical": { "version": "2.6.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz", + "integrity": "sha512-ofhi8kjIje6npGozTip9Fr8iecmYfEbS06i0JnIg+rh51KakryWF4+jX8lLKZVhy6N+ID45WYSFCxPOdTWCzNg==", + "dev": true }, "node_modules/uc.micro": { "version": "1.0.6", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", + "dev": true }, "node_modules/uglify-js": { - "version": "3.16.3", + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.0.tgz", + "integrity": "sha512-aTeNPVmgIMPpm1cxXr2Q/nEbvkmV8yq66F3om7X3P/cvOXQ0TMQ64Wk63iyT1gPlmdmGzjGpyLh1f3y8MZWXGg==", "dev": true, - "license": "BSD-2-Clause", "optional": true, "bin": { "uglifyjs": "bin/uglifyjs" @@ -5791,13 +6276,15 @@ }, "node_modules/underscore": { "version": "1.13.4", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.4.tgz", + "integrity": "sha512-BQFnUDuAQ4Yf/cYY5LNrK9NCJFKriaRbD9uR1fTeXnBeoa97W0i41qkZfGO9pSo8I5KzjAcSY2XYtdf0oKd7KQ==", + "dev": true }, "node_modules/unified": { "version": "10.1.2", + "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz", + "integrity": "sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==", "dev": true, - "license": "MIT", "dependencies": { "@types/unist": "^2.0.0", "bail": "^2.0.0", @@ -5814,6 +6301,8 @@ }, "node_modules/union": { "version": "0.5.0", + "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", + "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==", "dev": true, "dependencies": { "qs": "^6.4.0" @@ -5824,8 +6313,9 @@ }, "node_modules/unist-util-stringify-position": { "version": "3.0.2", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.2.tgz", + "integrity": "sha512-7A6eiDCs9UtjcwZOcCpM4aPII3bAAGv13E96IkawkOAW0OhH+yRxtY0lzo8KiHpzEMfH7Q+FizUmwp8Iqy5EWg==", "dev": true, - "license": "MIT", "dependencies": { "@types/unist": "^2.0.0" }, @@ -5836,22 +6326,26 @@ }, "node_modules/universalify": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", "dev": true, - "license": "MIT", "engines": { "node": ">= 10.0.0" } }, "node_modules/untildify": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/update-browserslist-db": { - "version": "1.0.5", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz", + "integrity": "sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg==", "dev": true, "funding": [ { @@ -5863,7 +6357,6 @@ "url": "https://tidelift.com/funding/github/npm/browserslist" } ], - "license": "MIT", "dependencies": { "escalade": "^3.1.1", "picocolors": "^1.0.0" @@ -5877,29 +6370,33 @@ }, "node_modules/uri-js": { "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" } }, "node_modules/url-join": { "version": "4.0.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", + "dev": true }, "node_modules/uuid": { "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true, - "license": "MIT", "bin": { "uuid": "dist/bin/uuid" } }, "node_modules/uvu": { "version": "0.5.6", + "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz", + "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", "dev": true, - "license": "MIT", "dependencies": { "dequal": "^2.0.0", "diff": "^5.0.0", @@ -5915,16 +6412,18 @@ }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true }, "node_modules/verror": { "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", "dev": true, "engines": [ "node >=0.6.0" ], - "license": "MIT", "dependencies": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", @@ -5932,9 +6431,10 @@ } }, "node_modules/vfile": { - "version": "5.3.4", + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.5.tgz", + "integrity": "sha512-U1ho2ga33eZ8y8pkbQLH54uKqGhFJ6GYIHnnG5AhRpAh3OWjkrRHKa/KogbmQn8We+c0KVV3rTOgR9V/WowbXQ==", "dev": true, - "license": "MIT", "dependencies": { "@types/unist": "^2.0.0", "is-buffer": "^2.0.0", @@ -5948,8 +6448,9 @@ }, "node_modules/vfile-message": { "version": "3.1.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.2.tgz", + "integrity": "sha512-QjSNP6Yxzyycd4SVOtmKKyTsSvClqBPJcd00Z0zuPj3hOIjg0rUPG6DbFGPvUKRgYyaIWLPKpuEclcuvb3H8qA==", "dev": true, - "license": "MIT", "dependencies": { "@types/unist": "^2.0.0", "unist-util-stringify-position": "^3.0.0" @@ -5961,17 +6462,19 @@ }, "node_modules/walk-back": { "version": "5.1.0", + "resolved": "https://registry.npmjs.org/walk-back/-/walk-back-5.1.0.tgz", + "integrity": "sha512-Uhxps5yZcVNbLEAnb+xaEEMdgTXl9qAQDzKYejG2AZ7qPwRQ81lozY9ECDbjLPNWm7YsO1IK5rsP1KoQzXAcGA==", "dev": true, - "license": "MIT", "engines": { "node": ">=12.17" } }, "node_modules/wasm-opt": { "version": "1.3.0", + "resolved": "https://registry.npmjs.org/wasm-opt/-/wasm-opt-1.3.0.tgz", + "integrity": "sha512-24+IOboX4Sav0bI8Krwf0Y6dnpN4KxYtqpl0qWt86qVLsmayUqx1KMBrJTlQWNC+/dsqzQJjK6QvxNJsZYOgJg==", "dev": true, "hasInstallScript": true, - "license": "MIT", "dependencies": { "node-fetch": "^2.6.7", "tar": "^6.1.11" @@ -5982,9 +6485,10 @@ }, "node_modules/wasm-pack": { "version": "0.10.1", + "resolved": "https://registry.npmjs.org/wasm-pack/-/wasm-pack-0.10.1.tgz", + "integrity": "sha512-bw480KaaJQhL6UX8wAm6YCO497uaULDrsvUey3EB86dKAfFc6qCGVN1kItcwUklEeufonwo9mwz9/fy9xLOPtQ==", "dev": true, "hasInstallScript": true, - "license": "MIT OR Apache-2.0", "dependencies": { "binary-install": "^0.1.0" }, @@ -5994,8 +6498,9 @@ }, "node_modules/watchpack": { "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", "dev": true, - "license": "MIT", "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -6006,12 +6511,14 @@ }, "node_modules/webidl-conversions": { "version": "3.0.1", - "license": "BSD-2-Clause" + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "node_modules/webpack": { "version": "5.74.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz", + "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==", "dev": true, - "license": "MIT", "dependencies": { "@types/eslint-scope": "^3.7.3", "@types/estree": "^0.0.51", @@ -6056,8 +6563,9 @@ }, "node_modules/webpack-cli": { "version": "4.10.0", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", + "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", "dev": true, - "license": "MIT", "dependencies": { "@discoveryjs/json-ext": "^0.5.0", "@webpack-cli/configtest": "^1.2.0", @@ -6102,16 +6610,18 @@ }, "node_modules/webpack-cli/node_modules/commander": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", "dev": true, - "license": "MIT", "engines": { "node": ">= 10" } }, "node_modules/webpack-merge": { "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", "dev": true, - "license": "MIT", "dependencies": { "clone-deep": "^4.0.1", "wildcard": "^2.0.0" @@ -6122,16 +6632,18 @@ }, "node_modules/webpack-sources": { "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", "dev": true, - "license": "MIT", "engines": { "node": ">=10.13.0" } }, "node_modules/whatwg-encoding": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", "dev": true, - "license": "MIT", "dependencies": { "iconv-lite": "0.6.3" }, @@ -6141,7 +6653,8 @@ }, "node_modules/whatwg-url": { "version": "5.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -6149,8 +6662,9 @@ }, "node_modules/which": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, - "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -6163,29 +6677,42 @@ }, "node_modules/which-module": { "version": "2.0.0", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", + "dev": true }, "node_modules/wide-align": { "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dev": true, - "license": "ISC", "dependencies": { "string-width": "^1.0.2 || 2" } }, + "node_modules/wide-align/node_modules/ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/wide-align/node_modules/is-fullwidth-code-point": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/wide-align/node_modules/string-width": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, - "license": "MIT", "dependencies": { "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^4.0.0" @@ -6194,20 +6721,35 @@ "node": ">=4" } }, + "node_modules/wide-align/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "dev": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/wildcard": { "version": "2.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", + "dev": true }, "node_modules/wordwrap": { "version": "1.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true }, "node_modules/wordwrapjs": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-3.0.0.tgz", + "integrity": "sha512-mO8XtqyPvykVCsrwj5MlOVWvSnCdT+C+QVbm6blradR7JExAhbkZ7hZ9A+9NUtwzSqrlUo9a67ws0EiILrvRpw==", "dev": true, - "license": "MIT", "dependencies": { "reduce-flatten": "^1.0.1", "typical": "^2.6.1" @@ -6218,21 +6760,24 @@ }, "node_modules/wordwrapjs/node_modules/reduce-flatten": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-1.0.1.tgz", + "integrity": "sha512-j5WfFJfc9CoXv/WbwVLHq74i/hdTUpy+iNC534LxczMRP67vJeK3V9JOdnL0N1cIRbn9mYhE2yVjvvKXDxvNXQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/workerpool": { "version": "6.2.0", - "dev": true, - "license": "Apache-2.0" + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", + "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", + "dev": true }, "node_modules/wrap-ansi": { "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -6245,52 +6790,38 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/wrappy": { "version": "1.0.2", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true }, "node_modules/xmlcreate": { "version": "2.0.4", - "dev": true, - "license": "Apache-2.0" + "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", + "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", + "dev": true }, "node_modules/y18n": { "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, - "license": "ISC", "engines": { "node": ">=10" } }, "node_modules/yallist": { "version": "4.0.0", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true }, "node_modules/yargs": { "version": "17.5.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", + "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", "dev": true, - "license": "MIT", "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", @@ -6306,16 +6837,18 @@ }, "node_modules/yargs-parser": { "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", "dev": true, - "license": "ISC", "engines": { "node": ">=10" } }, "node_modules/yargs-unparser": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", "dev": true, - "license": "MIT", "dependencies": { "camelcase": "^6.0.0", "decamelize": "^4.0.0", @@ -6328,24 +6861,27 @@ }, "node_modules/yargs-unparser/node_modules/is-plain-obj": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/yargs/node_modules/yargs-parser": { "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, - "license": "ISC", "engines": { "node": ">=12" } }, "node_modules/yauzl": { "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", "dev": true, - "license": "MIT", "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" @@ -6353,16 +6889,18 @@ }, "node_modules/yn": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/yocto-queue": { "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -6373,16 +6911,22 @@ }, "dependencies": { "@babel/parser": { - "version": "7.18.11", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.1.tgz", + "integrity": "sha512-h7RCSorm1DdTVGJf3P2Mhj3kdnkmF/EiysUkzS2TdgAYqyjFdMQJbVuXOBej2SBJaXan/lIVtT6KkGbyyq753A==", "dev": true }, "@colors/colors": { "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", "dev": true, "optional": true }, "@cspotcode/source-map-support": { "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, "requires": { "@jridgewell/trace-mapping": "0.3.9" @@ -6390,6 +6934,8 @@ }, "@cypress/request": { "version": "2.88.10", + "resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.10.tgz", + "integrity": "sha512-Zp7F+R93N0yZyG34GutyTNr+okam7s/Fzc1+i3kcqOP8vk6OuajuE9qZJ6Rs+10/1JFtXFYMdyarnU1rZuJesg==", "dev": true, "requires": { "aws-sign2": "~0.7.0", @@ -6414,6 +6960,8 @@ "dependencies": { "form-data": { "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", "dev": true, "requires": { "asynckit": "^0.4.0", @@ -6425,6 +6973,8 @@ }, "@cypress/xvfb": { "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz", + "integrity": "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==", "dev": true, "requires": { "debug": "^3.1.0", @@ -6433,6 +6983,8 @@ "dependencies": { "debug": { "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "requires": { "ms": "^2.1.1" @@ -6442,10 +6994,14 @@ }, "@discoveryjs/json-ext": { "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", "dev": true }, "@iota/crypto.js": { - "version": "1.9.0-stardust.6", + "version": "1.9.0-stardust.7", + "resolved": "https://registry.npmjs.org/@iota/crypto.js/-/crypto.js-1.9.0-stardust.7.tgz", + "integrity": "sha512-GiUByLYpLuf9i+Cpm5lFPQ9CnO0t54FXWKxf0iDPkqaI5smpBMGKHpqPuk4tAiSvUsIcZ4QIVIgBLgYuOssSCQ==", "requires": { "@iota/util.js": "^1.9.0-stardust.5", "big-integer": "^1.6.51" @@ -6453,6 +7009,8 @@ }, "@iota/iota-client-wasm": { "version": "0.5.0-alpha.2", + "resolved": "https://registry.npmjs.org/@iota/iota-client-wasm/-/iota-client-wasm-0.5.0-alpha.2.tgz", + "integrity": "sha512-mRlrFwTabOZznvamM0I7Ux+dfaGLOEJ5v1197Ni5tH0qbNtiL4h/HdtQIClEtFcZQ+MQ+Pp1rz7qwD3s7nJh1A==", "peer": true, "requires": { "@iota/types": "^1.0.0-beta.11", @@ -6462,6 +7020,8 @@ }, "@iota/iota.js": { "version": "1.9.0-stardust.25", + "resolved": "https://registry.npmjs.org/@iota/iota.js/-/iota.js-1.9.0-stardust.25.tgz", + "integrity": "sha512-mx/ZpkrUEgPjd1FxVxDKGpmDF/aTuUcRXjzZWjBL8v2w9dn3/Bs2fZ3AJw7wIwMG6v48AMXWq2yPwndpwDx/qw==", "peer": true, "requires": { "@iota/crypto.js": "^1.9.0-stardust.6", @@ -6471,16 +7031,22 @@ } }, "@iota/types": { - "version": "1.0.0-beta.11" + "version": "1.0.0-beta.12", + "resolved": "https://registry.npmjs.org/@iota/types/-/types-1.0.0-beta.12.tgz", + "integrity": "sha512-GNIvPBauq8jAuOG7Xyb0W08KV9J0jeZ08igcl6sj3vSSiDb5360tON1NShdCCLfql4vr7n6U5f9Gmv0uSRuTYA==" }, "@iota/util.js": { "version": "1.9.0-stardust.5", + "resolved": "https://registry.npmjs.org/@iota/util.js/-/util.js-1.9.0-stardust.5.tgz", + "integrity": "sha512-BGXzzjUQQYaV/H0bXJYKW+Zgd9PIN+HLPH8BcYhubI65X4G2ID23/osmpK/za+Ds6t/MkpdPKIiBwZPGXuCUBw==", "requires": { "big-integer": "^1.6.51" } }, "@jridgewell/gen-mapping": { "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", "dev": true, "requires": { "@jridgewell/set-array": "^1.0.1", @@ -6490,14 +7056,20 @@ }, "@jridgewell/resolve-uri": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", "dev": true }, "@jridgewell/set-array": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", "dev": true }, "@jridgewell/source-map": { "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", "dev": true, "requires": { "@jridgewell/gen-mapping": "^0.3.0", @@ -6506,10 +7078,14 @@ }, "@jridgewell/sourcemap-codec": { "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", "dev": true }, "@jridgewell/trace-mapping": { "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", "dev": true, "requires": { "@jridgewell/resolve-uri": "^3.0.3", @@ -6518,6 +7094,8 @@ }, "@nodelib/fs.scandir": { "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "requires": { "@nodelib/fs.stat": "2.0.5", @@ -6526,10 +7104,14 @@ }, "@nodelib/fs.stat": { "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true }, "@nodelib/fs.walk": { "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "requires": { "@nodelib/fs.scandir": "2.1.5", @@ -6538,29 +7120,41 @@ }, "@tsconfig/node10": { "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", "dev": true }, "@tsconfig/node12": { "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", "dev": true }, "@tsconfig/node14": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", "dev": true }, "@tsconfig/node16": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", "dev": true }, "@types/debug": { "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", + "integrity": "sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==", "dev": true, "requires": { "@types/ms": "*" } }, "@types/eslint": { - "version": "8.4.5", + "version": "8.4.6", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.6.tgz", + "integrity": "sha512-/fqTbjxyFUaYNO7VcW5g+4npmqVACz1bB7RTHYuLj+PRjw9hrCwrUXVQFpChUS0JsyEFvMZ7U/PfmvWgxJhI9g==", "dev": true, "requires": { "@types/estree": "*", @@ -6569,6 +7163,8 @@ }, "@types/eslint-scope": { "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", "dev": true, "requires": { "@types/eslint": "*", @@ -6577,23 +7173,33 @@ }, "@types/estree": { "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", "dev": true }, "@types/json-schema": { "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", "dev": true }, "@types/json5": { "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true, "optional": true }, "@types/linkify-it": { "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.2.tgz", + "integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==", "dev": true }, "@types/markdown-it": { "version": "12.2.3", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", + "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", "dev": true, "requires": { "@types/linkify-it": "*", @@ -6602,6 +7208,8 @@ }, "@types/mdast": { "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz", + "integrity": "sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==", "dev": true, "requires": { "@types/unist": "*" @@ -6609,21 +7217,31 @@ }, "@types/mdurl": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", + "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", "dev": true }, "@types/mocha": { "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", + "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", "dev": true }, "@types/ms": { "version": "0.7.31", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", + "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==", "dev": true }, "@types/node": { - "version": "18.6.5" + "version": "18.7.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.18.tgz", + "integrity": "sha512-m+6nTEOadJZuTPkKR/SYK3A2d7FZrgElol9UP1Kae90VVU4a6mxnPuLiIW1m4Cq4gZ/nWb9GrdVXJCoCazDAbg==" }, "@types/node-fetch": { "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.2.tgz", + "integrity": "sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==", "requires": { "@types/node": "*", "form-data": "^3.0.0" @@ -6631,18 +7249,26 @@ }, "@types/sinonjs__fake-timers": { "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz", + "integrity": "sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==", "dev": true }, "@types/sizzle": { "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz", + "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==", "dev": true }, "@types/unist": { "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", + "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==", "dev": true }, "@types/yauzl": { "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==", "dev": true, "optional": true, "requires": { @@ -6651,10 +7277,14 @@ }, "@ungap/promise-all-settled": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", "dev": true }, "@webassemblyjs/ast": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", "dev": true, "requires": { "@webassemblyjs/helper-numbers": "1.11.1", @@ -6663,18 +7293,26 @@ }, "@webassemblyjs/floating-point-hex-parser": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", "dev": true }, "@webassemblyjs/helper-api-error": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", "dev": true }, "@webassemblyjs/helper-buffer": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", "dev": true }, "@webassemblyjs/helper-numbers": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", "dev": true, "requires": { "@webassemblyjs/floating-point-hex-parser": "1.11.1", @@ -6684,10 +7322,14 @@ }, "@webassemblyjs/helper-wasm-bytecode": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", "dev": true }, "@webassemblyjs/helper-wasm-section": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", "dev": true, "requires": { "@webassemblyjs/ast": "1.11.1", @@ -6698,6 +7340,8 @@ }, "@webassemblyjs/ieee754": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", "dev": true, "requires": { "@xtuc/ieee754": "^1.2.0" @@ -6705,6 +7349,8 @@ }, "@webassemblyjs/leb128": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", "dev": true, "requires": { "@xtuc/long": "4.2.2" @@ -6712,10 +7358,14 @@ }, "@webassemblyjs/utf8": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", "dev": true }, "@webassemblyjs/wasm-edit": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", "dev": true, "requires": { "@webassemblyjs/ast": "1.11.1", @@ -6730,6 +7380,8 @@ }, "@webassemblyjs/wasm-gen": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", "dev": true, "requires": { "@webassemblyjs/ast": "1.11.1", @@ -6741,6 +7393,8 @@ }, "@webassemblyjs/wasm-opt": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", "dev": true, "requires": { "@webassemblyjs/ast": "1.11.1", @@ -6751,6 +7405,8 @@ }, "@webassemblyjs/wasm-parser": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", "dev": true, "requires": { "@webassemblyjs/ast": "1.11.1", @@ -6763,6 +7419,8 @@ }, "@webassemblyjs/wast-printer": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", "dev": true, "requires": { "@webassemblyjs/ast": "1.11.1", @@ -6771,11 +7429,15 @@ }, "@webpack-cli/configtest": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", + "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", "dev": true, "requires": {} }, "@webpack-cli/info": { "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz", + "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", "dev": true, "requires": { "envinfo": "^7.7.3" @@ -6783,32 +7445,46 @@ }, "@webpack-cli/serve": { "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz", + "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", "dev": true, "requires": {} }, "@xtuc/ieee754": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", "dev": true }, "@xtuc/long": { "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "dev": true }, "acorn": { "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", "dev": true }, "acorn-import-assertions": { "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", "dev": true, "requires": {} }, "acorn-walk": { "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", "dev": true }, "aggregate-error": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "dev": true, "requires": { "clean-stack": "^2.0.0", @@ -6817,6 +7493,8 @@ }, "ajv": { "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -6827,15 +7505,21 @@ }, "ajv-keywords": { "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "dev": true, "requires": {} }, "ansi-colors": { "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true }, "ansi-escape-sequences": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-escape-sequences/-/ansi-escape-sequences-4.1.0.tgz", + "integrity": "sha512-dzW9kHxH011uBsidTXd14JXgzye/YLb2LzeKZ4bsgl/Knwx8AtbSFkkGxagdNOoh0DlqHCmfiEjWKBaqjOanVw==", "dev": true, "requires": { "array-back": "^3.0.1" @@ -6843,23 +7527,31 @@ "dependencies": { "array-back": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", + "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", "dev": true } } }, "ansi-escapes": { "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, "requires": { "type-fest": "^0.21.3" } }, "ansi-regex": { - "version": "3.0.1", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { "color-convert": "^2.0.1" @@ -6867,6 +7559,8 @@ }, "anymatch": { "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, "requires": { "normalize-path": "^3.0.0", @@ -6875,30 +7569,44 @@ }, "arch": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", + "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", "dev": true }, "arg": { "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", "dev": true }, "argparse": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, "array-back": { "version": "6.2.2", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.2.tgz", + "integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==", "dev": true }, "array-union": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, "arrify": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", "dev": true }, "asn1": { "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", "dev": true, "requires": { "safer-buffer": "~2.1.0" @@ -6906,33 +7614,49 @@ }, "assert-plus": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", "dev": true }, "astral-regex": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true }, "async": { "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", "dev": true }, "asynckit": { - "version": "0.4.0" + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "at-least-node": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", "dev": true }, "aws-sign2": { "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", "dev": true }, "aws4": { "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", "dev": true }, "axios": { "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", "dev": true, "requires": { "follow-redirects": "^1.14.0" @@ -6940,18 +7664,26 @@ }, "bail": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", "dev": true }, "balanced-match": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, "base64-js": { "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "dev": true }, "basic-auth": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", "dev": true, "requires": { "safe-buffer": "5.1.2" @@ -6959,30 +7691,42 @@ "dependencies": { "safe-buffer": { "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true } } }, "bcrypt-pbkdf": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", "dev": true, "requires": { "tweetnacl": "^0.14.3" } }, "big-integer": { - "version": "1.6.51" + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==" }, "big.js": { "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", "dev": true }, "binary-extensions": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, "binary-install": { "version": "0.1.1", + "resolved": "https://registry.npmjs.org/binary-install/-/binary-install-0.1.1.tgz", + "integrity": "sha512-DqED0D/6LrS+BHDkKn34vhRqOGjy5gTMgvYZsGK2TpNbdPuz4h+MRlNgGv5QBRd7pWq/jylM4eKNCizgAq3kNQ==", "dev": true, "requires": { "axios": "^0.21.1", @@ -6992,14 +7736,20 @@ }, "blob-util": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz", + "integrity": "sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==", "dev": true }, "bluebird": { "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", "dev": true }, "brace-expansion": { "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { "balanced-match": "^1.0.0", @@ -7008,6 +7758,8 @@ }, "braces": { "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, "requires": { "fill-range": "^7.0.1" @@ -7015,20 +7767,26 @@ }, "browser-stdout": { "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, "browserslist": { - "version": "4.21.3", + "version": "4.21.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001370", - "electron-to-chromium": "^1.4.202", + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.5" + "update-browserslist-db": "^1.0.9" } }, "buffer": { "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "dev": true, "requires": { "base64-js": "^1.3.1", @@ -7037,14 +7795,20 @@ }, "buffer-crc32": { "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "dev": true }, "buffer-from": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, "cache-point": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cache-point/-/cache-point-2.0.0.tgz", + "integrity": "sha512-4gkeHlFpSKgm3vm2gJN5sPqfmijYRFYCQ6tv5cLw0xVmT6r1z1vd4FNnpuOREco3cBs1G709sZ72LdgddKvL5w==", "dev": true, "requires": { "array-back": "^4.0.1", @@ -7054,28 +7818,40 @@ "dependencies": { "array-back": { "version": "4.0.2", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz", + "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==", "dev": true } } }, "cachedir": { "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz", + "integrity": "sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==", "dev": true }, "camelcase": { "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true }, "caniuse-lite": { - "version": "1.0.30001375", + "version": "1.0.30001402", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001402.tgz", + "integrity": "sha512-Mx4MlhXO5NwuvXGgVb+hg65HZ+bhUYsz8QtDGDo2QmaJS2GBX47Xfi2koL86lc8K+l+htXeTEB/Aeqvezoo6Ew==", "dev": true }, "caseless": { "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", "dev": true }, "catharsis": { "version": "0.9.0", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", + "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", "dev": true, "requires": { "lodash": "^4.17.15" @@ -7083,6 +7859,8 @@ }, "chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -7091,6 +7869,8 @@ "dependencies": { "supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { "has-flag": "^4.0.0" @@ -7100,14 +7880,20 @@ }, "character-entities": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", "dev": true }, "check-more-types": { "version": "2.24.0", + "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", + "integrity": "sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==", "dev": true }, "chokidar": { "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, "requires": { "anymatch": "~3.1.2", @@ -7120,27 +7906,43 @@ "readdirp": "~3.6.0" } }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true + }, "chrome-trace-event": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", "dev": true }, "ci-info": { - "version": "3.3.2", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.4.0.tgz", + "integrity": "sha512-t5QdPT5jq3o262DOQ8zA6E1tlH2upmUc4Hlvrbx1pGYJuiiHl7O7rvVNI+l8HTVhd/q3Qc9vqimkNk5yiXsAug==", "dev": true }, "clean-stack": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true }, "cli-cursor": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "dev": true, "requires": { "restore-cursor": "^3.1.0" } }, "cli-table3": { - "version": "0.6.2", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", + "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==", "dev": true, "requires": { "@colors/colors": "1.5.0", @@ -7149,6 +7951,8 @@ }, "cli-truncate": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", "dev": true, "requires": { "slice-ansi": "^3.0.0", @@ -7157,28 +7961,19 @@ }, "cliui": { "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, "requires": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^7.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "dev": true - }, - "strip-ansi": { - "version": "6.0.1", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - } } }, "clone-deep": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", "dev": true, "requires": { "is-plain-object": "^2.0.4", @@ -7188,6 +7983,8 @@ }, "collect-all": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/collect-all/-/collect-all-1.0.4.tgz", + "integrity": "sha512-RKZhRwJtJEP5FWul+gkSMEnaK6H3AGPTTWOiRimCcs+rc/OmQE3Yhy1Q7A7KsdkG3ZXVdZq68Y6ONSdvkeEcKA==", "dev": true, "requires": { "stream-connect": "^1.0.2", @@ -7196,6 +7993,8 @@ }, "color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { "color-name": "~1.1.4" @@ -7203,24 +8002,34 @@ }, "color-name": { "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, "colorette": { "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", "dev": true }, "colors": { "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", "dev": true }, "combined-stream": { "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "requires": { "delayed-stream": "~1.0.0" } }, "command-line-args": { "version": "5.2.1", + "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz", + "integrity": "sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==", "dev": true, "requires": { "array-back": "^3.1.0", @@ -7231,16 +8040,22 @@ "dependencies": { "array-back": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", + "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", "dev": true }, "typical": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", + "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==", "dev": true } } }, "command-line-tool": { "version": "0.8.0", + "resolved": "https://registry.npmjs.org/command-line-tool/-/command-line-tool-0.8.0.tgz", + "integrity": "sha512-Xw18HVx/QzQV3Sc5k1vy3kgtOeGmsKIqwtFFoyjI4bbcpSgnw2CWVULvtakyw4s6fhyAdI6soQQhXc2OzJy62g==", "dev": true, "requires": { "ansi-escape-sequences": "^4.0.0", @@ -7252,6 +8067,8 @@ "dependencies": { "array-back": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", + "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", "dev": true, "requires": { "typical": "^2.6.1" @@ -7261,6 +8078,8 @@ }, "command-line-usage": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-4.1.0.tgz", + "integrity": "sha512-MxS8Ad995KpdAC0Jopo/ovGIroV/m0KHwzKfXxKag6FHOkGsH8/lv5yjgablcRxCJJC0oJeUMuO/gmaq+Wq46g==", "dev": true, "requires": { "ansi-escape-sequences": "^4.0.0", @@ -7271,6 +8090,8 @@ "dependencies": { "array-back": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", + "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", "dev": true, "requires": { "typical": "^2.6.1" @@ -7280,26 +8101,36 @@ }, "commander": { "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", "dev": true }, "common-sequence": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/common-sequence/-/common-sequence-2.0.2.tgz", + "integrity": "sha512-jAg09gkdkrDO9EWTdXfv80WWH3yeZl5oT69fGfedBNS9pXUKYInVJ1bJ+/ht2+Moeei48TmSbQDYMc8EOx9G0g==", "dev": true }, "common-tags": { "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", "dev": true }, "concat-map": { "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, "concurrently": { - "version": "7.3.0", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-7.4.0.tgz", + "integrity": "sha512-M6AfrueDt/GEna/Vg9BqQ+93yuvzkSKmoTixnwEJkH0LlcGrRC2eCmjeG1tLLHIYfpYJABokqSGyMcXjm96AFA==", "dev": true, "requires": { "chalk": "^4.1.0", - "date-fns": "^2.16.1", + "date-fns": "^2.29.1", "lodash": "^4.17.21", "rxjs": "^7.0.0", "shell-quote": "^1.7.3", @@ -7311,6 +8142,8 @@ }, "config-master": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/config-master/-/config-master-3.1.0.tgz", + "integrity": "sha512-n7LBL1zBzYdTpF1mx5DNcZnZn05CWIdsdvtPL4MosvqbBUK3Rq6VWEtGUuF3Y0s9/CIhMejezqlSkP6TnCJ/9g==", "dev": true, "requires": { "walk-back": "^2.0.1" @@ -7318,12 +8151,16 @@ "dependencies": { "walk-back": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/walk-back/-/walk-back-2.0.1.tgz", + "integrity": "sha512-Nb6GvBR8UWX1D+Le+xUq0+Q1kFmRBIWVrfLnQAOmcpEzA9oAxwJ9gIr36t9TWYfzvWRvuMtjHiVsJYEkXWaTAQ==", "dev": true } } }, "copy-webpack-plugin": { "version": "7.0.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-7.0.0.tgz", + "integrity": "sha512-SLjQNa5iE3BoCP76ESU9qYo9ZkEWtXoZxDurHoqPchAFRblJ9g96xTeC560UXBMre1Nx6ixIIUfiY3VcjpJw3g==", "dev": true, "requires": { "fast-glob": "^3.2.4", @@ -7338,18 +8175,26 @@ }, "core-util-is": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", "dev": true }, "corser": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", + "integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==", "dev": true }, "create-require": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, "cross-spawn": { "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "requires": { "path-key": "^3.1.0", @@ -7358,7 +8203,9 @@ } }, "cypress": { - "version": "10.6.0", + "version": "10.8.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-10.8.0.tgz", + "integrity": "sha512-QVse0dnLm018hgti2enKMVZR9qbIO488YGX06nH5j3Dg1isL38DwrBtyrax02CANU6y8F4EJUuyW6HJKw1jsFA==", "dev": true, "requires": { "@cypress/request": "^2.88.10", @@ -7380,7 +8227,7 @@ "dayjs": "^1.10.4", "debug": "^4.3.2", "enquirer": "^2.3.6", - "eventemitter2": "^6.4.3", + "eventemitter2": "6.4.7", "execa": "4.1.0", "executable": "^4.1.1", "extract-zip": "2.0.1", @@ -7406,11 +8253,15 @@ }, "dependencies": { "@types/node": { - "version": "14.18.25", + "version": "14.18.29", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.29.tgz", + "integrity": "sha512-LhF+9fbIX4iPzhsRLpK5H7iPdvW8L4IwGciXQIOEcuF62+9nw/VQVsOViAOOGxY3OlOKGLFv0sWwJXdwQeTn6A==", "dev": true }, "fs-extra": { "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, "requires": { "at-least-node": "^1.0.0", @@ -7423,6 +8274,8 @@ }, "cypress-multi-reporters": { "version": "1.6.1", + "resolved": "https://registry.npmjs.org/cypress-multi-reporters/-/cypress-multi-reporters-1.6.1.tgz", + "integrity": "sha512-FPeC0xWF1N6Myrwc2m7KC0xxlrtG8+x4hlsPFBDRWP8u/veR2x90pGaH3BuJfweV7xoQ4Zo85Qjhu3fgZGrBQQ==", "dev": true, "peer": true, "requires": { @@ -7432,6 +8285,8 @@ }, "cypress-parallel": { "version": "0.9.1", + "resolved": "https://registry.npmjs.org/cypress-parallel/-/cypress-parallel-0.9.1.tgz", + "integrity": "sha512-7VSfFr8HEEN6zkgo6SkG7pPoHK7VakFhEH1jbM4+Ire/I+O2jNzyd1bRUA+O3V2DIMow64ECDJKf13YHBon+BQ==", "dev": true, "requires": { "cli-table3": "^0.6.0", @@ -7447,18 +8302,20 @@ "dependencies": { "ansi-colors": { "version": "4.1.1", - "dev": true - }, - "ansi-regex": { - "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true }, "camelcase": { "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, "chokidar": { "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", "dev": true, "requires": { "anymatch": "~3.1.1", @@ -7473,6 +8330,8 @@ }, "debug": { "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, "requires": { "ms": "2.1.2" @@ -7480,14 +8339,20 @@ }, "decamelize": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", "dev": true }, "escape-string-regexp": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, "glob": { "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -7500,6 +8365,8 @@ }, "js-yaml": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", + "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", "dev": true, "requires": { "argparse": "^2.0.1" @@ -7507,6 +8374,8 @@ }, "locate-path": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "requires": { "p-locate": "^4.1.0" @@ -7514,6 +8383,8 @@ }, "log-symbols": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", + "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", "dev": true, "requires": { "chalk": "^4.0.0" @@ -7521,6 +8392,8 @@ }, "minimatch": { "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -7528,6 +8401,8 @@ }, "mocha": { "version": "8.4.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz", + "integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==", "dev": true, "requires": { "@ungap/promise-all-settled": "1.1.2", @@ -7559,10 +8434,14 @@ "dependencies": { "ms": { "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "yargs": { "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "requires": { "cliui": "^7.0.2", @@ -7578,10 +8457,14 @@ }, "nanoid": { "version": "3.1.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", + "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", "dev": true }, "p-limit": { "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -7589,6 +8472,8 @@ }, "p-locate": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "requires": { "p-limit": "^2.2.0" @@ -7596,24 +8481,23 @@ }, "readdirp": { "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", "dev": true, "requires": { "picomatch": "^2.2.1" } }, - "strip-ansi": { - "version": "6.0.1", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, "workerpool": { "version": "6.1.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz", + "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==", "dev": true }, "wrap-ansi": { "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, "requires": { "ansi-styles": "^4.0.0", @@ -7623,6 +8507,8 @@ }, "yargs": { "version": "15.3.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz", + "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==", "dev": true, "requires": { "cliui": "^6.0.0", @@ -7640,6 +8526,8 @@ "dependencies": { "cliui": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", "dev": true, "requires": { "string-width": "^4.2.0", @@ -7649,6 +8537,8 @@ }, "find-up": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "requires": { "locate-path": "^5.0.0", @@ -7657,10 +8547,14 @@ }, "y18n": { "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", "dev": true }, "yargs-parser": { "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "dev": true, "requires": { "camelcase": "^5.0.0", @@ -7673,21 +8567,29 @@ }, "dashdash": { "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", "dev": true, "requires": { "assert-plus": "^1.0.0" } }, "date-fns": { - "version": "2.29.1", + "version": "2.29.3", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.3.tgz", + "integrity": "sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==", "dev": true }, "dayjs": { "version": "1.11.5", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.5.tgz", + "integrity": "sha512-CAdX5Q3YW3Gclyo5Vpqkgpj8fSdLQcRuzfX6mC6Phy0nfJ0eGYOeS7m4mt2plDWLAtA4TqTakvbboHvUxfe4iA==", "dev": true }, "debug": { "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "requires": { "ms": "2.1.2" @@ -7695,10 +8597,14 @@ }, "decamelize": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true }, "decode-named-character-reference": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", + "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", "dev": true, "requires": { "character-entities": "^2.0.0" @@ -7706,25 +8612,37 @@ }, "deep-extend": { "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true }, "delayed-stream": { - "version": "1.0.0" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" }, "dequal": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", "dev": true }, "diff": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", "dev": true }, "diff-match-patch": { "version": "1.0.5", + "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", + "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==", "dev": true }, "dir-glob": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, "requires": { "path-type": "^4.0.0" @@ -7732,6 +8650,8 @@ }, "dmd": { "version": "6.1.0", + "resolved": "https://registry.npmjs.org/dmd/-/dmd-6.1.0.tgz", + "integrity": "sha512-0zQIJ873gay1scCTFZvHPWM9mVJBnaylB2NQDI8O9u8O32m00Jb6uxDKexZm8hjTRM7RiWe0FJ32pExHoXdwoQ==", "dev": true, "requires": { "array-back": "^6.2.2", @@ -7750,6 +8670,8 @@ }, "ecc-jsbn": { "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", "dev": true, "requires": { "jsbn": "~0.1.0", @@ -7757,19 +8679,27 @@ } }, "electron-to-chromium": { - "version": "1.4.213", + "version": "1.4.253", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.253.tgz", + "integrity": "sha512-1pezJ2E1UyBTGbA7fUlHdPSXQw1k+82VhTFLG5G0AUqLGvsZqFzleOblceqegZzxYX4kC7hGEEdzIQI9RZ1Cuw==", "dev": true }, "emoji-regex": { "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, "emojis-list": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", "dev": true }, "end-of-stream": { "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "dev": true, "requires": { "once": "^1.4.0" @@ -7777,6 +8707,8 @@ }, "enhanced-resolve": { "version": "5.10.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", + "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", "dev": true, "requires": { "graceful-fs": "^4.2.4", @@ -7785,6 +8717,8 @@ }, "enquirer": { "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", "dev": true, "requires": { "ansi-colors": "^4.1.1" @@ -7792,26 +8726,38 @@ }, "entities": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", "dev": true }, "envinfo": { "version": "7.8.1", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", + "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", "dev": true }, "es-module-lexer": { "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", "dev": true }, "escalade": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true }, "escape-string-regexp": { "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true }, "eslint-scope": { "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, "requires": { "esrecurse": "^4.3.0", @@ -7820,6 +8766,8 @@ }, "esrecurse": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "requires": { "estraverse": "^5.2.0" @@ -7827,28 +8775,40 @@ "dependencies": { "estraverse": { "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true } } }, "estraverse": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true }, "eventemitter2": { "version": "6.4.7", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.7.tgz", + "integrity": "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==", "dev": true }, "eventemitter3": { "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", "dev": true }, "events": { "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true }, "execa": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", "dev": true, "requires": { "cross-spawn": "^7.0.0", @@ -7864,6 +8824,8 @@ }, "executable": { "version": "4.1.1", + "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", + "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", "dev": true, "requires": { "pify": "^2.2.0" @@ -7871,10 +8833,14 @@ }, "extend": { "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "dev": true }, "extract-zip": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", "dev": true, "requires": { "@types/yauzl": "^2.9.1", @@ -7885,14 +8851,20 @@ }, "extsprintf": { "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", "dev": true }, "fast-deep-equal": { "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, "fast-glob": { - "version": "3.2.11", + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", @@ -7904,14 +8876,20 @@ }, "fast-json-stable-stringify": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, "fastest-levenshtein": { "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", "dev": true }, "fastq": { "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", "dev": true, "requires": { "reusify": "^1.0.4" @@ -7919,6 +8897,8 @@ }, "fd-slicer": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", "dev": true, "requires": { "pend": "~1.2.0" @@ -7926,6 +8906,8 @@ }, "figures": { "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", "dev": true, "requires": { "escape-string-regexp": "^1.0.5" @@ -7933,6 +8915,8 @@ }, "file-set": { "version": "4.0.2", + "resolved": "https://registry.npmjs.org/file-set/-/file-set-4.0.2.tgz", + "integrity": "sha512-fuxEgzk4L8waGXaAkd8cMr73Pm0FxOVkn8hztzUW7BAHhOGH90viQNXbiOsnecCWmfInqU6YmAMwxRMdKETceQ==", "dev": true, "requires": { "array-back": "^5.0.0", @@ -7941,12 +8925,16 @@ "dependencies": { "array-back": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-5.0.0.tgz", + "integrity": "sha512-kgVWwJReZWmVuWOQKEOohXKJX+nD02JAZ54D1RRWlv8L0NebauKAaFxACKzB74RTclt1+WNz5KHaLRDAPZbDEw==", "dev": true } } }, "fill-range": { "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, "requires": { "to-regex-range": "^5.0.1" @@ -7954,6 +8942,8 @@ }, "find-replace": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", + "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", "dev": true, "requires": { "array-back": "^3.0.1" @@ -7961,12 +8951,16 @@ "dependencies": { "array-back": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", + "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", "dev": true } } }, "find-up": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "requires": { "locate-path": "^6.0.0", @@ -7975,18 +8969,26 @@ }, "flat": { "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "dev": true }, "follow-redirects": { - "version": "1.15.1", + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", "dev": true }, "forever-agent": { "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", "dev": true }, "form-data": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -7995,6 +8997,8 @@ }, "fs-extra": { "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, "requires": { "graceful-fs": "^4.2.0", @@ -8004,6 +9008,8 @@ }, "fs-minipass": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", "dev": true, "requires": { "minipass": "^3.0.0" @@ -8011,22 +9017,39 @@ }, "fs-then-native": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fs-then-native/-/fs-then-native-2.0.0.tgz", + "integrity": "sha512-X712jAOaWXkemQCAmWeg5rOT2i+KOpWz1Z/txk/cW0qlOu2oQ9H61vc5w3X/iyuUEfq/OyaFJ78/cZAQD1/bgA==", "dev": true }, "fs.realpath": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, "function-bind": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, "get-caller-file": { "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, "get-stream": { "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, "requires": { "pump": "^3.0.0" @@ -8034,6 +9057,8 @@ }, "getos": { "version": "3.2.1", + "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz", + "integrity": "sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==", "dev": true, "requires": { "async": "^3.2.0" @@ -8041,6 +9066,8 @@ }, "getpass": { "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", "dev": true, "requires": { "assert-plus": "^1.0.0" @@ -8048,6 +9075,8 @@ }, "glob": { "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -8060,10 +9089,14 @@ }, "glob-escape": { "version": "0.0.2", + "resolved": "https://registry.npmjs.org/glob-escape/-/glob-escape-0.0.2.tgz", + "integrity": "sha512-L/cXYz8x7qer1HAyUQ+mbjcUsJVdpRxpAf7CwqHoNBs9vTpABlGfNN4tzkDxt+u3Z7ZncVyKlCNPtzb0R/7WbA==", "dev": true }, "glob-parent": { "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "requires": { "is-glob": "^4.0.1" @@ -8071,10 +9104,14 @@ }, "glob-to-regexp": { "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", "dev": true }, "global-dirs": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz", + "integrity": "sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==", "dev": true, "requires": { "ini": "2.0.0" @@ -8082,6 +9119,8 @@ }, "globby": { "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "requires": { "array-union": "^2.1.0", @@ -8094,14 +9133,20 @@ }, "graceful-fs": { "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, "growl": { "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", "dev": true }, "handlebars": { "version": "4.7.7", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", + "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", "dev": true, "requires": { "minimist": "^1.2.5", @@ -8113,6 +9158,8 @@ }, "has": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, "requires": { "function-bind": "^1.1.1" @@ -8120,14 +9167,20 @@ }, "has-flag": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, "he": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, "html-encoding-sniffer": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", "dev": true, "requires": { "whatwg-encoding": "^2.0.0" @@ -8135,6 +9188,8 @@ }, "http-proxy": { "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", "dev": true, "requires": { "eventemitter3": "^4.0.0", @@ -8144,6 +9199,8 @@ }, "http-server": { "version": "14.1.1", + "resolved": "https://registry.npmjs.org/http-server/-/http-server-14.1.1.tgz", + "integrity": "sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==", "dev": true, "requires": { "basic-auth": "^2.0.1", @@ -8163,6 +9220,8 @@ }, "http-signature": { "version": "1.3.6", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz", + "integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==", "dev": true, "requires": { "assert-plus": "^1.0.0", @@ -8172,10 +9231,14 @@ }, "human-signals": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", "dev": true }, "iconv-lite": { "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, "requires": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -8183,14 +9246,20 @@ }, "ieee754": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "dev": true }, "ignore": { "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "dev": true }, "import-local": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", "dev": true, "requires": { "pkg-dir": "^4.2.0", @@ -8199,10 +9268,14 @@ }, "indent-string": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true }, "inflight": { "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, "requires": { "once": "^1.3.0", @@ -8211,18 +9284,26 @@ }, "inherits": { "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, "ini": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", "dev": true }, "interpret": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", "dev": true }, "is-binary-path": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, "requires": { "binary-extensions": "^2.0.0" @@ -8230,10 +9311,14 @@ }, "is-buffer": { "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", "dev": true }, "is-ci": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", + "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", "dev": true, "requires": { "ci-info": "^3.2.0" @@ -8241,6 +9326,8 @@ }, "is-core-module": { "version": "2.10.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", + "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", "dev": true, "requires": { "has": "^1.0.3" @@ -8248,14 +9335,20 @@ }, "is-extglob": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true }, "is-fullwidth-code-point": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true }, "is-glob": { "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "requires": { "is-extglob": "^2.1.1" @@ -8263,6 +9356,8 @@ }, "is-installed-globally": { "version": "0.4.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", + "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", "dev": true, "requires": { "global-dirs": "^3.0.0", @@ -8271,22 +9366,32 @@ }, "is-npm": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz", + "integrity": "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==", "dev": true }, "is-number": { "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, "is-path-inside": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true }, "is-plain-obj": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", "dev": true }, "is-plain-object": { "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, "requires": { "isobject": "^3.0.1" @@ -8294,30 +9399,44 @@ }, "is-stream": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true }, "is-typedarray": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", "dev": true }, "is-unicode-supported": { "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true }, "isexe": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "isobject": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", "dev": true }, "isstream": { "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", "dev": true }, "jest-worker": { "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "dev": true, "requires": { "@types/node": "*", @@ -8327,6 +9446,8 @@ }, "js-yaml": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "requires": { "argparse": "^2.0.1" @@ -8334,6 +9455,8 @@ }, "js2xmlparser": { "version": "4.0.2", + "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", + "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", "dev": true, "requires": { "xmlcreate": "^2.0.4" @@ -8341,10 +9464,14 @@ }, "jsbn": { "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", "dev": true }, "jsdoc": { "version": "3.6.11", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.11.tgz", + "integrity": "sha512-8UCU0TYeIYD9KeLzEcAu2q8N/mx9O3phAGl32nmHlE0LpaJL71mMkP4d+QE5zWfNt50qheHtOZ0qoxVrsX5TUg==", "dev": true, "requires": { "@babel/parser": "^7.9.4", @@ -8366,12 +9493,16 @@ "dependencies": { "escape-string-regexp": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", "dev": true } } }, "jsdoc-api": { "version": "7.1.1", + "resolved": "https://registry.npmjs.org/jsdoc-api/-/jsdoc-api-7.1.1.tgz", + "integrity": "sha512-0pkuPCzVXiqsDAsVrNFXCkHzlyNepBIDVtwwehry4RJAnZmXtlAz7rh8F9FRz53u3NeynGbex+bpYWwi8lE66A==", "dev": true, "requires": { "array-back": "^6.2.2", @@ -8387,6 +9518,8 @@ }, "jsdoc-parse": { "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsdoc-parse/-/jsdoc-parse-6.1.0.tgz", + "integrity": "sha512-n/hDGQJa69IBun1yZAjqzV4gVR41+flZ3bIlm9fKvNe2Xjsd1/+zCo2+R9ls8LxtePgIWbpA1jU7xkB2lRdLLg==", "dev": true, "requires": { "array-back": "^6.2.2", @@ -8399,6 +9532,8 @@ }, "jsdoc-to-markdown": { "version": "7.1.1", + "resolved": "https://registry.npmjs.org/jsdoc-to-markdown/-/jsdoc-to-markdown-7.1.1.tgz", + "integrity": "sha512-CI86d63xAVNO+ENumWwmJ034lYe5iGU5GwjtTA11EuphP9tpnoi4hrKgR/J8uME0D+o4KUpVfwX1fjZhc8dEtg==", "dev": true, "requires": { "array-back": "^6.2.2", @@ -8412,26 +9547,38 @@ }, "json-parse-even-better-errors": { "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, "json-schema": { "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", "dev": true }, "json-schema-traverse": { "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, "json-stringify-safe": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", "dev": true }, "json5": { "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", "dev": true }, "jsonfile": { "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, "requires": { "graceful-fs": "^4.1.6", @@ -8440,6 +9587,8 @@ }, "jsprim": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", + "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==", "dev": true, "requires": { "assert-plus": "1.0.0", @@ -8450,10 +9599,14 @@ }, "kind-of": { "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true }, "klaw": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", + "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", "dev": true, "requires": { "graceful-fs": "^4.1.9" @@ -8461,14 +9614,20 @@ }, "kleur": { "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", "dev": true }, "lazy-ass": { "version": "1.6.0", + "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", + "integrity": "sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==", "dev": true }, "linkify-it": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", + "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", "dev": true, "requires": { "uc.micro": "^1.0.1" @@ -8476,6 +9635,8 @@ }, "listr2": { "version": "3.14.0", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.14.0.tgz", + "integrity": "sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==", "dev": true, "requires": { "cli-truncate": "^2.1.0", @@ -8490,10 +9651,14 @@ }, "loader-runner": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", "dev": true }, "loader-utils": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", "dev": true, "requires": { "big.js": "^5.2.2", @@ -8503,6 +9668,8 @@ }, "locate-path": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "requires": { "p-locate": "^5.0.0" @@ -8510,30 +9677,44 @@ }, "lodash": { "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, "lodash.camelcase": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", "dev": true }, "lodash.omit": { "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", + "integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==", "dev": true }, "lodash.once": { "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", "dev": true }, "lodash.padend": { "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz", + "integrity": "sha512-sOQs2aqGpbl27tmCS1QNZA09Uqp01ZzWfDUoD+xzTii0E7dSQfRKcRetFwa+uXaxaqL+TKm7CgD2JdKP7aZBSw==", "dev": true }, "lodash.pick": { "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", + "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==", "dev": true }, "log-symbols": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "requires": { "chalk": "^4.1.0", @@ -8542,6 +9723,8 @@ }, "log-update": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", "dev": true, "requires": { "ansi-escapes": "^4.3.0", @@ -8550,12 +9733,10 @@ "wrap-ansi": "^6.2.0" }, "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "dev": true - }, "slice-ansi": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", "dev": true, "requires": { "ansi-styles": "^4.0.0", @@ -8563,15 +9744,10 @@ "is-fullwidth-code-point": "^3.0.0" } }, - "strip-ansi": { - "version": "6.0.1", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, "wrap-ansi": { "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, "requires": { "ansi-styles": "^4.0.0", @@ -8583,6 +9759,8 @@ }, "lru-cache": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "requires": { "yallist": "^4.0.0" @@ -8590,10 +9768,14 @@ }, "make-error": { "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, "markdown-it": { "version": "12.3.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", + "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", "dev": true, "requires": { "argparse": "^2.0.1", @@ -8604,16 +9786,22 @@ } }, "markdown-it-anchor": { - "version": "8.6.4", + "version": "8.6.5", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.5.tgz", + "integrity": "sha512-PI1qEHHkTNWT+X6Ip9w+paonfIQ+QZP9sCeMYi47oqhH+EsW8CrJ8J7CzV19QVOj6il8ATGbK2nTECj22ZHGvQ==", "dev": true, "requires": {} }, "marked": { - "version": "4.0.18", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.1.0.tgz", + "integrity": "sha512-+Z6KDjSPa6/723PQYyc1axYZpYYpDnECDaU6hkaf5gqBieBkMKYReL5hteF2QizhlMbgbo8umXl/clZ67+GlsA==", "dev": true }, "mdast-util-from-markdown": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.2.0.tgz", + "integrity": "sha512-iZJyyvKD1+K7QX1b5jXdE7Sc5dtoTry1vzV28UZZe8Z1xVnB/czKntJ7ZAkG0tANqRnBF6p3p7GpU1y19DTf2Q==", "dev": true, "requires": { "@types/mdast": "^3.0.0", @@ -8632,22 +9820,32 @@ }, "mdast-util-to-string": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.1.0.tgz", + "integrity": "sha512-n4Vypz/DZgwo0iMHLQL49dJzlp7YtAJP+N07MZHpjPf/5XJuHUWstviF4Mn2jEiR/GNmtnRRqnwsXExk3igfFA==", "dev": true }, "mdurl": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", "dev": true }, "merge-stream": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, "merge2": { "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true }, "micromark": { "version": "3.0.10", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.0.10.tgz", + "integrity": "sha512-ryTDy6UUunOXy2HPjelppgJ2sNfcPz1pLlMdA6Rz9jPzhLikWXv/irpWV/I2jd68Uhmny7hHxAlAhk4+vWggpg==", "dev": true, "requires": { "@types/debug": "^4.0.0", @@ -8671,6 +9869,8 @@ }, "micromark-core-commonmark": { "version": "1.0.6", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.0.6.tgz", + "integrity": "sha512-K+PkJTxqjFfSNkfAhp4GB+cZPfQd6dxtTXnf+RjZOV7T4EEXnvgzOcnp+eSTmpGk9d1S9sL6/lqrgSNn/s0HZA==", "dev": true, "requires": { "decode-named-character-reference": "^1.0.0", @@ -8693,6 +9893,8 @@ }, "micromark-factory-destination": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.0.0.tgz", + "integrity": "sha512-eUBA7Rs1/xtTVun9TmV3gjfPz2wEwgK5R5xcbIM5ZYAtvGF6JkyaDsj0agx8urXnO31tEO6Ug83iVH3tdedLnw==", "dev": true, "requires": { "micromark-util-character": "^1.0.0", @@ -8702,6 +9904,8 @@ }, "micromark-factory-label": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.0.2.tgz", + "integrity": "sha512-CTIwxlOnU7dEshXDQ+dsr2n+yxpP0+fn271pu0bwDIS8uqfFcumXpj5mLn3hSC8iw2MUr6Gx8EcKng1dD7i6hg==", "dev": true, "requires": { "micromark-util-character": "^1.0.0", @@ -8712,6 +9916,8 @@ }, "micromark-factory-space": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.0.0.tgz", + "integrity": "sha512-qUmqs4kj9a5yBnk3JMLyjtWYN6Mzfcx8uJfi5XAveBniDevmZasdGBba5b4QsvRcAkmvGo5ACmSUmyGiKTLZew==", "dev": true, "requires": { "micromark-util-character": "^1.0.0", @@ -8720,6 +9926,8 @@ }, "micromark-factory-title": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.0.2.tgz", + "integrity": "sha512-zily+Nr4yFqgMGRKLpTVsNl5L4PMu485fGFDOQJQBl2NFpjGte1e86zC0da93wf97jrc4+2G2GQudFMHn3IX+A==", "dev": true, "requires": { "micromark-factory-space": "^1.0.0", @@ -8731,6 +9939,8 @@ }, "micromark-factory-whitespace": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.0.0.tgz", + "integrity": "sha512-Qx7uEyahU1lt1RnsECBiuEbfr9INjQTGa6Err+gF3g0Tx4YEviPbqqGKNv/NrBaE7dVHdn1bVZKM/n5I/Bak7A==", "dev": true, "requires": { "micromark-factory-space": "^1.0.0", @@ -8741,6 +9951,8 @@ }, "micromark-util-character": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.1.0.tgz", + "integrity": "sha512-agJ5B3unGNJ9rJvADMJ5ZiYjBRyDpzKAOk01Kpi1TKhlT1APx3XZk6eN7RtSz1erbWHC2L8T3xLZ81wdtGRZzg==", "dev": true, "requires": { "micromark-util-symbol": "^1.0.0", @@ -8749,6 +9961,8 @@ }, "micromark-util-chunked": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.0.0.tgz", + "integrity": "sha512-5e8xTis5tEZKgesfbQMKRCyzvffRRUX+lK/y+DvsMFdabAicPkkZV6gO+FEWi9RfuKKoxxPwNL+dFF0SMImc1g==", "dev": true, "requires": { "micromark-util-symbol": "^1.0.0" @@ -8756,6 +9970,8 @@ }, "micromark-util-classify-character": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.0.0.tgz", + "integrity": "sha512-F8oW2KKrQRb3vS5ud5HIqBVkCqQi224Nm55o5wYLzY/9PwHGXC01tr3d7+TqHHz6zrKQ72Okwtvm/xQm6OVNZA==", "dev": true, "requires": { "micromark-util-character": "^1.0.0", @@ -8765,6 +9981,8 @@ }, "micromark-util-combine-extensions": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.0.0.tgz", + "integrity": "sha512-J8H058vFBdo/6+AsjHp2NF7AJ02SZtWaVUjsayNFeAiydTxUwViQPxN0Hf8dp4FmCQi0UUFovFsEyRSUmFH3MA==", "dev": true, "requires": { "micromark-util-chunked": "^1.0.0", @@ -8773,6 +9991,8 @@ }, "micromark-util-decode-numeric-character-reference": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.0.0.tgz", + "integrity": "sha512-OzO9AI5VUtrTD7KSdagf4MWgHMtET17Ua1fIpXTpuhclCqD8egFWo85GxSGvxgkGS74bEahvtM0WP0HjvV0e4w==", "dev": true, "requires": { "micromark-util-symbol": "^1.0.0" @@ -8780,6 +10000,8 @@ }, "micromark-util-decode-string": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.0.2.tgz", + "integrity": "sha512-DLT5Ho02qr6QWVNYbRZ3RYOSSWWFuH3tJexd3dgN1odEuPNxCngTCXJum7+ViRAd9BbdxCvMToPOD/IvVhzG6Q==", "dev": true, "requires": { "decode-named-character-reference": "^1.0.0", @@ -8790,14 +10012,20 @@ }, "micromark-util-encode": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.0.1.tgz", + "integrity": "sha512-U2s5YdnAYexjKDel31SVMPbfi+eF8y1U4pfiRW/Y8EFVCy/vgxk/2wWTxzcqE71LHtCuCzlBDRU2a5CQ5j+mQA==", "dev": true }, "micromark-util-html-tag-name": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.1.0.tgz", + "integrity": "sha512-BKlClMmYROy9UiV03SwNmckkjn8QHVaWkqoAqzivabvdGcwNGMMMH/5szAnywmsTBUzDsU57/mFi0sp4BQO6dA==", "dev": true }, "micromark-util-normalize-identifier": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.0.0.tgz", + "integrity": "sha512-yg+zrL14bBTFrQ7n35CmByWUTFsgst5JhA4gJYoty4Dqzj4Z4Fr/DHekSS5aLfH9bdlfnSvKAWsAgJhIbogyBg==", "dev": true, "requires": { "micromark-util-symbol": "^1.0.0" @@ -8805,6 +10033,8 @@ }, "micromark-util-resolve-all": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.0.0.tgz", + "integrity": "sha512-CB/AGk98u50k42kvgaMM94wzBqozSzDDaonKU7P7jwQIuH2RU0TeBqGYJz2WY1UdihhjweivStrJ2JdkdEmcfw==", "dev": true, "requires": { "micromark-util-types": "^1.0.0" @@ -8812,6 +10042,8 @@ }, "micromark-util-sanitize-uri": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.0.0.tgz", + "integrity": "sha512-cCxvBKlmac4rxCGx6ejlIviRaMKZc0fWm5HdCHEeDWRSkn44l6NdYVRyU+0nT1XC72EQJMZV8IPHF+jTr56lAg==", "dev": true, "requires": { "micromark-util-character": "^1.0.0", @@ -8821,6 +10053,8 @@ }, "micromark-util-subtokenize": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.0.2.tgz", + "integrity": "sha512-d90uqCnXp/cy4G881Ub4psE57Sf8YD0pim9QdjCRNjfas2M1u6Lbt+XZK9gnHL2XFhnozZiEdCa9CNfXSfQ6xA==", "dev": true, "requires": { "micromark-util-chunked": "^1.0.0", @@ -8831,14 +10065,20 @@ }, "micromark-util-symbol": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.0.1.tgz", + "integrity": "sha512-oKDEMK2u5qqAptasDAwWDXq0tG9AssVwAx3E9bBF3t/shRIGsWIRG+cGafs2p/SnDSOecnt6hZPCE2o6lHfFmQ==", "dev": true }, "micromark-util-types": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.0.2.tgz", + "integrity": "sha512-DCfg/T8fcrhrRKTPjRrw/5LLvdGV7BHySf/1LOZx7TzWZdYRjogNtyNq885z3nNallwr3QUKARjqvHqX1/7t+w==", "dev": true }, "micromatch": { "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, "requires": { "braces": "^3.0.2", @@ -8847,23 +10087,33 @@ }, "mime": { "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "dev": true }, "mime-db": { - "version": "1.52.0" + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" }, "mime-types": { "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "requires": { "mime-db": "1.52.0" } }, "mimic-fn": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true }, "minimatch": { "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -8871,10 +10121,14 @@ }, "minimist": { "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, "minipass": { "version": "3.3.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.4.tgz", + "integrity": "sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw==", "dev": true, "requires": { "yallist": "^4.0.0" @@ -8882,6 +10136,8 @@ }, "minizlib": { "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", "dev": true, "requires": { "minipass": "^3.0.0", @@ -8890,14 +10146,20 @@ }, "mkdirp": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true }, "mkdirp2": { "version": "1.0.5", + "resolved": "https://registry.npmjs.org/mkdirp2/-/mkdirp2-1.0.5.tgz", + "integrity": "sha512-xOE9xbICroUDmG1ye2h4bZ8WBie9EGmACaco8K8cx6RlkJJrxGIqjGqztAI+NMhexXBcdGbSEzI6N3EJPevxZw==", "dev": true }, "mocha": { "version": "9.2.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", + "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", "dev": true, "requires": { "@ungap/promise-all-settled": "1.1.2", @@ -8928,10 +10190,14 @@ "dependencies": { "ansi-colors": { "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true }, "debug": { "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "dev": true, "requires": { "ms": "2.1.2" @@ -8939,16 +10205,22 @@ "dependencies": { "ms": { "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true } } }, "escape-string-regexp": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, "glob": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -8961,6 +10233,8 @@ "dependencies": { "minimatch": { "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -8970,6 +10244,8 @@ }, "minimatch": { "version": "4.2.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", + "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -8977,10 +10253,14 @@ }, "ms": { "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "serialize-javascript": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", "dev": true, "requires": { "randombytes": "^2.1.0" @@ -8988,6 +10268,8 @@ }, "yargs": { "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "requires": { "cliui": "^7.0.2", @@ -9003,40 +10285,58 @@ }, "mri": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", "dev": true }, "ms": { "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, "mylas": { "version": "2.1.11", + "resolved": "https://registry.npmjs.org/mylas/-/mylas-2.1.11.tgz", + "integrity": "sha512-krnPUl3n9/k52FGCltWMYcqp9SttxjRJEy0sWLk+g7mIa7wnZrmNSZ40Acx7ghzRSOsxt2rEqMbaq4jWlnTDKg==", "dev": true }, "nanoid": { "version": "3.3.1", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", + "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", "dev": true }, "neo-async": { "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, "node-fetch": { "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", "requires": { "whatwg-url": "^5.0.0" } }, "node-releases": { "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", "dev": true }, "normalize-path": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, "npm-run-path": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, "requires": { "path-key": "^3.0.0" @@ -9044,14 +10344,20 @@ }, "object-get": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/object-get/-/object-get-2.1.1.tgz", + "integrity": "sha512-7n4IpLMzGGcLEMiQKsNR7vCe+N5E9LORFrtNUVy4sO3dj9a3HedZCxEL2T7QuLhcHN1NBuBsMOKaOsAYI9IIvg==", "dev": true }, "object-to-spawn-args": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object-to-spawn-args/-/object-to-spawn-args-2.0.1.tgz", + "integrity": "sha512-6FuKFQ39cOID+BMZ3QaphcC8Y4cw6LXBLyIgPU+OhIYwviJamPAn+4mITapnSBQrejB+NNp+FMskhD8Cq+Ys3w==", "dev": true }, "once": { "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "requires": { "wrappy": "1" @@ -9059,6 +10365,8 @@ }, "onetime": { "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, "requires": { "mimic-fn": "^2.1.0" @@ -9066,14 +10374,20 @@ }, "opener": { "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", "dev": true }, "ospath": { "version": "1.2.2", + "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz", + "integrity": "sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==", "dev": true }, "p-limit": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "requires": { "yocto-queue": "^0.1.0" @@ -9081,6 +10395,8 @@ }, "p-locate": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "requires": { "p-limit": "^3.0.2" @@ -9088,6 +10404,8 @@ }, "p-map": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", "dev": true, "requires": { "aggregate-error": "^3.0.0" @@ -9095,50 +10413,74 @@ }, "p-try": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, "path-exists": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true }, "path-is-absolute": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true }, "path-key": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, "path-parse": { "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, "path-type": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true }, "pend": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", "dev": true }, "performance-now": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", "dev": true }, "picocolors": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "dev": true }, "picomatch": { "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, "pify": { "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true }, "pkg-dir": { "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, "requires": { "find-up": "^4.0.0" @@ -9146,6 +10488,8 @@ "dependencies": { "find-up": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "requires": { "locate-path": "^5.0.0", @@ -9154,6 +10498,8 @@ }, "locate-path": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "requires": { "p-locate": "^4.1.0" @@ -9161,6 +10507,8 @@ }, "p-limit": { "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -9168,6 +10516,8 @@ }, "p-locate": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "requires": { "p-limit": "^2.2.0" @@ -9176,14 +10526,18 @@ } }, "plimit-lit": { - "version": "1.3.0", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/plimit-lit/-/plimit-lit-1.4.0.tgz", + "integrity": "sha512-e4VKIdzc5+vvTuNZeWDTWHCNPLZ1+jXeQ9HqZhAS+vlAodNr8A26IcTA9OudjLXj8fV+KRW/ZzsoyrTZzoWD/A==", "dev": true, "requires": { - "queue-lit": "^1.3.0" + "queue-lit": "^1.4.0" } }, "portfinder": { - "version": "1.0.29", + "version": "1.0.32", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz", + "integrity": "sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==", "dev": true, "requires": { "async": "^2.6.4", @@ -9193,6 +10547,8 @@ "dependencies": { "async": { "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", "dev": true, "requires": { "lodash": "^4.17.14" @@ -9200,6 +10556,8 @@ }, "debug": { "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "requires": { "ms": "^2.1.1" @@ -9207,6 +10565,8 @@ }, "mkdirp": { "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, "requires": { "minimist": "^1.2.6" @@ -9216,18 +10576,26 @@ }, "pretty-bytes": { "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", "dev": true }, "proxy-from-env": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", + "integrity": "sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==", "dev": true }, "psl": { "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", "dev": true }, "pump": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, "requires": { "end-of-stream": "^1.1.0", @@ -9236,22 +10604,32 @@ }, "punycode": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, "qs": { "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", "dev": true }, "queue-lit": { - "version": "1.3.0", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/queue-lit/-/queue-lit-1.4.0.tgz", + "integrity": "sha512-l1+4YHm4vHWpCnvTg8JMsnPETmPvLGWhqjvNOc8TSbqscGplHVSWXOxybA3vYeMNNIR9Z1PQt85U+S3wFJX2uQ==", "dev": true }, "queue-microtask": { "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, "randombytes": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, "requires": { "safe-buffer": "^5.1.0" @@ -9259,6 +10637,8 @@ }, "readdirp": { "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "requires": { "picomatch": "^2.2.1" @@ -9266,6 +10646,8 @@ }, "rechoir": { "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", + "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", "dev": true, "requires": { "resolve": "^1.9.0" @@ -9273,6 +10655,8 @@ }, "reduce-extract": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/reduce-extract/-/reduce-extract-1.0.0.tgz", + "integrity": "sha512-QF8vjWx3wnRSL5uFMyCjDeDc5EBMiryoT9tz94VvgjKfzecHAVnqmXAwQDcr7X4JmLc2cjkjFGCVzhMqDjgR9g==", "dev": true, "requires": { "test-value": "^1.0.1" @@ -9280,6 +10664,8 @@ "dependencies": { "array-back": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz", + "integrity": "sha512-1WxbZvrmyhkNoeYcizokbmh5oiOCIfyvGtcqbK3Ls1v1fKcquzxnQSceOx6tzq7jmai2kFLWIpGND2cLhH6TPw==", "dev": true, "requires": { "typical": "^2.6.0" @@ -9287,6 +10673,8 @@ }, "test-value": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/test-value/-/test-value-1.1.0.tgz", + "integrity": "sha512-wrsbRo7qP+2Je8x8DsK8ovCGyxe3sYfQwOraIY/09A2gFXU9DYKiTF14W4ki/01AEh56kMzAmlj9CaHGDDUBJA==", "dev": true, "requires": { "array-back": "^1.0.2", @@ -9297,14 +10685,20 @@ }, "reduce-flatten": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-3.0.1.tgz", + "integrity": "sha512-bYo+97BmUUOzg09XwfkwALt4PQH1M5L0wzKerBt6WLm3Fhdd43mMS89HiT1B9pJIqko/6lWx3OnV4J9f2Kqp5Q==", "dev": true }, "reduce-unique": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/reduce-unique/-/reduce-unique-2.0.1.tgz", + "integrity": "sha512-x4jH/8L1eyZGR785WY+ePtyMNhycl1N2XOLxhCbzZFaqF4AXjLzqSxa2UHgJ2ZVR/HHyPOvl1L7xRnW8ye5MdA==", "dev": true }, "reduce-without": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/reduce-without/-/reduce-without-1.0.1.tgz", + "integrity": "sha512-zQv5y/cf85sxvdrKPlfcRzlDn/OqKFThNimYmsS3flmkioKvkUGn2Qg9cJVoQiEvdxFGLE0MQER/9fZ9sUqdxg==", "dev": true, "requires": { "test-value": "^2.0.0" @@ -9312,6 +10706,8 @@ "dependencies": { "array-back": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz", + "integrity": "sha512-1WxbZvrmyhkNoeYcizokbmh5oiOCIfyvGtcqbK3Ls1v1fKcquzxnQSceOx6tzq7jmai2kFLWIpGND2cLhH6TPw==", "dev": true, "requires": { "typical": "^2.6.0" @@ -9319,6 +10715,8 @@ }, "test-value": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/test-value/-/test-value-2.1.0.tgz", + "integrity": "sha512-+1epbAxtKeXttkGFMTX9H42oqzOTufR1ceCF+GYA5aOmvaPq9wd4PUS8329fn2RRLGNeUkgRLnVpycjx8DsO2w==", "dev": true, "requires": { "array-back": "^1.0.3", @@ -9329,6 +10727,8 @@ }, "remark-parse": { "version": "10.0.1", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-10.0.1.tgz", + "integrity": "sha512-1fUyHr2jLsVOkhbvPRBJ5zTKZZyD6yZzYaWCS6BPBdQ8vEMBCH+9zNCDA6tET/zHCi/jLqjCWtlJZUPk+DbnFw==", "dev": true, "requires": { "@types/mdast": "^3.0.0", @@ -9338,6 +10738,8 @@ }, "request-progress": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", + "integrity": "sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==", "dev": true, "requires": { "throttleit": "^1.0.0" @@ -9345,18 +10747,26 @@ }, "require-directory": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true }, "require-main-filename": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, "requires-port": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "dev": true }, "requizzle": { "version": "0.2.3", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.3.tgz", + "integrity": "sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==", "dev": true, "requires": { "lodash": "^4.17.14" @@ -9364,6 +10774,8 @@ }, "resolve": { "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", "dev": true, "requires": { "is-core-module": "^2.9.0", @@ -9373,6 +10785,8 @@ }, "resolve-cwd": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, "requires": { "resolve-from": "^5.0.0" @@ -9380,10 +10794,14 @@ }, "resolve-from": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true }, "restore-cursor": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "dev": true, "requires": { "onetime": "^5.1.0", @@ -9392,14 +10810,20 @@ }, "reusify": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true }, "rfdc": { "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", "dev": true }, "rimraf": { "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, "requires": { "glob": "^7.1.3" @@ -9407,6 +10831,8 @@ }, "run-parallel": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, "requires": { "queue-microtask": "^1.2.2" @@ -9414,6 +10840,8 @@ }, "rxjs": { "version": "7.5.6", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.6.tgz", + "integrity": "sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw==", "dev": true, "requires": { "tslib": "^2.1.0" @@ -9421,6 +10849,8 @@ }, "sade": { "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", "dev": true, "requires": { "mri": "^1.1.0" @@ -9428,14 +10858,20 @@ }, "safe-buffer": { "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true }, "safer-buffer": { "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, "schema-utils": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", "dev": true, "requires": { "@types/json-schema": "^7.0.8", @@ -9445,10 +10881,14 @@ }, "secure-compare": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", + "integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==", "dev": true }, "semver": { "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -9456,6 +10896,8 @@ }, "serialize-javascript": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", + "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", "dev": true, "requires": { "randombytes": "^2.1.0" @@ -9463,10 +10905,14 @@ }, "set-blocking": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, "shallow-clone": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", "dev": true, "requires": { "kind-of": "^6.0.2" @@ -9474,6 +10920,8 @@ }, "shebang-command": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "requires": { "shebang-regex": "^3.0.0" @@ -9481,22 +10929,32 @@ }, "shebang-regex": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, "shell-quote": { "version": "1.7.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz", + "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==", "dev": true }, "signal-exit": { "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, "slash": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, "slice-ansi": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", "dev": true, "requires": { "ansi-styles": "^4.0.0", @@ -9506,6 +10964,8 @@ }, "sort-array": { "version": "4.1.5", + "resolved": "https://registry.npmjs.org/sort-array/-/sort-array-4.1.5.tgz", + "integrity": "sha512-Ya4peoS1fgFN42RN1REk2FgdNOeLIEMKFGJvs7VTP3OklF8+kl2SkpVliZ4tk/PurWsrWRsdNdU+tgyOBkB9sA==", "dev": true, "requires": { "array-back": "^5.0.0", @@ -9514,20 +10974,28 @@ "dependencies": { "array-back": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-5.0.0.tgz", + "integrity": "sha512-kgVWwJReZWmVuWOQKEOohXKJX+nD02JAZ54D1RRWlv8L0NebauKAaFxACKzB74RTclt1+WNz5KHaLRDAPZbDEw==", "dev": true }, "typical": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/typical/-/typical-6.0.1.tgz", + "integrity": "sha512-+g3NEp7fJLe9DPa1TArHm9QAA7YciZmWnfAqEaFrBihQ7epOv9i99rjtgb6Iz0wh3WuQDjsCTDfgRoGnmHN81A==", "dev": true } } }, "source-map": { "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, "source-map-support": { "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -9536,10 +11004,14 @@ }, "spawn-command": { "version": "0.0.2-1", + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz", + "integrity": "sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg==", "dev": true }, "sshpk": { "version": "1.17.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", + "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", "dev": true, "requires": { "asn1": "~0.2.3", @@ -9555,6 +11027,8 @@ }, "stream-connect": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stream-connect/-/stream-connect-1.0.2.tgz", + "integrity": "sha512-68Kl+79cE0RGKemKkhxTSg8+6AGrqBt+cbZAXevg2iJ6Y3zX4JhA/sZeGzLpxW9cXhmqAcE7KnJCisUmIUfnFQ==", "dev": true, "requires": { "array-back": "^1.0.2" @@ -9562,6 +11036,8 @@ "dependencies": { "array-back": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz", + "integrity": "sha512-1WxbZvrmyhkNoeYcizokbmh5oiOCIfyvGtcqbK3Ls1v1fKcquzxnQSceOx6tzq7jmai2kFLWIpGND2cLhH6TPw==", "dev": true, "requires": { "typical": "^2.6.0" @@ -9571,52 +11047,53 @@ }, "stream-via": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/stream-via/-/stream-via-1.0.4.tgz", + "integrity": "sha512-DBp0lSvX5G9KGRDTkR/R+a29H+Wk2xItOF+MpZLLNDWbEV9tGPnqLPxHEYjmiz8xGtJHRIqmI+hCjmNzqoA4nQ==", "dev": true }, "string-width": { "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "dev": true - }, - "strip-ansi": { - "version": "6.0.1", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - } } }, "strip-ansi": { - "version": "4.0.0", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "^5.0.1" } }, "strip-bom": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, "optional": true }, "strip-final-newline": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true }, "strip-json-comments": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, "supports-color": { "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "requires": { "has-flag": "^4.0.0" @@ -9624,10 +11101,14 @@ }, "supports-preserve-symlinks-flag": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true }, "table-layout": { "version": "0.4.5", + "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-0.4.5.tgz", + "integrity": "sha512-zTvf0mcggrGeTe/2jJ6ECkJHAQPIYEwDoqsiqBjI24mvRmQbInK5jq33fyypaCBxX08hMkfmdOqj6haT33EqWw==", "dev": true, "requires": { "array-back": "^2.0.0", @@ -9639,6 +11120,8 @@ "dependencies": { "array-back": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", + "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", "dev": true, "requires": { "typical": "^2.6.1" @@ -9648,14 +11131,20 @@ }, "taffydb": { "version": "2.6.2", + "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", + "integrity": "sha512-y3JaeRSplks6NYQuCOj3ZFMO3j60rTwbuKCvZxsAraGYH2epusatvZ0baZYA01WsGqJBq/Dl6vOrMUJqyMj8kA==", "dev": true }, "tapable": { "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "dev": true }, "tar": { "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", "dev": true, "requires": { "chownr": "^2.0.0", @@ -9664,20 +11153,18 @@ "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" - }, - "dependencies": { - "chownr": { - "version": "2.0.0", - "dev": true - } } }, "temp-path": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/temp-path/-/temp-path-1.0.0.tgz", + "integrity": "sha512-TvmyH7kC6ZVTYkqCODjJIbgvu0FKiwQpZ4D1aknE7xpcDf/qEOB8KZEK5ef2pfbVoiBhNWs3yx4y+ESMtNYmlg==", "dev": true }, "terser": { - "version": "5.14.2", + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.15.0.tgz", + "integrity": "sha512-L1BJiXVmheAQQy+as0oF3Pwtlo4s3Wi1X2zNZ2NxOB4wx9bdS9Vk67XQENLFdLYGCK/Z2di53mTj/hBafR+dTA==", "dev": true, "requires": { "@jridgewell/source-map": "^0.3.2", @@ -9688,23 +11175,39 @@ "dependencies": { "commander": { "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true } } }, "terser-webpack-plugin": { - "version": "5.3.3", + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz", + "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==", "dev": true, "requires": { - "@jridgewell/trace-mapping": "^0.3.7", + "@jridgewell/trace-mapping": "^0.3.14", "jest-worker": "^27.4.5", "schema-utils": "^3.1.1", "serialize-javascript": "^6.0.0", - "terser": "^5.7.2" + "terser": "^5.14.1" }, "dependencies": { + "@jridgewell/trace-mapping": { + "version": "0.3.15", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", + "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "serialize-javascript": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", "dev": true, "requires": { "randombytes": "^2.1.0" @@ -9714,6 +11217,8 @@ }, "test-value": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/test-value/-/test-value-3.0.0.tgz", + "integrity": "sha512-sVACdAWcZkSU9x7AOmJo5TqE+GyNJknHaHsMrR6ZnhjVlVN9Yx6FjHrsKZ3BjIpPCT68zYesPWkakrNupwfOTQ==", "dev": true, "requires": { "array-back": "^2.0.0", @@ -9722,6 +11227,8 @@ "dependencies": { "array-back": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", + "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", "dev": true, "requires": { "typical": "^2.6.1" @@ -9731,18 +11238,26 @@ }, "text-encoding": { "version": "0.7.0", + "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.7.0.tgz", + "integrity": "sha512-oJQ3f1hrOnbRLOcwKz0Liq2IcrvDeZRHXhd9RgLrsT+DjWY/nty1Hi7v3dtkaEYbPYe0mUoOfzRrMwfXXwgPUA==", "peer": true }, "throttleit": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz", + "integrity": "sha512-rkTVqu6IjfQ/6+uNuuc3sZek4CEYxTJom3IktzgdSxcZqdARuebbA/f4QmAxMQIxqq9ZLEUkSYqvuk1I6VKq4g==", "dev": true }, "through": { "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, "tmp": { "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", "dev": true, "requires": { "rimraf": "^3.0.0" @@ -9750,6 +11265,8 @@ }, "to-regex-range": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "requires": { "is-number": "^7.0.0" @@ -9757,6 +11274,8 @@ }, "tough-cookie": { "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", "dev": true, "requires": { "psl": "^1.1.28", @@ -9764,18 +11283,26 @@ } }, "tr46": { - "version": "0.0.3" + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, "tree-kill": { "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", "dev": true }, "trough": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.1.0.tgz", + "integrity": "sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==", "dev": true }, "ts-mocha": { "version": "9.0.2", + "resolved": "https://registry.npmjs.org/ts-mocha/-/ts-mocha-9.0.2.tgz", + "integrity": "sha512-WyQjvnzwrrubl0JT7EC1yWmNpcsU3fOuBFfdps30zbmFBgKniSaSOyZMZx+Wq7kytUs5CY+pEbSYEbGfIKnXTw==", "dev": true, "requires": { "ts-node": "7.0.1", @@ -9784,18 +11311,14 @@ "dependencies": { "diff": { "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", "dev": true }, - "json5": { - "version": "1.0.1", - "dev": true, - "optional": true, - "requires": { - "minimist": "^1.2.0" - } - }, "mkdirp": { "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, "requires": { "minimist": "^1.2.6" @@ -9803,6 +11326,8 @@ }, "ts-node": { "version": "7.0.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz", + "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==", "dev": true, "requires": { "arrify": "^1.0.0", @@ -9815,25 +11340,18 @@ "yn": "^2.0.0" } }, - "tsconfig-paths": { - "version": "3.14.1", - "dev": true, - "optional": true, - "requires": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, "yn": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", + "integrity": "sha512-uTv8J/wiWTgUTg+9vLTi//leUl5vDQS6uii/emeTb2ssY7vl6QWf2fFbIIGjnhjvbdKlU0ed7QPgY1htTC86jQ==", "dev": true } } }, "ts-node": { "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", "dev": true, "requires": { "@cspotcode/source-map-support": "^0.8.0", @@ -9853,12 +11371,16 @@ "dependencies": { "diff": { "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true } } }, "tsc-alias": { "version": "1.7.0", + "resolved": "https://registry.npmjs.org/tsc-alias/-/tsc-alias-1.7.0.tgz", + "integrity": "sha512-n/K6g8S7Ec7Y/A2Z77Ikp2Uv1S1ERtT63ni69XV4W1YPT4rnNmz8ItgIiJYvKfFnKfqcZQ81UPjoKpMTxaC/rg==", "dev": true, "requires": { "chokidar": "^3.5.3", @@ -9871,16 +11393,47 @@ "dependencies": { "commander": { "version": "9.4.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.0.tgz", + "integrity": "sha512-sRPT+umqkz90UA8M1yqYfnHlZA7fF6nSphDtxeywPZ49ysjxDQybzk13CL+mXekDRG92skbcqCLVovuCusNmFw==", "dev": true } } }, + "tsconfig-paths": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "dev": true, + "optional": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "optional": true, + "requires": { + "minimist": "^1.2.0" + } + } + } + }, "tslib": { "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", "dev": true }, "tunnel-agent": { "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", "dev": true, "requires": { "safe-buffer": "^5.0.1" @@ -9888,10 +11441,14 @@ }, "tweetnacl": { "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", "dev": true }, "txm": { "version": "8.0.2", + "resolved": "https://registry.npmjs.org/txm/-/txm-8.0.2.tgz", + "integrity": "sha512-X6gUOeUAXzIrUvxp0zvOiJBM0l6Zk8NexaYugac6c6PW7dbrjgz6zC/rfSEjtd29VejI3VKvAnc6pDdOfSNKug==", "dev": true, "requires": { "async": "^3.2.1", @@ -9903,38 +11460,54 @@ }, "dependencies": { "supports-color": { - "version": "9.2.2", + "version": "9.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.2.3.tgz", + "integrity": "sha512-aszYUX/DVK/ed5rFLb/dDinVJrQjG/vmU433wtqVSD800rYsJNWxh2R3USV90aLSU+UsyQkbNeffVLzc6B6foA==", "dev": true } } }, "type-fest": { "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true }, "typescript": { - "version": "4.7.4", + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.3.tgz", + "integrity": "sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==", "dev": true }, "typical": { "version": "2.6.1", + "resolved": "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz", + "integrity": "sha512-ofhi8kjIje6npGozTip9Fr8iecmYfEbS06i0JnIg+rh51KakryWF4+jX8lLKZVhy6N+ID45WYSFCxPOdTWCzNg==", "dev": true }, "uc.micro": { "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", "dev": true }, "uglify-js": { - "version": "3.16.3", + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.0.tgz", + "integrity": "sha512-aTeNPVmgIMPpm1cxXr2Q/nEbvkmV8yq66F3om7X3P/cvOXQ0TMQ64Wk63iyT1gPlmdmGzjGpyLh1f3y8MZWXGg==", "dev": true, "optional": true }, "underscore": { "version": "1.13.4", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.4.tgz", + "integrity": "sha512-BQFnUDuAQ4Yf/cYY5LNrK9NCJFKriaRbD9uR1fTeXnBeoa97W0i41qkZfGO9pSo8I5KzjAcSY2XYtdf0oKd7KQ==", "dev": true }, "unified": { "version": "10.1.2", + "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz", + "integrity": "sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==", "dev": true, "requires": { "@types/unist": "^2.0.0", @@ -9948,6 +11521,8 @@ }, "union": { "version": "0.5.0", + "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", + "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==", "dev": true, "requires": { "qs": "^6.4.0" @@ -9955,6 +11530,8 @@ }, "unist-util-stringify-position": { "version": "3.0.2", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.2.tgz", + "integrity": "sha512-7A6eiDCs9UtjcwZOcCpM4aPII3bAAGv13E96IkawkOAW0OhH+yRxtY0lzo8KiHpzEMfH7Q+FizUmwp8Iqy5EWg==", "dev": true, "requires": { "@types/unist": "^2.0.0" @@ -9962,14 +11539,20 @@ }, "universalify": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", "dev": true }, "untildify": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", "dev": true }, "update-browserslist-db": { - "version": "1.0.5", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz", + "integrity": "sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg==", "dev": true, "requires": { "escalade": "^3.1.1", @@ -9978,6 +11561,8 @@ }, "uri-js": { "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "requires": { "punycode": "^2.1.0" @@ -9985,14 +11570,20 @@ }, "url-join": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", "dev": true }, "uuid": { "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true }, "uvu": { "version": "0.5.6", + "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz", + "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", "dev": true, "requires": { "dequal": "^2.0.0", @@ -10003,10 +11594,14 @@ }, "v8-compile-cache-lib": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true }, "verror": { "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", "dev": true, "requires": { "assert-plus": "^1.0.0", @@ -10015,7 +11610,9 @@ } }, "vfile": { - "version": "5.3.4", + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.5.tgz", + "integrity": "sha512-U1ho2ga33eZ8y8pkbQLH54uKqGhFJ6GYIHnnG5AhRpAh3OWjkrRHKa/KogbmQn8We+c0KVV3rTOgR9V/WowbXQ==", "dev": true, "requires": { "@types/unist": "^2.0.0", @@ -10026,6 +11623,8 @@ }, "vfile-message": { "version": "3.1.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.2.tgz", + "integrity": "sha512-QjSNP6Yxzyycd4SVOtmKKyTsSvClqBPJcd00Z0zuPj3hOIjg0rUPG6DbFGPvUKRgYyaIWLPKpuEclcuvb3H8qA==", "dev": true, "requires": { "@types/unist": "^2.0.0", @@ -10034,10 +11633,14 @@ }, "walk-back": { "version": "5.1.0", + "resolved": "https://registry.npmjs.org/walk-back/-/walk-back-5.1.0.tgz", + "integrity": "sha512-Uhxps5yZcVNbLEAnb+xaEEMdgTXl9qAQDzKYejG2AZ7qPwRQ81lozY9ECDbjLPNWm7YsO1IK5rsP1KoQzXAcGA==", "dev": true }, "wasm-opt": { "version": "1.3.0", + "resolved": "https://registry.npmjs.org/wasm-opt/-/wasm-opt-1.3.0.tgz", + "integrity": "sha512-24+IOboX4Sav0bI8Krwf0Y6dnpN4KxYtqpl0qWt86qVLsmayUqx1KMBrJTlQWNC+/dsqzQJjK6QvxNJsZYOgJg==", "dev": true, "requires": { "node-fetch": "^2.6.7", @@ -10046,6 +11649,8 @@ }, "wasm-pack": { "version": "0.10.1", + "resolved": "https://registry.npmjs.org/wasm-pack/-/wasm-pack-0.10.1.tgz", + "integrity": "sha512-bw480KaaJQhL6UX8wAm6YCO497uaULDrsvUey3EB86dKAfFc6qCGVN1kItcwUklEeufonwo9mwz9/fy9xLOPtQ==", "dev": true, "requires": { "binary-install": "^0.1.0" @@ -10053,6 +11658,8 @@ }, "watchpack": { "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", "dev": true, "requires": { "glob-to-regexp": "^0.4.1", @@ -10060,10 +11667,14 @@ } }, "webidl-conversions": { - "version": "3.0.1" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "webpack": { "version": "5.74.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz", + "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==", "dev": true, "requires": { "@types/eslint-scope": "^3.7.3", @@ -10094,6 +11705,8 @@ }, "webpack-cli": { "version": "4.10.0", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", + "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", "dev": true, "requires": { "@discoveryjs/json-ext": "^0.5.0", @@ -10112,12 +11725,16 @@ "dependencies": { "commander": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", "dev": true } } }, "webpack-merge": { "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", "dev": true, "requires": { "clone-deep": "^4.0.1", @@ -10126,10 +11743,14 @@ }, "webpack-sources": { "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", "dev": true }, "whatwg-encoding": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", "dev": true, "requires": { "iconv-lite": "0.6.3" @@ -10137,6 +11758,8 @@ }, "whatwg-url": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "requires": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -10144,6 +11767,8 @@ }, "which": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "requires": { "isexe": "^2.0.0" @@ -10151,39 +11776,68 @@ }, "which-module": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", "dev": true }, "wide-align": { "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dev": true, "requires": { "string-width": "^1.0.2 || 2" }, "dependencies": { + "ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "dev": true + }, "is-fullwidth-code-point": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", "dev": true }, "string-width": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^4.0.0" } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } } } }, "wildcard": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", "dev": true }, "wordwrap": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", "dev": true }, "wordwrapjs": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-3.0.0.tgz", + "integrity": "sha512-mO8XtqyPvykVCsrwj5MlOVWvSnCdT+C+QVbm6blradR7JExAhbkZ7hZ9A+9NUtwzSqrlUo9a67ws0EiILrvRpw==", "dev": true, "requires": { "reduce-flatten": "^1.0.1", @@ -10192,54 +11846,57 @@ "dependencies": { "reduce-flatten": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-1.0.1.tgz", + "integrity": "sha512-j5WfFJfc9CoXv/WbwVLHq74i/hdTUpy+iNC534LxczMRP67vJeK3V9JOdnL0N1cIRbn9mYhE2yVjvvKXDxvNXQ==", "dev": true } } }, "workerpool": { "version": "6.2.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", + "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", "dev": true }, "wrap-ansi": { "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "requires": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "dev": true - }, - "strip-ansi": { - "version": "6.0.1", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - } } }, "wrappy": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, "xmlcreate": { "version": "2.0.4", + "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", + "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", "dev": true }, "y18n": { "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true }, "yallist": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, "yargs": { "version": "17.5.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", + "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", "dev": true, "requires": { "cliui": "^7.0.2", @@ -10253,16 +11910,22 @@ "dependencies": { "yargs-parser": { "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true } } }, "yargs-parser": { "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", "dev": true }, "yargs-unparser": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", "dev": true, "requires": { "camelcase": "^6.0.0", @@ -10273,12 +11936,16 @@ "dependencies": { "is-plain-obj": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true } } }, "yauzl": { "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", "dev": true, "requires": { "buffer-crc32": "~0.2.3", @@ -10287,10 +11954,14 @@ }, "yn": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true }, "yocto-queue": { "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true } } diff --git a/bindings/wasm/src/resolver/constructor_input.rs b/bindings/wasm/src/resolver/constructor_input.rs index 54c3c1fb52..a8758568df 100644 --- a/bindings/wasm/src/resolver/constructor_input.rs +++ b/bindings/wasm/src/resolver/constructor_input.rs @@ -30,7 +30,7 @@ const HANDLERS: &'static str = #[wasm_bindgen(typescript_custom_section)] const TS_RESOLVER_CONFIG: &'static str = r#" /** - * Configurations for the new {@link MixedResolver}. + * Configurations for the new {@link Resolver}. */ export type ResolverConfig = { diff --git a/bindings/wasm/src/resolver/mod.rs b/bindings/wasm/src/resolver/mod.rs index 54d851436c..58e2e8e12a 100644 --- a/bindings/wasm/src/resolver/mod.rs +++ b/bindings/wasm/src/resolver/mod.rs @@ -1,9 +1,8 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -//mod function_transformation; mod constructor_input; -mod mixed_resolver; mod supported_document_types; +mod wasm_resolver; pub use supported_document_types::*; diff --git a/bindings/wasm/src/resolver/mixed_resolver.rs b/bindings/wasm/src/resolver/wasm_resolver.rs similarity index 97% rename from bindings/wasm/src/resolver/mixed_resolver.rs rename to bindings/wasm/src/resolver/wasm_resolver.rs index b3b62b0a8e..a50bd84e5e 100644 --- a/bindings/wasm/src/resolver/mixed_resolver.rs +++ b/bindings/wasm/src/resolver/wasm_resolver.rs @@ -47,18 +47,18 @@ use wasm_bindgen_futures::future_to_promise; /// /// # Configuration /// The resolver will only be able to resolve DID documents for methods it has been configured for in the constructor. -#[wasm_bindgen(js_name = MixedResolver)] -pub struct MixedResolver(Rc); +#[wasm_bindgen(js_name = Resolver)] +pub struct WasmResolver(Rc); -#[wasm_bindgen(js_class = MixedResolver)] -impl MixedResolver { - /// Constructs a new `MixedResolver`. +#[wasm_bindgen(js_class = Resolver)] +impl WasmResolver { + /// Constructs a new `Resolver`. /// /// # Errors /// If both a `client` is given and the `handlers` map contains the "iota" key the construction process /// will throw an error as it is then ambiguous what should be . #[wasm_bindgen(constructor)] - pub fn new(config: ResolverConfig) -> Result { + pub fn new(config: ResolverConfig) -> Result { let mut resolver: SingleThreadedResolver = SingleThreadedResolver::new(); let mut attached_iota_method = false; @@ -128,7 +128,7 @@ impl MixedResolver { *attached_iota_method = true; } - MixedResolver::attach_handler(resolver, did_method, handler); + WasmResolver::attach_handler(resolver, did_method, handler); } else { Err(WasmError::new( Cow::Borrowed("ResolverError::ConstructionError"), diff --git a/bindings/wasm/tests/resolver.ts b/bindings/wasm/tests/resolver.ts index 965085846b..772ebe90a0 100644 --- a/bindings/wasm/tests/resolver.ts +++ b/bindings/wasm/tests/resolver.ts @@ -3,7 +3,7 @@ export {}; import { CoreDocument, IotaDID, - MixedResolver, + Resolver, Presentation, IotaDocument, CoreDID, @@ -57,7 +57,7 @@ describe("Resolver", function () { handlerMap.set("foo", resolveDidFoo); handlerMap.set("bar", resolveDidBar); - const resolver = new MixedResolver({ + const resolver = new Resolver({ handlers: handlerMap }); @@ -140,7 +140,7 @@ describe("Resolver", function () { handlerMap.set("foo", resolveDidFooMisconfigured); handlerMap.set("bar", resolveDidBarMisconfigured); - const resolver = new MixedResolver({ + const resolver = new Resolver({ handlers: handlerMap }); diff --git a/identity_credential/Cargo.toml b/identity_credential/Cargo.toml index 4e386489c7..5440702cc4 100644 --- a/identity_credential/Cargo.toml +++ b/identity_credential/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["iota", "tangle", "identity"] license = "Apache-2.0" readme = "./README.md" repository = "https://github.com/iotaledger/identity.rs" -rust-version = "1.60" +rust-version = "1.62" description = "An implementation of the Verifiable Credentials standard." [dependencies] diff --git a/identity_credential/src/validator/credential_validator.rs b/identity_credential/src/validator/credential_validator.rs index 59c999f51f..7d62adc216 100644 --- a/identity_credential/src/validator/credential_validator.rs +++ b/identity_credential/src/validator/credential_validator.rs @@ -81,18 +81,16 @@ impl CredentialValidator { /// Validate that the [`Credential`] expires on or after the specified [`Timestamp`]. pub fn check_expires_on_or_after(credential: &Credential, timestamp: Timestamp) -> ValidationUnitResult { - let is_ok = if let Some(expiration_date) = credential.expiration_date { - expiration_date >= timestamp - } else { - true - }; - is_ok.then(|| ()).ok_or(ValidationError::ExpirationDate) + let expiration_date: Option = credential.expiration_date; + (expiration_date.is_none() || expiration_date >= Some(timestamp)) + .then_some(()) + .ok_or(ValidationError::ExpirationDate) } /// Validate that the [`Credential`] is issued on or before the specified [`Timestamp`]. pub fn check_issued_on_or_before(credential: &Credential, timestamp: Timestamp) -> ValidationUnitResult { (credential.issuance_date <= timestamp) - .then(|| ()) + .then_some(()) .ok_or(ValidationError::IssuanceDate) } @@ -145,19 +143,16 @@ impl CredentialValidator { } }; - let is_ok: bool = match relationship { - SubjectHolderRelationship::AlwaysSubject => url_matches, - SubjectHolderRelationship::SubjectOnNonTransferable => { - url_matches || !credential.non_transferable.unwrap_or(false) - } - SubjectHolderRelationship::Any => true, - }; - - if is_ok { - Ok(()) - } else { - Err(ValidationError::SubjectHolderRelationship) - } + Some(relationship) + .filter(|relationship| match relationship { + SubjectHolderRelationship::AlwaysSubject => url_matches, + SubjectHolderRelationship::SubjectOnNonTransferable => { + url_matches || !credential.non_transferable.unwrap_or(false) + } + SubjectHolderRelationship::Any => true, + }) + .map(|_| ()) + .ok_or(ValidationError::SubjectHolderRelationship) } /// Checks whether the credential status has been revoked. From bea72400b578fc6813b9a0109c9e844e2f53898f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 16 Sep 2022 15:07:16 +0200 Subject: [PATCH 65/89] changelog and versions (#1015) Co-authored-by: Identity Bot --- CHANGELOG.md | 42 ++++++++++++++++++++++++++ bindings/stronghold-nodejs/Cargo.toml | 8 ++--- bindings/wasm/Cargo.toml | 2 +- examples/Cargo.toml | 2 +- examples_legacy/Cargo.toml | 2 +- identity_account/Cargo.toml | 6 ++-- identity_account_storage/Cargo.toml | 4 +-- identity_agent/Cargo.toml | 4 +-- identity_core/Cargo.toml | 4 +-- identity_credential/Cargo.toml | 6 ++-- identity_did/Cargo.toml | 4 +-- identity_diff/Cargo.toml | 4 +-- identity_diff/derive/Cargo.toml | 2 +- identity_iota/Cargo.toml | 14 ++++----- identity_iota_client_legacy/Cargo.toml | 6 ++-- identity_iota_core/Cargo.toml | 8 ++--- identity_iota_core_legacy/Cargo.toml | 4 +-- identity_resolver/Cargo.toml | 10 +++--- 18 files changed, 87 insertions(+), 45 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c5726f994b..bc02303f4a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,47 @@ # Changelog +## [v0.7.0-alpha.1](https://github.com/iotaledger/identity.rs/tree/v0.7.0-alpha.1) (2022-09-16) + +[Full Changelog](https://github.com/iotaledger/identity.rs/compare/v0.6.0...v0.7.0-alpha.1) + +This version introduces a new DID method targeting the IOTA UTXO ledger. This method works fundamentally differently from the previous method and introduces new capabilities to interact with Layer 1 entities like native tokens, NFTs and smart contracts. + + This is an early alpha release, so there may be breaking changes in upcoming versions that invalidate DIDs created with this version. The method at this point is only intended for experimentation. + + Note: Identities created with the earlier versions cannot be resolved with this version of the library. + + + +### Changed + +- Chore/rename mixed resolver [\#1026](https://github.com/iotaledger/identity.rs/pull/1026) +- Add length prefix to DID Document payloads [\#1010](https://github.com/iotaledger/identity.rs/pull/1010) +- Feature-gate `Resolver` [\#1007](https://github.com/iotaledger/identity.rs/pull/1007) +- Rename `Stardust` types to `Iota` [\#1000](https://github.com/iotaledger/identity.rs/pull/1000) +- Change Stardust DID method to IOTA [\#982](https://github.com/iotaledger/identity.rs/pull/982) +- Add Wasm Stardust Client [\#975](https://github.com/iotaledger/identity.rs/pull/975) +- Generalized Resolver [\#970](https://github.com/iotaledger/identity.rs/pull/970) +- Change `Storage` to handle `CoreDID` [\#968](https://github.com/iotaledger/identity.rs/pull/968) +- Feature-gate `iota-client` dependency, integrate `StardustDID` [\#958](https://github.com/iotaledger/identity.rs/pull/958) +- Change `Storage` to store arbitrary blobs [\#953](https://github.com/iotaledger/identity.rs/pull/953) +- Add `StardustDocumentMetadata`, implement `StardustDocument` methods [\#951](https://github.com/iotaledger/identity.rs/pull/951) +- Fix stack overflow in `CoreDID` `PartialEq` impl [\#946](https://github.com/iotaledger/identity.rs/pull/946) +- Change `Service` `type` field to allow sets [\#944](https://github.com/iotaledger/identity.rs/pull/944) +- Generalise `CredentialValidator`, `PresentationValidator` to support arbitrary DID Documents [\#935](https://github.com/iotaledger/identity.rs/pull/935) + +### Added + +- Add Stardust Client Extension Trait [\#963](https://github.com/iotaledger/identity.rs/pull/963) +- Add StardustDID [\#949](https://github.com/iotaledger/identity.rs/pull/949) +- State metadata serialization for the stardust DID method [\#947](https://github.com/iotaledger/identity.rs/pull/947) +- Stardust DID Method Proof-of-Concept [\#940](https://github.com/iotaledger/identity.rs/pull/940) +- Implement the Identity Agent [\#322](https://github.com/iotaledger/identity.rs/pull/322) + +### Patch + +- Support case insensitive serialization of `RentStructure` [\#1012](https://github.com/iotaledger/identity.rs/pull/1012) +- Update stronghold to 0.6.4 [\#928](https://github.com/iotaledger/identity.rs/pull/928) + ## [v0.6.0](https://github.com/iotaledger/identity.rs/tree/v0.6.0) (2022-06-15) [Full Changelog](https://github.com/iotaledger/identity.rs/compare/v0.5.0...v0.6.0) diff --git a/bindings/stronghold-nodejs/Cargo.toml b/bindings/stronghold-nodejs/Cargo.toml index 97f674e323..dba768976f 100644 --- a/bindings/stronghold-nodejs/Cargo.toml +++ b/bindings/stronghold-nodejs/Cargo.toml @@ -7,10 +7,10 @@ publish = false crate-type = ["cdylib"] [dependencies] -identity_account_storage = { version = "=0.6.0", path = "../../identity_account_storage", default-features = false, features = ["stronghold", "send-sync-storage", "encryption"] } -identity_core = { version = "=0.6.0", path = "../../identity_core", default-features = false } -identity_did = { version = "=0.6.0", path = "../../identity_did", default-features = false } -identity_iota_core_legacy = { version = "=0.6.0", path = "../../identity_iota_core_legacy", default-features = false } +identity_account_storage = { version = "0.6.0", path = "../../identity_account_storage", default-features = false, features = ["stronghold", "send-sync-storage", "encryption"] } +identity_core = { version = "0.7.0-alpha.1", path = "../../identity_core", default-features = false } +identity_did = { version = "0.7.0-alpha.1", path = "../../identity_did", default-features = false } +identity_iota_core_legacy = { version = "0.6.0", path = "../../identity_iota_core_legacy", default-features = false } napi = { version = "2.4.3", default-features = false, features = ["napi4", "tokio_rt", "serde-json"] } napi-derive = { version = "2.4.1", default-features = false, features = ["compat-mode", "full"] } serde = { version = "1.0", default-features = false, features = ["derive"] } diff --git a/bindings/wasm/Cargo.toml b/bindings/wasm/Cargo.toml index d21f9649fe..b7b109d445 100644 --- a/bindings/wasm/Cargo.toml +++ b/bindings/wasm/Cargo.toml @@ -30,7 +30,7 @@ wasm-bindgen = { version = "0.2.82", features = ["serde-serialize"] } wasm-bindgen-futures = { version = "0.4", default-features = false } [dependencies.identity_iota] -version = "=0.6.0" +version = "0.7.0-alpha.1" path = "../../identity_iota" default-features = false features = ["client", "revocation-bitmap", "resolver"] diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 6b57c7acc0..15da7be4b6 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "examples" -version = "0.6.0" +version = "0.7.0-alpha.1" authors = ["IOTA Stiftung"] edition = "2021" publish = false diff --git a/examples_legacy/Cargo.toml b/examples_legacy/Cargo.toml index d561521a1f..06ebf414d5 100644 --- a/examples_legacy/Cargo.toml +++ b/examples_legacy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "examples_legacy" -version = "0.6.0" +version = "0.7.0-alpha.1" edition = "2021" publish = false diff --git a/identity_account/Cargo.toml b/identity_account/Cargo.toml index d7aa74b873..33bee24b74 100644 --- a/identity_account/Cargo.toml +++ b/identity_account/Cargo.toml @@ -13,9 +13,9 @@ description = "High-level interface for managing IOTA DID Documents." [dependencies] identity_account_storage = { version = "=0.6.0", path = "../identity_account_storage", default-features = false } -identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } -identity_credential = { version = "=0.6.0", path = "../identity_credential", default-features = false } -identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } +identity_core = { version = "=0.7.0-alpha.1", path = "../identity_core", default-features = false } +identity_credential = { version = "=0.7.0-alpha.1", path = "../identity_credential", default-features = false } +identity_did = { version = "=0.7.0-alpha.1", path = "../identity_did", default-features = false } identity_iota_client_legacy = { version = "=0.6.0", path = "../identity_iota_client_legacy", default-features = false } identity_iota_core_legacy = { version = "=0.6.0", path = "../identity_iota_core_legacy", default-features = false } log = { version = "0.4", default-features = false } diff --git a/identity_account_storage/Cargo.toml b/identity_account_storage/Cargo.toml index b08a86ba91..bd25876963 100644 --- a/identity_account_storage/Cargo.toml +++ b/identity_account_storage/Cargo.toml @@ -17,8 +17,8 @@ async-trait = { version = "0.1", default-features = false } function_name = { version = "0.2", default-features = false, optional = true } futures = { version = "0.3", optional = true } hashbrown = { version = "0.11", features = ["serde"] } -identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } -identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } +identity_core = { version = "=0.7.0-alpha.1", path = "../identity_core", default-features = false } +identity_did = { version = "=0.7.0-alpha.1", path = "../identity_did", default-features = false } identity_iota_core_legacy = { version = "=0.6.0", path = "../identity_iota_core_legacy", default-features = false } iota-crypto = { version = "0.12.1", default-features = false, features = ["hmac", "pbkdf", "sha", "std", "aes-gcm", "aes-kw"] } iota_stronghold = { version = "0.6.4", default-features = false, features = ["std"], optional = true } diff --git a/identity_agent/Cargo.toml b/identity_agent/Cargo.toml index 7436b0cf2a..6cfaa9c93d 100644 --- a/identity_agent/Cargo.toml +++ b/identity_agent/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity_agent" -version = "0.6.0" +version = "0.7.0-alpha.1" authors = ["IOTA Stiftung"] edition = "2021" homepage = "https://www.iota.org" @@ -14,7 +14,7 @@ description = "A peer-to-peer communication framework for building SSI agents on async-trait = { version = "0.1", default-features = false } dashmap = { version = "5.3", default-features = false } futures = { version = "0.3", default-features = false } -identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } +identity_core = { version = "=0.7.0-alpha.1", path = "../identity_core", default-features = false } libp2p = { version = "0.45", default-features = false, features = ["tcp-tokio", "dns-tokio", "websocket", "request-response", "noise", "yamux"] } log = { version = "0.4", default-features = false } serde = { version = "1.0", default-features = false, features = ["derive"] } diff --git a/identity_core/Cargo.toml b/identity_core/Cargo.toml index a76819477c..8474e2d304 100644 --- a/identity_core/Cargo.toml +++ b/identity_core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity_core" -version = "0.6.0" +version = "0.7.0-alpha.1" authors = ["IOTA Stiftung"] edition = "2021" homepage = "https://www.iota.org" @@ -11,7 +11,7 @@ repository = "https://github.com/iotaledger/identity.rs" description = "The core traits and types for the identity-rs library." [dependencies] -identity-diff = { version = "=0.6.0", path = "../identity_diff", default-features = false } +identity-diff = { version = "=0.7.0-alpha.1", path = "../identity_diff", default-features = false } multibase = { version = "0.9", default-features = false, features = ["std"] } serde = { version = "1.0", default-features = false, features = ["std", "derive"] } serde_jcs = { version = "0.1", default-features = false } diff --git a/identity_credential/Cargo.toml b/identity_credential/Cargo.toml index 5440702cc4..ae86d78ca8 100644 --- a/identity_credential/Cargo.toml +++ b/identity_credential/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity_credential" -version = "0.6.0" +version = "0.7.0-alpha.1" authors = ["IOTA Stiftung"] edition = "2021" homepage = "https://www.iota.org" @@ -13,8 +13,8 @@ description = "An implementation of the Verifiable Credentials standard." [dependencies] erased-serde = { version = "0.3.21", default-features = false, features = ["std"], optional = true } -identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } -identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } +identity_core = { version = "=0.7.0-alpha.1", path = "../identity_core", default-features = false } +identity_did = { version = "=0.7.0-alpha.1", path = "../identity_did", default-features = false } itertools = { version = "0.10", default-features = false, features = ["use_std"], optional = true } lazy_static = { version = "1.4", default-features = false } serde = { version = "1.0", default-features = false, features = ["std", "derive"] } diff --git a/identity_did/Cargo.toml b/identity_did/Cargo.toml index cc57752cc4..70ffea7053 100644 --- a/identity_did/Cargo.toml +++ b/identity_did/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity_did" -version = "0.6.0" +version = "0.7.0-alpha.1" authors = ["IOTA Stiftung"] edition = "2021" homepage = "https://www.iota.org" @@ -15,7 +15,7 @@ dataurl = { version = "0.1.2", default-features = false, optional = true } did_url = { version = "0.1", default-features = false, features = ["std", "serde"] } flate2 = { version = "1.0.23", default-features = false, features = ["rust_backend"], optional = true } form_urlencoded = { version = "1.0.1", default-features = false } -identity_core = { version = "=0.6.0", path = "../identity_core" } +identity_core = { version = "=0.7.0-alpha.1", path = "../identity_core" } indexmap = { version = "1.7", default-features = false, features = ["std", "serde-1"] } roaring = { version = "0.9.0", default-features = false, optional = true } serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] } diff --git a/identity_diff/Cargo.toml b/identity_diff/Cargo.toml index 3c92f6a4a3..71a84a12d5 100644 --- a/identity_diff/Cargo.toml +++ b/identity_diff/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity-diff" -version = "0.6.0" +version = "0.7.0-alpha.1" authors = ["IOTA Stiftung"] edition = "2021" homepage = "https://www.iota.org" @@ -11,7 +11,7 @@ repository = "https://github.com/iotaledger/identity.rs" description = "`Diff` trait to compute and merge data structure differences." [dependencies] -identity-diff-derive = { version = "=0.6.0", path = "derive", optional = true } +identity-diff-derive = { version = "=0.7.0-alpha.1", path = "derive", optional = true } serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0" } strum = { version = "0.24.0", default-features = false, features = ["std", "derive"] } diff --git a/identity_diff/derive/Cargo.toml b/identity_diff/derive/Cargo.toml index 9dd3bb32d4..5489e18976 100644 --- a/identity_diff/derive/Cargo.toml +++ b/identity_diff/derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity-diff-derive" -version = "0.6.0" +version = "0.7.0-alpha.1" authors = ["IOTA Stiftung"] edition = "2021" homepage = "https://www.iota.org" diff --git a/identity_iota/Cargo.toml b/identity_iota/Cargo.toml index 8ae283cf10..a958ef4ce8 100644 --- a/identity_iota/Cargo.toml +++ b/identity_iota/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity_iota" -version = "0.6.0" +version = "0.7.0-alpha.1" authors = ["IOTA Stiftung"] edition = "2021" homepage = "https://www.iota.org" @@ -12,12 +12,12 @@ rust-version = "1.62" description = "Framework for Self-Sovereign Identity with IOTA DID." [dependencies] -identity_agent = { version = "=0.6.0", path = "../identity_agent", default-features = false, optional = true } -identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } -identity_credential = { version = "=0.6.0", path = "../identity_credential", features = ["validator"], default-features = false } -identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } -identity_iota_core = { version = "=0.6.0", path = "../identity_iota_core", default-features = false } -identity_resolver = { version = "=0.6.0", path = "../identity_resolver", default-features = false, optional = true } +identity_agent = { version = "=0.7.0-alpha.1", path = "../identity_agent", default-features = false, optional = true } +identity_core = { version = "=0.7.0-alpha.1", path = "../identity_core", default-features = false } +identity_credential = { version = "=0.7.0-alpha.1", path = "../identity_credential", features = ["validator"], default-features = false } +identity_did = { version = "=0.7.0-alpha.1", path = "../identity_did", default-features = false } +identity_iota_core = { version = "=0.7.0-alpha.1", path = "../identity_iota_core", default-features = false } +identity_resolver = { version = "=0.7.0-alpha.1", path = "../identity_resolver", default-features = false, optional = true } [dev-dependencies] anyhow = "1.0.64" diff --git a/identity_iota_client_legacy/Cargo.toml b/identity_iota_client_legacy/Cargo.toml index 94a02fff7d..5a2bb82087 100644 --- a/identity_iota_client_legacy/Cargo.toml +++ b/identity_iota_client_legacy/Cargo.toml @@ -17,9 +17,9 @@ bee-rest-api = { version = "0.1.7", default-features = false } brotli = { version = "3.3", default-features = false, features = ["std"] } form_urlencoded = { version = "1.0" } futures = { version = "0.3" } -identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } -identity_credential = { version = "=0.6.0", path = "../identity_credential", default-features = false, features = ["validator"] } -identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } +identity_core = { version = "=0.7.0-alpha.1", path = "../identity_core", default-features = false } +identity_credential = { version = "=0.7.0-alpha.1", path = "../identity_credential", default-features = false, features = ["validator"] } +identity_did = { version = "=0.7.0-alpha.1", path = "../identity_did", default-features = false } identity_iota_core_legacy = { version = "=0.6.0", path = "../identity_iota_core_legacy", default-features = false } itertools = { version = "0.10" } lazy_static = { version = "1.4", default-features = false } diff --git a/identity_iota_core/Cargo.toml b/identity_iota_core/Cargo.toml index 90aa0e211d..ba0f893329 100644 --- a/identity_iota_core/Cargo.toml +++ b/identity_iota_core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity_iota_core" -version = "0.6.0" +version = "0.7.0-alpha.1" authors = ["IOTA Stiftung"] edition = "2021" homepage = "https://www.iota.org" @@ -14,9 +14,9 @@ description = "An IOTA Ledger integration for the IOTA DID Method." [dependencies] # Ensure bee-block always matches the version used by iota-client. bee-block = { version = "1.0.0-beta.7", default-features = false, features = ["std"], optional = true } -identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } -identity_credential = { version = "=0.6.0", path = "../identity_credential", default-features = false, features = ["validator"] } -identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } +identity_core = { version = "=0.7.0-alpha.1", path = "../identity_core", default-features = false } +identity_credential = { version = "=0.7.0-alpha.1", path = "../identity_credential", default-features = false, features = ["validator"] } +identity_did = { version = "=0.7.0-alpha.1", path = "../identity_did", default-features = false } async-trait = { version = "0.1.56", default-features = false, optional = true } futures = { version = "0.3" } diff --git a/identity_iota_core_legacy/Cargo.toml b/identity_iota_core_legacy/Cargo.toml index 97472094e9..53c8a1d90b 100644 --- a/identity_iota_core_legacy/Cargo.toml +++ b/identity_iota_core_legacy/Cargo.toml @@ -13,8 +13,8 @@ description = "Core data structures for the IOTA DID Method." [dependencies] bee-message = { version = "0.1.6", default-features = false, features = ["serde"] } -identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } -identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } +identity_core = { version = "=0.7.0-alpha.1", path = "../identity_core", default-features = false } +identity_did = { version = "=0.7.0-alpha.1", path = "../identity_did", default-features = false } lazy_static = { version = "1.4", default-features = false } serde = { version = "1.0", default-features = false, features = ["std", "derive"] } strum = { version = "0.24.0", default-features = false, features = ["std", "derive"] } diff --git a/identity_resolver/Cargo.toml b/identity_resolver/Cargo.toml index 3fdcfa4769..82f3fbc02d 100644 --- a/identity_resolver/Cargo.toml +++ b/identity_resolver/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity_resolver" -version = "0.6.0" +version = "0.7.0-alpha.1" authors = ["IOTA Stiftung"] edition = "2021" homepage = "https://www.iota.org" @@ -15,15 +15,15 @@ description = "DID Resolution utilities for the identity.rs library." # This is currently necessary for the ResolutionHandler trait. This can be made an optional dependency if alternative ways of attaching handlers are introduced. async-trait = { version = "0.1", default-features = false } futures = { version = "0.3" } -identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } -identity_credential = { version = "=0.6.0", path = "../identity_credential", default-features = false, features = ["validator"] } -identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } +identity_core = { version = "=0.7.0-alpha.1", path = "../identity_core", default-features = false } +identity_credential = { version = "=0.7.0-alpha.1", path = "../identity_credential", default-features = false, features = ["validator"] } +identity_did = { version = "=0.7.0-alpha.1", path = "../identity_did", default-features = false } serde = { version = "1.0", default-features = false, features = ["std", "derive"] } strum = { version = "0.21", features = ["derive"] } thiserror = { version = "1.0", default-features = false } [dependencies.identity_iota_core] -version = "=0.6.0" +version = "=0.7.0-alpha.1" path = "../identity_iota_core" default-features = false features = ["send-sync-client-ext", "iota-client"] From b8a2b38caa6cf467318adb41d2f72baec746ce98 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 16 Sep 2022 15:54:48 +0200 Subject: [PATCH 66/89] changelog and versions (#1014) Co-authored-by: Identity Bot --- bindings/stronghold-nodejs/Cargo.toml | 2 +- bindings/stronghold-nodejs/package-lock.json | 4 +-- bindings/stronghold-nodejs/package.json | 4 +-- bindings/wasm/CHANGELOG.md | 37 ++++++++++++++++++++ bindings/wasm/Cargo.toml | 2 +- bindings/wasm/package-lock.json | 4 +-- bindings/wasm/package.json | 2 +- 7 files changed, 46 insertions(+), 9 deletions(-) diff --git a/bindings/stronghold-nodejs/Cargo.toml b/bindings/stronghold-nodejs/Cargo.toml index dba768976f..b29684cbc8 100644 --- a/bindings/stronghold-nodejs/Cargo.toml +++ b/bindings/stronghold-nodejs/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity-stronghold-nodejs" -version = "0.6.0" +version = "0.7.0-alpha.1" edition = "2021" publish = false [lib] diff --git a/bindings/stronghold-nodejs/package-lock.json b/bindings/stronghold-nodejs/package-lock.json index c00da9ab6a..27a742c56c 100644 --- a/bindings/stronghold-nodejs/package-lock.json +++ b/bindings/stronghold-nodejs/package-lock.json @@ -1,12 +1,12 @@ { "name": "@iota/identity-stronghold-nodejs", - "version": "0.6.0", + "version": "0.7.0-alpha.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@iota/identity-stronghold-nodejs", - "version": "0.6.0", + "version": "0.7.0-alpha.1", "license": "Apache-2.0", "dependencies": { "@iota/identity-stronghold-nodejs": "^0.6.0" diff --git a/bindings/stronghold-nodejs/package.json b/bindings/stronghold-nodejs/package.json index 1c411e59b5..45788c406d 100644 --- a/bindings/stronghold-nodejs/package.json +++ b/bindings/stronghold-nodejs/package.json @@ -1,6 +1,6 @@ { "name": "@iota/identity-stronghold-nodejs", - "version": "0.6.0", + "version": "0.7.0-alpha.1", "main": "dist/index.js", "types": "dist/index.d.ts", "files": [ @@ -50,6 +50,6 @@ "test:readme": "mocha ./tests/txm_readme.js --retries 3 --timeout 180000 --exit" }, "peerDependencies": { - "@iota/identity-wasm": "0.6.0" + "@iota/identity-wasm": "0.7.0-alpha.1" } } diff --git a/bindings/wasm/CHANGELOG.md b/bindings/wasm/CHANGELOG.md index 335f93ce52..045be5adda 100644 --- a/bindings/wasm/CHANGELOG.md +++ b/bindings/wasm/CHANGELOG.md @@ -1,5 +1,42 @@ # Changelog +## [wasm-v0.7.0-alpha.1](https://github.com/iotaledger/identity.rs/tree/wasm-v0.7.0-alpha.1) (2022-09-16) + +[Full Changelog](https://github.com/iotaledger/identity.rs/compare/wasm-v0.6.0...wasm-v0.7.0-alpha.1) + +This version introduces a new DID method targeting the IOTA UTXO ledger. This method works fundamentally differently from the previous method and introduces new capabilities to interact with Layer 1 entities like native tokens, NFTs and smart contracts. + + This is an early alpha release, so there may be breaking changes in upcoming versions that invalidate DIDs created with this version. The method at this point is only intended for experimentation. + + Note: Identities created with the earlier versions cannot be resolved with this version of the library. + + + +### Changed + +- Chore/rename mixed resolver [\#1026](https://github.com/iotaledger/identity.rs/pull/1026) +- Add length prefix to DID Document payloads [\#1010](https://github.com/iotaledger/identity.rs/pull/1010) +- Update Wasm credential, presentation validators for Stardust [\#1004](https://github.com/iotaledger/identity.rs/pull/1004) +- Rename `Stardust` types to `Iota` [\#1000](https://github.com/iotaledger/identity.rs/pull/1000) +- Change Stardust DID method to IOTA [\#982](https://github.com/iotaledger/identity.rs/pull/982) +- Add Wasm Stardust Client [\#975](https://github.com/iotaledger/identity.rs/pull/975) +- Generalized Resolver [\#970](https://github.com/iotaledger/identity.rs/pull/970) +- Change `Storage` to handle `CoreDID` [\#968](https://github.com/iotaledger/identity.rs/pull/968) +- Change `Storage` to store arbitrary blobs [\#953](https://github.com/iotaledger/identity.rs/pull/953) +- Change `Service` `type` field to allow sets [\#944](https://github.com/iotaledger/identity.rs/pull/944) +- Generalise `CredentialValidator`, `PresentationValidator` to support arbitrary DID Documents [\#935](https://github.com/iotaledger/identity.rs/pull/935) + +### Added + +- Add Wasm bindings for `CoreDocument` [\#994](https://github.com/iotaledger/identity.rs/pull/994) +- Add initial Wasm Stardust bindings [\#967](https://github.com/iotaledger/identity.rs/pull/967) + +### Patch + +- Support case insensitive serialization of `RentStructure` [\#1012](https://github.com/iotaledger/identity.rs/pull/1012) +- Fix broken wasm bindings compilation [\#995](https://github.com/iotaledger/identity.rs/pull/995) +- Fix DID TypeScript references [\#977](https://github.com/iotaledger/identity.rs/pull/977) + ## [wasm-v0.6.0](https://github.com/iotaledger/identity.rs/tree/wasm-v0.6.0) (2022-06-15) [Full Changelog](https://github.com/iotaledger/identity.rs/compare/wasm-v0.5.0...wasm-v0.6.0) diff --git a/bindings/wasm/Cargo.toml b/bindings/wasm/Cargo.toml index b7b109d445..f2d4a158bb 100644 --- a/bindings/wasm/Cargo.toml +++ b/bindings/wasm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity_wasm" -version = "0.6.0" +version = "0.7.0-alpha.1" authors = ["IOTA Stiftung"] edition = "2021" homepage = "https://www.iota.org" diff --git a/bindings/wasm/package-lock.json b/bindings/wasm/package-lock.json index 6abfdd1a68..2bf6fb25da 100644 --- a/bindings/wasm/package-lock.json +++ b/bindings/wasm/package-lock.json @@ -1,12 +1,12 @@ { "name": "@iota/identity-wasm", - "version": "0.6.0", + "version": "0.7.0-alpha.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@iota/identity-wasm", - "version": "0.6.0", + "version": "0.7.0-alpha.1", "license": "Apache-2.0", "dependencies": { "@iota/types": "^1.0.0-beta.11", diff --git a/bindings/wasm/package.json b/bindings/wasm/package.json index 9cf935643f..76988fc113 100644 --- a/bindings/wasm/package.json +++ b/bindings/wasm/package.json @@ -1,6 +1,6 @@ { "name": "@iota/identity-wasm", - "version": "0.6.0", + "version": "0.7.0-alpha.1", "description": "WASM bindings for IOTA Identity - A Self Sovereign Identity Framework implementing the DID and VC standards from W3C. To be used in Javascript/Typescript", "repository": { "type": "git", From 43a228d47f01966910e45790fbd4726892dd19a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Ha=C3=9F?= Date: Fri, 16 Sep 2022 17:36:19 +0200 Subject: [PATCH 67/89] don't fix dependencies (#1027) --- .github/actions/publish/publish-rust/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/publish/publish-rust/action.yml b/.github/actions/publish/publish-rust/action.yml index 5af42b4f70..045debd5d9 100644 --- a/.github/actions/publish/publish-rust/action.yml +++ b/.github/actions/publish/publish-rust/action.yml @@ -31,4 +31,4 @@ runs: run: | echo "dry-run: '${{ inputs.dry-run }}'" echo "version: '${{ inputs.version }}'" - cargo release --workspace --token ${{ inputs.crates-token }} --isolated --no-dev-version --no-push --no-tag --verbose $(if [ "${{ inputs.dry-run }}" = "false" ]; then echo --execute --no-confirm; fi) ${{ inputs.version }} + cargo release --workspace --token ${{ inputs.crates-token }} --isolated --no-dev-version --no-push --no-tag --dependent-version warn --verbose $(if [ "${{ inputs.dry-run }}" = "false" ]; then echo --execute --no-confirm; fi) ${{ inputs.version }} From 9ce2914b5d6c4722d377364c4c75975029583a08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Ha=C3=9F?= Date: Fri, 16 Sep 2022 18:36:18 +0200 Subject: [PATCH 68/89] reorg workspace members, error on mismatched dependencies (#1028) * reorg workspace members, error on mismatched dependencies * give legacy packages their own workspace * fix formatting --- .github/actions/publish/publish-rust/action.yml | 2 +- Cargo.toml | 16 +++++++++------- examples_legacy/Cargo.toml | 2 ++ identity_account/Cargo.toml | 2 ++ identity_account_storage/Cargo.toml | 2 ++ identity_iota_client_legacy/Cargo.toml | 2 ++ identity_iota_core_legacy/Cargo.toml | 2 ++ 7 files changed, 20 insertions(+), 8 deletions(-) diff --git a/.github/actions/publish/publish-rust/action.yml b/.github/actions/publish/publish-rust/action.yml index 045debd5d9..2f0623e3fc 100644 --- a/.github/actions/publish/publish-rust/action.yml +++ b/.github/actions/publish/publish-rust/action.yml @@ -31,4 +31,4 @@ runs: run: | echo "dry-run: '${{ inputs.dry-run }}'" echo "version: '${{ inputs.version }}'" - cargo release --workspace --token ${{ inputs.crates-token }} --isolated --no-dev-version --no-push --no-tag --dependent-version warn --verbose $(if [ "${{ inputs.dry-run }}" = "false" ]; then echo --execute --no-confirm; fi) ${{ inputs.version }} + cargo release --workspace --token ${{ inputs.crates-token }} --isolated --no-dev-version --no-push --no-tag --dependent-version error --verbose $(if [ "${{ inputs.dry-run }}" = "false" ]; then echo --execute --no-confirm; fi) ${{ inputs.version }} diff --git a/Cargo.toml b/Cargo.toml index fafbf00c3e..68a75de2e0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,24 +2,26 @@ resolver = "2" members = [ # "identity_comm", - "identity_account", - "identity_account_storage", "identity_agent", "identity_core", "identity_credential", "identity_did", "identity_diff", "identity_iota", - "identity_iota_client_legacy", - "identity_iota_core_legacy", "identity_iota_core", "identity_resolver", - - "examples_legacy", "examples", ] -exclude = ["bindings/stronghold-nodejs", "bindings/wasm", "libjose"] +exclude = [ + "identity_account", + "identity_account_storage", + "examples_legacy", + "identity_iota_core_legacy", + "bindings/stronghold-nodejs", + "bindings/wasm", + "libjose", +] [profile.dev] split-debuginfo = "unpacked" diff --git a/examples_legacy/Cargo.toml b/examples_legacy/Cargo.toml index 06ebf414d5..c566868e5a 100644 --- a/examples_legacy/Cargo.toml +++ b/examples_legacy/Cargo.toml @@ -4,6 +4,8 @@ version = "0.7.0-alpha.1" edition = "2021" publish = false +[workspace] + [dependencies] identity_account = { path = "../identity_account" } identity_account_storage = { path = "../identity_account_storage" } diff --git a/identity_account/Cargo.toml b/identity_account/Cargo.toml index 33bee24b74..c5e4ddb294 100644 --- a/identity_account/Cargo.toml +++ b/identity_account/Cargo.toml @@ -11,6 +11,8 @@ readme = "./README.md" repository = "https://github.com/iotaledger/identity.rs" description = "High-level interface for managing IOTA DID Documents." +[workspace] + [dependencies] identity_account_storage = { version = "=0.6.0", path = "../identity_account_storage", default-features = false } identity_core = { version = "=0.7.0-alpha.1", path = "../identity_core", default-features = false } diff --git a/identity_account_storage/Cargo.toml b/identity_account_storage/Cargo.toml index bd25876963..adbb36f956 100644 --- a/identity_account_storage/Cargo.toml +++ b/identity_account_storage/Cargo.toml @@ -11,6 +11,8 @@ readme = "./README.md" repository = "https://github.com/iotaledger/identity.rs" description = "Secure storage for Decentralized Identifiers and Verifiable Credentials." +[workspace] + [dependencies] anyhow = { version = "1.0", default-features = false, features = ["std"], optional = true } async-trait = { version = "0.1", default-features = false } diff --git a/identity_iota_client_legacy/Cargo.toml b/identity_iota_client_legacy/Cargo.toml index 5a2bb82087..e10856ad33 100644 --- a/identity_iota_client_legacy/Cargo.toml +++ b/identity_iota_client_legacy/Cargo.toml @@ -11,6 +11,8 @@ readme = "./README.md" repository = "https://github.com/iotaledger/identity.rs" description = "Tangle Client integration for the IOTA DID Method." +[workspace] + [dependencies] async-trait = { version = "0.1", default-features = false } bee-rest-api = { version = "0.1.7", default-features = false } diff --git a/identity_iota_core_legacy/Cargo.toml b/identity_iota_core_legacy/Cargo.toml index 53c8a1d90b..a6e7123e52 100644 --- a/identity_iota_core_legacy/Cargo.toml +++ b/identity_iota_core_legacy/Cargo.toml @@ -11,6 +11,8 @@ readme = "./README.md" repository = "https://github.com/iotaledger/identity.rs" description = "Core data structures for the IOTA DID Method." +[workspace] + [dependencies] bee-message = { version = "0.1.6", default-features = false, features = ["serde"] } identity_core = { version = "=0.7.0-alpha.1", path = "../identity_core", default-features = false } From 634895cd37c1b53a798f2f754b5dbae417ef8487 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Mon, 19 Sep 2022 10:26:43 +0200 Subject: [PATCH 69/89] Pin agent dev-dependencies to crates versions (#1029) --- identity_agent/Cargo.toml | 4 ++-- identity_agent/benches/agent.rs | 4 ++-- identity_agent/src/tests/handler.rs | 2 +- identity_agent/src/tests/remote_account/handler.rs | 4 ++-- identity_agent/src/tests/remote_account/requests.rs | 4 ++-- identity_agent/src/tests/remote_account/tests.rs | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/identity_agent/Cargo.toml b/identity_agent/Cargo.toml index 6cfaa9c93d..42e2d39692 100644 --- a/identity_agent/Cargo.toml +++ b/identity_agent/Cargo.toml @@ -25,8 +25,8 @@ uuid = { version = "0.8", default-features = false, features = ["v4", "serde"] } [dev-dependencies] criterion = { version = "0.3", default-features = false, features = ["stable"] } -identity_account = { version = "=0.6.0", path = "../identity_account", default-features = false, features = ["send-sync-storage"] } -identity_iota_core_legacy = { version = "=0.6.0", path = "../identity_iota_core_legacy", default-features = false } +identity_account = { version = "=0.6.1", default-features = false, features = ["send-sync-storage", "encryption", "async", "revocation-bitmap"] } +identity_iota_core = { version = "=0.6.1", default-features = false } pretty_env_logger = { version = "0.4", default-features = false } [[bench]] diff --git a/identity_agent/benches/agent.rs b/identity_agent/benches/agent.rs index a349ed61c3..d2626b2d1f 100644 --- a/identity_agent/benches/agent.rs +++ b/identity_agent/benches/agent.rs @@ -76,8 +76,8 @@ mod remote_account { use identity_agent::agent::Handler; use identity_agent::agent::HandlerRequest; use identity_agent::agent::RequestContext; - use identity_iota_core_legacy::did::IotaDID; - use identity_iota_core_legacy::document::IotaDocument; + use identity_iota_core::did::IotaDID; + use identity_iota_core::document::IotaDocument; use serde::Deserialize; use serde::Serialize; use std::sync::Arc; diff --git a/identity_agent/src/tests/handler.rs b/identity_agent/src/tests/handler.rs index 522c54855a..feb76e3a00 100644 --- a/identity_agent/src/tests/handler.rs +++ b/identity_agent/src/tests/handler.rs @@ -8,7 +8,7 @@ use std::sync::Arc; use std::task::Poll; use futures::pin_mut; -use identity_iota_core_legacy::did::IotaDID; +use identity_iota_core::did::IotaDID; use libp2p::request_response::OutboundFailure; use libp2p::Multiaddr; diff --git a/identity_agent/src/tests/remote_account/handler.rs b/identity_agent/src/tests/remote_account/handler.rs index f8b1445e5b..d6e56b3032 100644 --- a/identity_agent/src/tests/remote_account/handler.rs +++ b/identity_agent/src/tests/remote_account/handler.rs @@ -6,8 +6,8 @@ use std::sync::Arc; use dashmap::DashMap; use identity_account::account::Account; use identity_account::account::AccountBuilder; -use identity_iota_core_legacy::did::IotaDID; -use identity_iota_core_legacy::document::IotaDocument; +use identity_iota_core::did::IotaDID; +use identity_iota_core::document::IotaDocument; use tokio::sync::Mutex; use crate::agent::Handler; diff --git a/identity_agent/src/tests/remote_account/requests.rs b/identity_agent/src/tests/remote_account/requests.rs index 098af9a792..6ad3b22fb2 100644 --- a/identity_agent/src/tests/remote_account/requests.rs +++ b/identity_agent/src/tests/remote_account/requests.rs @@ -2,8 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 use identity_account::types::IdentitySetup; -use identity_iota_core_legacy::did::IotaDID; -use identity_iota_core_legacy::document::IotaDocument; +use identity_iota_core::did::IotaDID; +use identity_iota_core::document::IotaDocument; use serde::Deserialize; use serde::Serialize; diff --git a/identity_agent/src/tests/remote_account/tests.rs b/identity_agent/src/tests/remote_account/tests.rs index 5f8d042c91..cc0cd7c71e 100644 --- a/identity_agent/src/tests/remote_account/tests.rs +++ b/identity_agent/src/tests/remote_account/tests.rs @@ -1,7 +1,7 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use identity_iota_core_legacy::document::IotaDocument; +use identity_iota_core::document::IotaDocument; use crate::agent::Result as AgentResult; use crate::tests::default_listening_agent; From 3d3c733476383bf8de1c896b14beb908a6a0c801 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Ha=C3=9F?= Date: Mon, 19 Sep 2022 11:56:02 +0200 Subject: [PATCH 70/89] Revert "changelog and versions (#1015)" (#1030) This reverts commit bea72400b578fc6813b9a0109c9e844e2f53898f. --- CHANGELOG.md | 42 -------------------------- bindings/stronghold-nodejs/Cargo.toml | 8 ++--- bindings/wasm/Cargo.toml | 2 +- examples/Cargo.toml | 2 +- examples_legacy/Cargo.toml | 2 +- identity_account/Cargo.toml | 6 ++-- identity_account_storage/Cargo.toml | 4 +-- identity_agent/Cargo.toml | 4 +-- identity_core/Cargo.toml | 4 +-- identity_credential/Cargo.toml | 6 ++-- identity_did/Cargo.toml | 4 +-- identity_diff/Cargo.toml | 4 +-- identity_diff/derive/Cargo.toml | 2 +- identity_iota/Cargo.toml | 14 ++++----- identity_iota_client_legacy/Cargo.toml | 6 ++-- identity_iota_core/Cargo.toml | 8 ++--- identity_iota_core_legacy/Cargo.toml | 4 +-- identity_resolver/Cargo.toml | 10 +++--- 18 files changed, 45 insertions(+), 87 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc02303f4a..c5726f994b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,47 +1,5 @@ # Changelog -## [v0.7.0-alpha.1](https://github.com/iotaledger/identity.rs/tree/v0.7.0-alpha.1) (2022-09-16) - -[Full Changelog](https://github.com/iotaledger/identity.rs/compare/v0.6.0...v0.7.0-alpha.1) - -This version introduces a new DID method targeting the IOTA UTXO ledger. This method works fundamentally differently from the previous method and introduces new capabilities to interact with Layer 1 entities like native tokens, NFTs and smart contracts. - - This is an early alpha release, so there may be breaking changes in upcoming versions that invalidate DIDs created with this version. The method at this point is only intended for experimentation. - - Note: Identities created with the earlier versions cannot be resolved with this version of the library. - - - -### Changed - -- Chore/rename mixed resolver [\#1026](https://github.com/iotaledger/identity.rs/pull/1026) -- Add length prefix to DID Document payloads [\#1010](https://github.com/iotaledger/identity.rs/pull/1010) -- Feature-gate `Resolver` [\#1007](https://github.com/iotaledger/identity.rs/pull/1007) -- Rename `Stardust` types to `Iota` [\#1000](https://github.com/iotaledger/identity.rs/pull/1000) -- Change Stardust DID method to IOTA [\#982](https://github.com/iotaledger/identity.rs/pull/982) -- Add Wasm Stardust Client [\#975](https://github.com/iotaledger/identity.rs/pull/975) -- Generalized Resolver [\#970](https://github.com/iotaledger/identity.rs/pull/970) -- Change `Storage` to handle `CoreDID` [\#968](https://github.com/iotaledger/identity.rs/pull/968) -- Feature-gate `iota-client` dependency, integrate `StardustDID` [\#958](https://github.com/iotaledger/identity.rs/pull/958) -- Change `Storage` to store arbitrary blobs [\#953](https://github.com/iotaledger/identity.rs/pull/953) -- Add `StardustDocumentMetadata`, implement `StardustDocument` methods [\#951](https://github.com/iotaledger/identity.rs/pull/951) -- Fix stack overflow in `CoreDID` `PartialEq` impl [\#946](https://github.com/iotaledger/identity.rs/pull/946) -- Change `Service` `type` field to allow sets [\#944](https://github.com/iotaledger/identity.rs/pull/944) -- Generalise `CredentialValidator`, `PresentationValidator` to support arbitrary DID Documents [\#935](https://github.com/iotaledger/identity.rs/pull/935) - -### Added - -- Add Stardust Client Extension Trait [\#963](https://github.com/iotaledger/identity.rs/pull/963) -- Add StardustDID [\#949](https://github.com/iotaledger/identity.rs/pull/949) -- State metadata serialization for the stardust DID method [\#947](https://github.com/iotaledger/identity.rs/pull/947) -- Stardust DID Method Proof-of-Concept [\#940](https://github.com/iotaledger/identity.rs/pull/940) -- Implement the Identity Agent [\#322](https://github.com/iotaledger/identity.rs/pull/322) - -### Patch - -- Support case insensitive serialization of `RentStructure` [\#1012](https://github.com/iotaledger/identity.rs/pull/1012) -- Update stronghold to 0.6.4 [\#928](https://github.com/iotaledger/identity.rs/pull/928) - ## [v0.6.0](https://github.com/iotaledger/identity.rs/tree/v0.6.0) (2022-06-15) [Full Changelog](https://github.com/iotaledger/identity.rs/compare/v0.5.0...v0.6.0) diff --git a/bindings/stronghold-nodejs/Cargo.toml b/bindings/stronghold-nodejs/Cargo.toml index b29684cbc8..dd16c1e7d6 100644 --- a/bindings/stronghold-nodejs/Cargo.toml +++ b/bindings/stronghold-nodejs/Cargo.toml @@ -7,10 +7,10 @@ publish = false crate-type = ["cdylib"] [dependencies] -identity_account_storage = { version = "0.6.0", path = "../../identity_account_storage", default-features = false, features = ["stronghold", "send-sync-storage", "encryption"] } -identity_core = { version = "0.7.0-alpha.1", path = "../../identity_core", default-features = false } -identity_did = { version = "0.7.0-alpha.1", path = "../../identity_did", default-features = false } -identity_iota_core_legacy = { version = "0.6.0", path = "../../identity_iota_core_legacy", default-features = false } +identity_account_storage = { version = "=0.6.0", path = "../../identity_account_storage", default-features = false, features = ["stronghold", "send-sync-storage", "encryption"] } +identity_core = { version = "=0.6.0", path = "../../identity_core", default-features = false } +identity_did = { version = "=0.6.0", path = "../../identity_did", default-features = false } +identity_iota_core_legacy = { version = "=0.6.0", path = "../../identity_iota_core_legacy", default-features = false } napi = { version = "2.4.3", default-features = false, features = ["napi4", "tokio_rt", "serde-json"] } napi-derive = { version = "2.4.1", default-features = false, features = ["compat-mode", "full"] } serde = { version = "1.0", default-features = false, features = ["derive"] } diff --git a/bindings/wasm/Cargo.toml b/bindings/wasm/Cargo.toml index f2d4a158bb..328f32faeb 100644 --- a/bindings/wasm/Cargo.toml +++ b/bindings/wasm/Cargo.toml @@ -30,7 +30,7 @@ wasm-bindgen = { version = "0.2.82", features = ["serde-serialize"] } wasm-bindgen-futures = { version = "0.4", default-features = false } [dependencies.identity_iota] -version = "0.7.0-alpha.1" +version = "=0.6.0" path = "../../identity_iota" default-features = false features = ["client", "revocation-bitmap", "resolver"] diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 15da7be4b6..6b57c7acc0 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "examples" -version = "0.7.0-alpha.1" +version = "0.6.0" authors = ["IOTA Stiftung"] edition = "2021" publish = false diff --git a/examples_legacy/Cargo.toml b/examples_legacy/Cargo.toml index c566868e5a..ff12c7852f 100644 --- a/examples_legacy/Cargo.toml +++ b/examples_legacy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "examples_legacy" -version = "0.7.0-alpha.1" +version = "0.6.0" edition = "2021" publish = false diff --git a/identity_account/Cargo.toml b/identity_account/Cargo.toml index c5e4ddb294..3f53b4d183 100644 --- a/identity_account/Cargo.toml +++ b/identity_account/Cargo.toml @@ -15,9 +15,9 @@ description = "High-level interface for managing IOTA DID Documents." [dependencies] identity_account_storage = { version = "=0.6.0", path = "../identity_account_storage", default-features = false } -identity_core = { version = "=0.7.0-alpha.1", path = "../identity_core", default-features = false } -identity_credential = { version = "=0.7.0-alpha.1", path = "../identity_credential", default-features = false } -identity_did = { version = "=0.7.0-alpha.1", path = "../identity_did", default-features = false } +identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } +identity_credential = { version = "=0.6.0", path = "../identity_credential", default-features = false } +identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } identity_iota_client_legacy = { version = "=0.6.0", path = "../identity_iota_client_legacy", default-features = false } identity_iota_core_legacy = { version = "=0.6.0", path = "../identity_iota_core_legacy", default-features = false } log = { version = "0.4", default-features = false } diff --git a/identity_account_storage/Cargo.toml b/identity_account_storage/Cargo.toml index adbb36f956..a7082f22a1 100644 --- a/identity_account_storage/Cargo.toml +++ b/identity_account_storage/Cargo.toml @@ -19,8 +19,8 @@ async-trait = { version = "0.1", default-features = false } function_name = { version = "0.2", default-features = false, optional = true } futures = { version = "0.3", optional = true } hashbrown = { version = "0.11", features = ["serde"] } -identity_core = { version = "=0.7.0-alpha.1", path = "../identity_core", default-features = false } -identity_did = { version = "=0.7.0-alpha.1", path = "../identity_did", default-features = false } +identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } +identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } identity_iota_core_legacy = { version = "=0.6.0", path = "../identity_iota_core_legacy", default-features = false } iota-crypto = { version = "0.12.1", default-features = false, features = ["hmac", "pbkdf", "sha", "std", "aes-gcm", "aes-kw"] } iota_stronghold = { version = "0.6.4", default-features = false, features = ["std"], optional = true } diff --git a/identity_agent/Cargo.toml b/identity_agent/Cargo.toml index 42e2d39692..31c31687e7 100644 --- a/identity_agent/Cargo.toml +++ b/identity_agent/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity_agent" -version = "0.7.0-alpha.1" +version = "0.6.0" authors = ["IOTA Stiftung"] edition = "2021" homepage = "https://www.iota.org" @@ -14,7 +14,7 @@ description = "A peer-to-peer communication framework for building SSI agents on async-trait = { version = "0.1", default-features = false } dashmap = { version = "5.3", default-features = false } futures = { version = "0.3", default-features = false } -identity_core = { version = "=0.7.0-alpha.1", path = "../identity_core", default-features = false } +identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } libp2p = { version = "0.45", default-features = false, features = ["tcp-tokio", "dns-tokio", "websocket", "request-response", "noise", "yamux"] } log = { version = "0.4", default-features = false } serde = { version = "1.0", default-features = false, features = ["derive"] } diff --git a/identity_core/Cargo.toml b/identity_core/Cargo.toml index 8474e2d304..a76819477c 100644 --- a/identity_core/Cargo.toml +++ b/identity_core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity_core" -version = "0.7.0-alpha.1" +version = "0.6.0" authors = ["IOTA Stiftung"] edition = "2021" homepage = "https://www.iota.org" @@ -11,7 +11,7 @@ repository = "https://github.com/iotaledger/identity.rs" description = "The core traits and types for the identity-rs library." [dependencies] -identity-diff = { version = "=0.7.0-alpha.1", path = "../identity_diff", default-features = false } +identity-diff = { version = "=0.6.0", path = "../identity_diff", default-features = false } multibase = { version = "0.9", default-features = false, features = ["std"] } serde = { version = "1.0", default-features = false, features = ["std", "derive"] } serde_jcs = { version = "0.1", default-features = false } diff --git a/identity_credential/Cargo.toml b/identity_credential/Cargo.toml index ae86d78ca8..5440702cc4 100644 --- a/identity_credential/Cargo.toml +++ b/identity_credential/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity_credential" -version = "0.7.0-alpha.1" +version = "0.6.0" authors = ["IOTA Stiftung"] edition = "2021" homepage = "https://www.iota.org" @@ -13,8 +13,8 @@ description = "An implementation of the Verifiable Credentials standard." [dependencies] erased-serde = { version = "0.3.21", default-features = false, features = ["std"], optional = true } -identity_core = { version = "=0.7.0-alpha.1", path = "../identity_core", default-features = false } -identity_did = { version = "=0.7.0-alpha.1", path = "../identity_did", default-features = false } +identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } +identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } itertools = { version = "0.10", default-features = false, features = ["use_std"], optional = true } lazy_static = { version = "1.4", default-features = false } serde = { version = "1.0", default-features = false, features = ["std", "derive"] } diff --git a/identity_did/Cargo.toml b/identity_did/Cargo.toml index 70ffea7053..cc57752cc4 100644 --- a/identity_did/Cargo.toml +++ b/identity_did/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity_did" -version = "0.7.0-alpha.1" +version = "0.6.0" authors = ["IOTA Stiftung"] edition = "2021" homepage = "https://www.iota.org" @@ -15,7 +15,7 @@ dataurl = { version = "0.1.2", default-features = false, optional = true } did_url = { version = "0.1", default-features = false, features = ["std", "serde"] } flate2 = { version = "1.0.23", default-features = false, features = ["rust_backend"], optional = true } form_urlencoded = { version = "1.0.1", default-features = false } -identity_core = { version = "=0.7.0-alpha.1", path = "../identity_core" } +identity_core = { version = "=0.6.0", path = "../identity_core" } indexmap = { version = "1.7", default-features = false, features = ["std", "serde-1"] } roaring = { version = "0.9.0", default-features = false, optional = true } serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] } diff --git a/identity_diff/Cargo.toml b/identity_diff/Cargo.toml index 71a84a12d5..3c92f6a4a3 100644 --- a/identity_diff/Cargo.toml +++ b/identity_diff/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity-diff" -version = "0.7.0-alpha.1" +version = "0.6.0" authors = ["IOTA Stiftung"] edition = "2021" homepage = "https://www.iota.org" @@ -11,7 +11,7 @@ repository = "https://github.com/iotaledger/identity.rs" description = "`Diff` trait to compute and merge data structure differences." [dependencies] -identity-diff-derive = { version = "=0.7.0-alpha.1", path = "derive", optional = true } +identity-diff-derive = { version = "=0.6.0", path = "derive", optional = true } serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0" } strum = { version = "0.24.0", default-features = false, features = ["std", "derive"] } diff --git a/identity_diff/derive/Cargo.toml b/identity_diff/derive/Cargo.toml index 5489e18976..9dd3bb32d4 100644 --- a/identity_diff/derive/Cargo.toml +++ b/identity_diff/derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity-diff-derive" -version = "0.7.0-alpha.1" +version = "0.6.0" authors = ["IOTA Stiftung"] edition = "2021" homepage = "https://www.iota.org" diff --git a/identity_iota/Cargo.toml b/identity_iota/Cargo.toml index a958ef4ce8..8ae283cf10 100644 --- a/identity_iota/Cargo.toml +++ b/identity_iota/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity_iota" -version = "0.7.0-alpha.1" +version = "0.6.0" authors = ["IOTA Stiftung"] edition = "2021" homepage = "https://www.iota.org" @@ -12,12 +12,12 @@ rust-version = "1.62" description = "Framework for Self-Sovereign Identity with IOTA DID." [dependencies] -identity_agent = { version = "=0.7.0-alpha.1", path = "../identity_agent", default-features = false, optional = true } -identity_core = { version = "=0.7.0-alpha.1", path = "../identity_core", default-features = false } -identity_credential = { version = "=0.7.0-alpha.1", path = "../identity_credential", features = ["validator"], default-features = false } -identity_did = { version = "=0.7.0-alpha.1", path = "../identity_did", default-features = false } -identity_iota_core = { version = "=0.7.0-alpha.1", path = "../identity_iota_core", default-features = false } -identity_resolver = { version = "=0.7.0-alpha.1", path = "../identity_resolver", default-features = false, optional = true } +identity_agent = { version = "=0.6.0", path = "../identity_agent", default-features = false, optional = true } +identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } +identity_credential = { version = "=0.6.0", path = "../identity_credential", features = ["validator"], default-features = false } +identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } +identity_iota_core = { version = "=0.6.0", path = "../identity_iota_core", default-features = false } +identity_resolver = { version = "=0.6.0", path = "../identity_resolver", default-features = false, optional = true } [dev-dependencies] anyhow = "1.0.64" diff --git a/identity_iota_client_legacy/Cargo.toml b/identity_iota_client_legacy/Cargo.toml index e10856ad33..224a394c2c 100644 --- a/identity_iota_client_legacy/Cargo.toml +++ b/identity_iota_client_legacy/Cargo.toml @@ -19,9 +19,9 @@ bee-rest-api = { version = "0.1.7", default-features = false } brotli = { version = "3.3", default-features = false, features = ["std"] } form_urlencoded = { version = "1.0" } futures = { version = "0.3" } -identity_core = { version = "=0.7.0-alpha.1", path = "../identity_core", default-features = false } -identity_credential = { version = "=0.7.0-alpha.1", path = "../identity_credential", default-features = false, features = ["validator"] } -identity_did = { version = "=0.7.0-alpha.1", path = "../identity_did", default-features = false } +identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } +identity_credential = { version = "=0.6.0", path = "../identity_credential", default-features = false, features = ["validator"] } +identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } identity_iota_core_legacy = { version = "=0.6.0", path = "../identity_iota_core_legacy", default-features = false } itertools = { version = "0.10" } lazy_static = { version = "1.4", default-features = false } diff --git a/identity_iota_core/Cargo.toml b/identity_iota_core/Cargo.toml index ba0f893329..90aa0e211d 100644 --- a/identity_iota_core/Cargo.toml +++ b/identity_iota_core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity_iota_core" -version = "0.7.0-alpha.1" +version = "0.6.0" authors = ["IOTA Stiftung"] edition = "2021" homepage = "https://www.iota.org" @@ -14,9 +14,9 @@ description = "An IOTA Ledger integration for the IOTA DID Method." [dependencies] # Ensure bee-block always matches the version used by iota-client. bee-block = { version = "1.0.0-beta.7", default-features = false, features = ["std"], optional = true } -identity_core = { version = "=0.7.0-alpha.1", path = "../identity_core", default-features = false } -identity_credential = { version = "=0.7.0-alpha.1", path = "../identity_credential", default-features = false, features = ["validator"] } -identity_did = { version = "=0.7.0-alpha.1", path = "../identity_did", default-features = false } +identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } +identity_credential = { version = "=0.6.0", path = "../identity_credential", default-features = false, features = ["validator"] } +identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } async-trait = { version = "0.1.56", default-features = false, optional = true } futures = { version = "0.3" } diff --git a/identity_iota_core_legacy/Cargo.toml b/identity_iota_core_legacy/Cargo.toml index a6e7123e52..af12d7a969 100644 --- a/identity_iota_core_legacy/Cargo.toml +++ b/identity_iota_core_legacy/Cargo.toml @@ -15,8 +15,8 @@ description = "Core data structures for the IOTA DID Method." [dependencies] bee-message = { version = "0.1.6", default-features = false, features = ["serde"] } -identity_core = { version = "=0.7.0-alpha.1", path = "../identity_core", default-features = false } -identity_did = { version = "=0.7.0-alpha.1", path = "../identity_did", default-features = false } +identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } +identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } lazy_static = { version = "1.4", default-features = false } serde = { version = "1.0", default-features = false, features = ["std", "derive"] } strum = { version = "0.24.0", default-features = false, features = ["std", "derive"] } diff --git a/identity_resolver/Cargo.toml b/identity_resolver/Cargo.toml index 82f3fbc02d..3fdcfa4769 100644 --- a/identity_resolver/Cargo.toml +++ b/identity_resolver/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity_resolver" -version = "0.7.0-alpha.1" +version = "0.6.0" authors = ["IOTA Stiftung"] edition = "2021" homepage = "https://www.iota.org" @@ -15,15 +15,15 @@ description = "DID Resolution utilities for the identity.rs library." # This is currently necessary for the ResolutionHandler trait. This can be made an optional dependency if alternative ways of attaching handlers are introduced. async-trait = { version = "0.1", default-features = false } futures = { version = "0.3" } -identity_core = { version = "=0.7.0-alpha.1", path = "../identity_core", default-features = false } -identity_credential = { version = "=0.7.0-alpha.1", path = "../identity_credential", default-features = false, features = ["validator"] } -identity_did = { version = "=0.7.0-alpha.1", path = "../identity_did", default-features = false } +identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } +identity_credential = { version = "=0.6.0", path = "../identity_credential", default-features = false, features = ["validator"] } +identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } serde = { version = "1.0", default-features = false, features = ["std", "derive"] } strum = { version = "0.21", features = ["derive"] } thiserror = { version = "1.0", default-features = false } [dependencies.identity_iota_core] -version = "=0.7.0-alpha.1" +version = "=0.6.0" path = "../identity_iota_core" default-features = false features = ["send-sync-client-ext", "iota-client"] From edc63a4a305ad97a32311cf7bf61851cf3d7f2e5 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Mon, 19 Sep 2022 12:46:50 +0200 Subject: [PATCH 71/89] Remove agent reexport from `identity_iota` (#1031) --- Cargo.toml | 2 +- identity_iota/Cargo.toml | 5 ----- identity_iota/src/lib.rs | 11 ----------- 3 files changed, 1 insertion(+), 17 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 68a75de2e0..2969633cda 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,6 @@ resolver = "2" members = [ # "identity_comm", - "identity_agent", "identity_core", "identity_credential", "identity_did", @@ -16,6 +15,7 @@ members = [ exclude = [ "identity_account", "identity_account_storage", + "identity_agent", "examples_legacy", "identity_iota_core_legacy", "bindings/stronghold-nodejs", diff --git a/identity_iota/Cargo.toml b/identity_iota/Cargo.toml index 8ae283cf10..4e212b8076 100644 --- a/identity_iota/Cargo.toml +++ b/identity_iota/Cargo.toml @@ -12,7 +12,6 @@ rust-version = "1.62" description = "Framework for Self-Sovereign Identity with IOTA DID." [dependencies] -identity_agent = { version = "=0.6.0", path = "../identity_agent", default-features = false, optional = true } identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } identity_credential = { version = "=0.6.0", path = "../identity_credential", features = ["validator"], default-features = false } identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } @@ -43,10 +42,6 @@ revocation-bitmap = [ # Enables support for the `Resolver`. resolver = ["dep:identity_resolver"] -# Enables support for the unstable identity agent. -# Breaking changes to types and functions behind this flag are not covered by semver. -unstable-agent = ["dep:identity_agent"] - [package.metadata.docs.rs] # To build locally: # RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features --no-deps --workspace --open diff --git a/identity_iota/src/lib.rs b/identity_iota/src/lib.rs index 0c5d7a5ef5..596c3845c1 100644 --- a/identity_iota/src/lib.rs +++ b/identity_iota/src/lib.rs @@ -118,14 +118,3 @@ pub mod resolver { pub use identity_resolver::*; } - -#[cfg(feature = "unstable-agent")] -#[cfg_attr(docsrs, doc(cfg(feature = "unstable-agent")))] -pub mod agent { - //! Identity agent types - - pub use identity_agent::agent::*; - pub use identity_agent::didcomm::*; - pub use identity_agent::IdentityKeypair; - pub use identity_agent::Multiaddr; -} From 82fad87f66b09bb7bd846ded8fceaaa59faea027 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Mon, 19 Sep 2022 13:25:44 +0200 Subject: [PATCH 72/89] Bump agent versions during release (#1033) --- .github/actions/release/bump-versions/action.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/actions/release/bump-versions/action.yml b/.github/actions/release/bump-versions/action.yml index 426266dd0a..1c6b39828c 100644 --- a/.github/actions/release/bump-versions/action.yml +++ b/.github/actions/release/bump-versions/action.yml @@ -53,6 +53,16 @@ runs: run: | cargo set-version ${{ inputs.version }} + # cargo workspaces ignores identity_agent because it is excluded from the workspace. + # Bump versions for consistency. + - name: Bump Rust agent version + shell: bash + if: ${{inputs.release-target == 'rust'}} + working-directory: identity_agent + run: | + cargo set-version ${{ inputs.version }} + cargo add --path ../identity_core + - name: Bump Wasm bindings crate version shell: bash if: ${{inputs.release-target == 'wasm'}} From 4a3ca89af593bdca749080ebacc4524946eeba6d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 19 Sep 2022 13:48:55 +0200 Subject: [PATCH 73/89] changelog and versions (#1032) Co-authored-by: Identity Bot --- CHANGELOG.md | 44 +++++++++++++++++++++++++++ bindings/stronghold-nodejs/Cargo.toml | 8 ++--- bindings/wasm/Cargo.toml | 2 +- examples/Cargo.toml | 2 +- examples_legacy/Cargo.toml | 2 +- identity_agent/Cargo.toml | 4 +-- identity_core/Cargo.toml | 4 +-- identity_credential/Cargo.toml | 6 ++-- identity_did/Cargo.toml | 4 +-- identity_diff/Cargo.toml | 4 +-- identity_diff/derive/Cargo.toml | 2 +- identity_iota/Cargo.toml | 12 ++++---- identity_iota_core/Cargo.toml | 8 ++--- identity_resolver/Cargo.toml | 10 +++--- 14 files changed, 78 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c5726f994b..d2591ba20c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,49 @@ # Changelog +## [v0.7.0-alpha.1](https://github.com/iotaledger/identity.rs/tree/v0.7.0-alpha.1) (2022-09-19) + +[Full Changelog](https://github.com/iotaledger/identity.rs/compare/v0.6.0...v0.7.0-alpha.1) + +This version introduces a new DID method targeting the IOTA UTXO ledger. This method works fundamentally differently from the previous method and introduces new capabilities to interact with Layer 1 entities like native tokens, NFTs and smart contracts. + + This is an early alpha release, so there may be breaking changes in upcoming versions that invalidate DIDs created with this version. The method at this point is only intended for experimentation. + + Note: Identities created with the earlier versions cannot be resolved with this version of the library. + + + +### Changed + +- Remove `identity_agent` reexport [\#1031](https://github.com/iotaledger/identity.rs/pull/1031) +- Rename `MixedResolver` to `Resolver` in Wasm [\#1026](https://github.com/iotaledger/identity.rs/pull/1026) +- Add length prefix to DID Document payloads [\#1010](https://github.com/iotaledger/identity.rs/pull/1010) +- Feature-gate `Resolver` [\#1007](https://github.com/iotaledger/identity.rs/pull/1007) +- Rename `Stardust` types to `Iota` [\#1000](https://github.com/iotaledger/identity.rs/pull/1000) +- Change Stardust DID method to IOTA [\#982](https://github.com/iotaledger/identity.rs/pull/982) +- Add Wasm Stardust Client [\#975](https://github.com/iotaledger/identity.rs/pull/975) +- Generalized Resolver [\#970](https://github.com/iotaledger/identity.rs/pull/970) +- Change `Storage` to handle `CoreDID` [\#968](https://github.com/iotaledger/identity.rs/pull/968) +- Feature-gate `iota-client` dependency, integrate `StardustDID` [\#958](https://github.com/iotaledger/identity.rs/pull/958) +- Change `Storage` to store arbitrary blobs [\#953](https://github.com/iotaledger/identity.rs/pull/953) +- Add `StardustDocumentMetadata`, implement `StardustDocument` methods [\#951](https://github.com/iotaledger/identity.rs/pull/951) +- Fix stack overflow in `CoreDID` `PartialEq` impl [\#946](https://github.com/iotaledger/identity.rs/pull/946) +- Change `Service` `type` field to allow sets [\#944](https://github.com/iotaledger/identity.rs/pull/944) +- Generalise `CredentialValidator`, `PresentationValidator` to support arbitrary DID Documents [\#935](https://github.com/iotaledger/identity.rs/pull/935) + +### Added + +- Add Stardust Client Extension Trait [\#963](https://github.com/iotaledger/identity.rs/pull/963) +- Add StardustDID [\#949](https://github.com/iotaledger/identity.rs/pull/949) +- State metadata serialization for the stardust DID method [\#947](https://github.com/iotaledger/identity.rs/pull/947) +- Stardust DID Method Proof-of-Concept [\#940](https://github.com/iotaledger/identity.rs/pull/940) +- Implement the Identity Agent [\#322](https://github.com/iotaledger/identity.rs/pull/322) + +### Patch + +- Pin agent dev-dependencies to crates versions [\#1029](https://github.com/iotaledger/identity.rs/pull/1029) +- Support case insensitive serialization of `RentStructure` [\#1012](https://github.com/iotaledger/identity.rs/pull/1012) +- Update stronghold to 0.6.4 [\#928](https://github.com/iotaledger/identity.rs/pull/928) + ## [v0.6.0](https://github.com/iotaledger/identity.rs/tree/v0.6.0) (2022-06-15) [Full Changelog](https://github.com/iotaledger/identity.rs/compare/v0.5.0...v0.6.0) diff --git a/bindings/stronghold-nodejs/Cargo.toml b/bindings/stronghold-nodejs/Cargo.toml index dd16c1e7d6..b29684cbc8 100644 --- a/bindings/stronghold-nodejs/Cargo.toml +++ b/bindings/stronghold-nodejs/Cargo.toml @@ -7,10 +7,10 @@ publish = false crate-type = ["cdylib"] [dependencies] -identity_account_storage = { version = "=0.6.0", path = "../../identity_account_storage", default-features = false, features = ["stronghold", "send-sync-storage", "encryption"] } -identity_core = { version = "=0.6.0", path = "../../identity_core", default-features = false } -identity_did = { version = "=0.6.0", path = "../../identity_did", default-features = false } -identity_iota_core_legacy = { version = "=0.6.0", path = "../../identity_iota_core_legacy", default-features = false } +identity_account_storage = { version = "0.6.0", path = "../../identity_account_storage", default-features = false, features = ["stronghold", "send-sync-storage", "encryption"] } +identity_core = { version = "0.7.0-alpha.1", path = "../../identity_core", default-features = false } +identity_did = { version = "0.7.0-alpha.1", path = "../../identity_did", default-features = false } +identity_iota_core_legacy = { version = "0.6.0", path = "../../identity_iota_core_legacy", default-features = false } napi = { version = "2.4.3", default-features = false, features = ["napi4", "tokio_rt", "serde-json"] } napi-derive = { version = "2.4.1", default-features = false, features = ["compat-mode", "full"] } serde = { version = "1.0", default-features = false, features = ["derive"] } diff --git a/bindings/wasm/Cargo.toml b/bindings/wasm/Cargo.toml index 328f32faeb..f2d4a158bb 100644 --- a/bindings/wasm/Cargo.toml +++ b/bindings/wasm/Cargo.toml @@ -30,7 +30,7 @@ wasm-bindgen = { version = "0.2.82", features = ["serde-serialize"] } wasm-bindgen-futures = { version = "0.4", default-features = false } [dependencies.identity_iota] -version = "=0.6.0" +version = "0.7.0-alpha.1" path = "../../identity_iota" default-features = false features = ["client", "revocation-bitmap", "resolver"] diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 6b57c7acc0..15da7be4b6 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "examples" -version = "0.6.0" +version = "0.7.0-alpha.1" authors = ["IOTA Stiftung"] edition = "2021" publish = false diff --git a/examples_legacy/Cargo.toml b/examples_legacy/Cargo.toml index ff12c7852f..c566868e5a 100644 --- a/examples_legacy/Cargo.toml +++ b/examples_legacy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "examples_legacy" -version = "0.6.0" +version = "0.7.0-alpha.1" edition = "2021" publish = false diff --git a/identity_agent/Cargo.toml b/identity_agent/Cargo.toml index 31c31687e7..d37e5b07f3 100644 --- a/identity_agent/Cargo.toml +++ b/identity_agent/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity_agent" -version = "0.6.0" +version = "0.7.0-alpha.1" authors = ["IOTA Stiftung"] edition = "2021" homepage = "https://www.iota.org" @@ -14,7 +14,7 @@ description = "A peer-to-peer communication framework for building SSI agents on async-trait = { version = "0.1", default-features = false } dashmap = { version = "5.3", default-features = false } futures = { version = "0.3", default-features = false } -identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } +identity_core = { version = "0.7.0-alpha.1", path = "../identity_core", default-features = false } libp2p = { version = "0.45", default-features = false, features = ["tcp-tokio", "dns-tokio", "websocket", "request-response", "noise", "yamux"] } log = { version = "0.4", default-features = false } serde = { version = "1.0", default-features = false, features = ["derive"] } diff --git a/identity_core/Cargo.toml b/identity_core/Cargo.toml index a76819477c..8474e2d304 100644 --- a/identity_core/Cargo.toml +++ b/identity_core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity_core" -version = "0.6.0" +version = "0.7.0-alpha.1" authors = ["IOTA Stiftung"] edition = "2021" homepage = "https://www.iota.org" @@ -11,7 +11,7 @@ repository = "https://github.com/iotaledger/identity.rs" description = "The core traits and types for the identity-rs library." [dependencies] -identity-diff = { version = "=0.6.0", path = "../identity_diff", default-features = false } +identity-diff = { version = "=0.7.0-alpha.1", path = "../identity_diff", default-features = false } multibase = { version = "0.9", default-features = false, features = ["std"] } serde = { version = "1.0", default-features = false, features = ["std", "derive"] } serde_jcs = { version = "0.1", default-features = false } diff --git a/identity_credential/Cargo.toml b/identity_credential/Cargo.toml index 5440702cc4..ae86d78ca8 100644 --- a/identity_credential/Cargo.toml +++ b/identity_credential/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity_credential" -version = "0.6.0" +version = "0.7.0-alpha.1" authors = ["IOTA Stiftung"] edition = "2021" homepage = "https://www.iota.org" @@ -13,8 +13,8 @@ description = "An implementation of the Verifiable Credentials standard." [dependencies] erased-serde = { version = "0.3.21", default-features = false, features = ["std"], optional = true } -identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } -identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } +identity_core = { version = "=0.7.0-alpha.1", path = "../identity_core", default-features = false } +identity_did = { version = "=0.7.0-alpha.1", path = "../identity_did", default-features = false } itertools = { version = "0.10", default-features = false, features = ["use_std"], optional = true } lazy_static = { version = "1.4", default-features = false } serde = { version = "1.0", default-features = false, features = ["std", "derive"] } diff --git a/identity_did/Cargo.toml b/identity_did/Cargo.toml index cc57752cc4..70ffea7053 100644 --- a/identity_did/Cargo.toml +++ b/identity_did/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity_did" -version = "0.6.0" +version = "0.7.0-alpha.1" authors = ["IOTA Stiftung"] edition = "2021" homepage = "https://www.iota.org" @@ -15,7 +15,7 @@ dataurl = { version = "0.1.2", default-features = false, optional = true } did_url = { version = "0.1", default-features = false, features = ["std", "serde"] } flate2 = { version = "1.0.23", default-features = false, features = ["rust_backend"], optional = true } form_urlencoded = { version = "1.0.1", default-features = false } -identity_core = { version = "=0.6.0", path = "../identity_core" } +identity_core = { version = "=0.7.0-alpha.1", path = "../identity_core" } indexmap = { version = "1.7", default-features = false, features = ["std", "serde-1"] } roaring = { version = "0.9.0", default-features = false, optional = true } serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] } diff --git a/identity_diff/Cargo.toml b/identity_diff/Cargo.toml index 3c92f6a4a3..71a84a12d5 100644 --- a/identity_diff/Cargo.toml +++ b/identity_diff/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity-diff" -version = "0.6.0" +version = "0.7.0-alpha.1" authors = ["IOTA Stiftung"] edition = "2021" homepage = "https://www.iota.org" @@ -11,7 +11,7 @@ repository = "https://github.com/iotaledger/identity.rs" description = "`Diff` trait to compute and merge data structure differences." [dependencies] -identity-diff-derive = { version = "=0.6.0", path = "derive", optional = true } +identity-diff-derive = { version = "=0.7.0-alpha.1", path = "derive", optional = true } serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0" } strum = { version = "0.24.0", default-features = false, features = ["std", "derive"] } diff --git a/identity_diff/derive/Cargo.toml b/identity_diff/derive/Cargo.toml index 9dd3bb32d4..5489e18976 100644 --- a/identity_diff/derive/Cargo.toml +++ b/identity_diff/derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity-diff-derive" -version = "0.6.0" +version = "0.7.0-alpha.1" authors = ["IOTA Stiftung"] edition = "2021" homepage = "https://www.iota.org" diff --git a/identity_iota/Cargo.toml b/identity_iota/Cargo.toml index 4e212b8076..b4505136a8 100644 --- a/identity_iota/Cargo.toml +++ b/identity_iota/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity_iota" -version = "0.6.0" +version = "0.7.0-alpha.1" authors = ["IOTA Stiftung"] edition = "2021" homepage = "https://www.iota.org" @@ -12,11 +12,11 @@ rust-version = "1.62" description = "Framework for Self-Sovereign Identity with IOTA DID." [dependencies] -identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } -identity_credential = { version = "=0.6.0", path = "../identity_credential", features = ["validator"], default-features = false } -identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } -identity_iota_core = { version = "=0.6.0", path = "../identity_iota_core", default-features = false } -identity_resolver = { version = "=0.6.0", path = "../identity_resolver", default-features = false, optional = true } +identity_core = { version = "=0.7.0-alpha.1", path = "../identity_core", default-features = false } +identity_credential = { version = "=0.7.0-alpha.1", path = "../identity_credential", features = ["validator"], default-features = false } +identity_did = { version = "=0.7.0-alpha.1", path = "../identity_did", default-features = false } +identity_iota_core = { version = "=0.7.0-alpha.1", path = "../identity_iota_core", default-features = false } +identity_resolver = { version = "=0.7.0-alpha.1", path = "../identity_resolver", default-features = false, optional = true } [dev-dependencies] anyhow = "1.0.64" diff --git a/identity_iota_core/Cargo.toml b/identity_iota_core/Cargo.toml index 90aa0e211d..ba0f893329 100644 --- a/identity_iota_core/Cargo.toml +++ b/identity_iota_core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity_iota_core" -version = "0.6.0" +version = "0.7.0-alpha.1" authors = ["IOTA Stiftung"] edition = "2021" homepage = "https://www.iota.org" @@ -14,9 +14,9 @@ description = "An IOTA Ledger integration for the IOTA DID Method." [dependencies] # Ensure bee-block always matches the version used by iota-client. bee-block = { version = "1.0.0-beta.7", default-features = false, features = ["std"], optional = true } -identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } -identity_credential = { version = "=0.6.0", path = "../identity_credential", default-features = false, features = ["validator"] } -identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } +identity_core = { version = "=0.7.0-alpha.1", path = "../identity_core", default-features = false } +identity_credential = { version = "=0.7.0-alpha.1", path = "../identity_credential", default-features = false, features = ["validator"] } +identity_did = { version = "=0.7.0-alpha.1", path = "../identity_did", default-features = false } async-trait = { version = "0.1.56", default-features = false, optional = true } futures = { version = "0.3" } diff --git a/identity_resolver/Cargo.toml b/identity_resolver/Cargo.toml index 3fdcfa4769..82f3fbc02d 100644 --- a/identity_resolver/Cargo.toml +++ b/identity_resolver/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity_resolver" -version = "0.6.0" +version = "0.7.0-alpha.1" authors = ["IOTA Stiftung"] edition = "2021" homepage = "https://www.iota.org" @@ -15,15 +15,15 @@ description = "DID Resolution utilities for the identity.rs library." # This is currently necessary for the ResolutionHandler trait. This can be made an optional dependency if alternative ways of attaching handlers are introduced. async-trait = { version = "0.1", default-features = false } futures = { version = "0.3" } -identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } -identity_credential = { version = "=0.6.0", path = "../identity_credential", default-features = false, features = ["validator"] } -identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } +identity_core = { version = "=0.7.0-alpha.1", path = "../identity_core", default-features = false } +identity_credential = { version = "=0.7.0-alpha.1", path = "../identity_credential", default-features = false, features = ["validator"] } +identity_did = { version = "=0.7.0-alpha.1", path = "../identity_did", default-features = false } serde = { version = "1.0", default-features = false, features = ["std", "derive"] } strum = { version = "0.21", features = ["derive"] } thiserror = { version = "1.0", default-features = false } [dependencies.identity_iota_core] -version = "=0.6.0" +version = "=0.7.0-alpha.1" path = "../identity_iota_core" default-features = false features = ["send-sync-client-ext", "iota-client"] From 50f51ecefc090e2defa19b3629f5a4cb74b2646c Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Mon, 19 Sep 2022 14:07:03 +0200 Subject: [PATCH 74/89] Remove keywords to satisfy crates.io requirement (#1034) --- identity_iota_core/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/identity_iota_core/Cargo.toml b/identity_iota_core/Cargo.toml index ba0f893329..410d12b862 100644 --- a/identity_iota_core/Cargo.toml +++ b/identity_iota_core/Cargo.toml @@ -4,7 +4,7 @@ version = "0.7.0-alpha.1" authors = ["IOTA Stiftung"] edition = "2021" homepage = "https://www.iota.org" -keywords = ["iota", "tangle", "utxo", "shimmer", "stardust", "identity"] +keywords = ["iota", "tangle", "utxo", "shimmer", "identity"] license = "Apache-2.0" readme = "./README.md" repository = "https://github.com/iotaledger/identity.rs" From 0fff1d1f16e9451a2549b9e41e454f986227aece Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Mon, 19 Sep 2022 16:37:34 +0200 Subject: [PATCH 75/89] Bump versions of legacy crates (#1035) --- bindings/stronghold-nodejs/Cargo.lock | 8 ++++---- identity_account_storage/Cargo.toml | 4 ++-- identity_iota_core_legacy/Cargo.toml | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/bindings/stronghold-nodejs/Cargo.lock b/bindings/stronghold-nodejs/Cargo.lock index 6edd12e8cb..22ffbb7e14 100644 --- a/bindings/stronghold-nodejs/Cargo.lock +++ b/bindings/stronghold-nodejs/Cargo.lock @@ -715,7 +715,7 @@ dependencies = [ [[package]] name = "identity-diff" -version = "0.6.0" +version = "0.7.0-alpha.1" dependencies = [ "serde", "serde_json", @@ -725,7 +725,7 @@ dependencies = [ [[package]] name = "identity-stronghold-nodejs" -version = "0.6.0" +version = "0.7.0-alpha.1" dependencies = [ "identity_account_storage", "identity_core", @@ -762,7 +762,7 @@ dependencies = [ [[package]] name = "identity_core" -version = "0.6.0" +version = "0.7.0-alpha.1" dependencies = [ "identity-diff", "iota-crypto 0.12.1", @@ -780,7 +780,7 @@ dependencies = [ [[package]] name = "identity_did" -version = "0.6.0" +version = "0.7.0-alpha.1" dependencies = [ "did_url", "form_urlencoded", diff --git a/identity_account_storage/Cargo.toml b/identity_account_storage/Cargo.toml index a7082f22a1..3564a6874b 100644 --- a/identity_account_storage/Cargo.toml +++ b/identity_account_storage/Cargo.toml @@ -19,8 +19,8 @@ async-trait = { version = "0.1", default-features = false } function_name = { version = "0.2", default-features = false, optional = true } futures = { version = "0.3", optional = true } hashbrown = { version = "0.11", features = ["serde"] } -identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } -identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } +identity_core = { version = "0.7.0-alpha.1", path = "../identity_core", default-features = false } +identity_did = { version = "0.7.0-alpha.1", path = "../identity_did", default-features = false } identity_iota_core_legacy = { version = "=0.6.0", path = "../identity_iota_core_legacy", default-features = false } iota-crypto = { version = "0.12.1", default-features = false, features = ["hmac", "pbkdf", "sha", "std", "aes-gcm", "aes-kw"] } iota_stronghold = { version = "0.6.4", default-features = false, features = ["std"], optional = true } diff --git a/identity_iota_core_legacy/Cargo.toml b/identity_iota_core_legacy/Cargo.toml index af12d7a969..a6e7123e52 100644 --- a/identity_iota_core_legacy/Cargo.toml +++ b/identity_iota_core_legacy/Cargo.toml @@ -15,8 +15,8 @@ description = "Core data structures for the IOTA DID Method." [dependencies] bee-message = { version = "0.1.6", default-features = false, features = ["serde"] } -identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } -identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } +identity_core = { version = "=0.7.0-alpha.1", path = "../identity_core", default-features = false } +identity_did = { version = "=0.7.0-alpha.1", path = "../identity_did", default-features = false } lazy_static = { version = "1.4", default-features = false } serde = { version = "1.0", default-features = false, features = ["std", "derive"] } strum = { version = "0.24.0", default-features = false, features = ["std", "derive"] } From ae0a01667997b3ab1e38c0823c05815ec1edebf6 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Mon, 19 Sep 2022 16:38:30 +0200 Subject: [PATCH 76/89] Port advanced Stardust examples to Wasm (#1018) * Refactor basic examples * Port 0_did_controls_did to Wasm * Expose `to_alias_id` method in Wasm * Remove unnecessary console logs * Remove unused imports * Partially impl 1_did_issues_nft * Remove unused imports; don't slice alias id * Finish porting 1_did_issues_nft * Improve variable name * Impl 2_nft_owns_did * Improve 2_nft_owns_did Rust example comments * Use convenience functions * Generate a new address * Impl 3_did_issues_tokens * Add `big-integer` dependency * Use iota-scoped package for new examples * Port 4_key_exchange * Create directory structure * Remove `ex` prefix from example names * Add comment * Remove unused imports * Fix wiki links to examples * Add advanced examples to Readme * Add new examples as tests * Update package-lock.json * Update api-reference.md * Document `create_did` utility function * Apply suggestions from code review * Remove `webpack` & `webpack-cli` * Use package-lock.json from dev * Update package-lock.json --- bindings/wasm/docs/api-reference.md | 7 + bindings/wasm/examples/README.md | 34 +- .../0_create_did.ts} | 77 +- .../1_update_did.ts} | 22 +- .../2_resolve_did.ts} | 22 +- .../3_deactivate_did.ts} | 22 +- .../4_delete_did.ts} | 23 +- .../src/1_advanced/0_did_controls_did.ts | 130 +++ .../src/1_advanced/1_did_issues_nft.ts | 135 +++ .../examples/src/1_advanced/2_nft_owns_did.ts | 140 +++ .../src/1_advanced/3_did_issues_tokens.ts | 181 ++++ .../examples/src/1_advanced/4_key_exchange.ts | 128 +++ bindings/wasm/examples/src/main.ts | 37 +- .../{ex0_create_did.ts => 0_create_did.ts} | 2 +- .../examples/src/tests/0_did_controls_did.ts | 8 + .../examples/src/tests/1_did_issues_nft.ts | 8 + .../{ex1_update_did.ts => 1_update_did.ts} | 2 +- .../wasm/examples/src/tests/2_nft_owns_did.ts | 8 + .../{ex2_resolve_did.ts => 2_resolve_did.ts} | 2 +- ...{ex4_delete_did.ts => 3_deactivate_did.ts} | 4 +- .../examples/src/tests/3_did_issues_tokens.ts | 8 + ...{ex3_deactivate_did.ts => 4_delete_did.ts} | 2 +- .../wasm/examples/src/tests/4_key_exchange.ts | 8 + bindings/wasm/examples/src/util.ts | 127 +++ bindings/wasm/package-lock.json | 848 ++++-------------- bindings/wasm/package.json | 8 +- bindings/wasm/src/iota/iota_did.rs | 7 + .../decentralized_identifiers/create.mdx | 10 +- .../decentralized_identifiers/delete.mdx | 8 +- .../decentralized_identifiers/resolve.mdx | 4 +- .../decentralized_identifiers/update.mdx | 4 +- examples/1_advanced/2_nft_owns_did.rs | 8 +- examples/1_advanced/3_did_issues_tokens.rs | 5 +- 33 files changed, 1198 insertions(+), 841 deletions(-) rename bindings/wasm/examples/src/{ex0_create_did.ts => 0_basic/0_create_did.ts} (52%) rename bindings/wasm/examples/src/{ex1_update_did.ts => 0_basic/1_update_did.ts} (75%) rename bindings/wasm/examples/src/{ex2_resolve_did.ts => 0_basic/2_resolve_did.ts} (50%) rename bindings/wasm/examples/src/{ex3_deactivate_did.ts => 0_basic/3_deactivate_did.ts} (76%) rename bindings/wasm/examples/src/{ex4_delete_did.ts => 0_basic/4_delete_did.ts} (52%) create mode 100644 bindings/wasm/examples/src/1_advanced/0_did_controls_did.ts create mode 100644 bindings/wasm/examples/src/1_advanced/1_did_issues_nft.ts create mode 100644 bindings/wasm/examples/src/1_advanced/2_nft_owns_did.ts create mode 100644 bindings/wasm/examples/src/1_advanced/3_did_issues_tokens.ts create mode 100644 bindings/wasm/examples/src/1_advanced/4_key_exchange.ts rename bindings/wasm/examples/src/tests/{ex0_create_did.ts => 0_create_did.ts} (79%) create mode 100644 bindings/wasm/examples/src/tests/0_did_controls_did.ts create mode 100644 bindings/wasm/examples/src/tests/1_did_issues_nft.ts rename bindings/wasm/examples/src/tests/{ex1_update_did.ts => 1_update_did.ts} (78%) create mode 100644 bindings/wasm/examples/src/tests/2_nft_owns_did.ts rename bindings/wasm/examples/src/tests/{ex2_resolve_did.ts => 2_resolve_did.ts} (78%) rename bindings/wasm/examples/src/tests/{ex4_delete_did.ts => 3_deactivate_did.ts} (61%) create mode 100644 bindings/wasm/examples/src/tests/3_did_issues_tokens.ts rename bindings/wasm/examples/src/tests/{ex3_deactivate_did.ts => 4_delete_did.ts} (78%) create mode 100644 bindings/wasm/examples/src/tests/4_key_exchange.ts create mode 100644 bindings/wasm/examples/src/util.ts diff --git a/bindings/wasm/docs/api-reference.md b/bindings/wasm/docs/api-reference.md index 01e8420d1e..563b665331 100644 --- a/bindings/wasm/docs/api-reference.md +++ b/bindings/wasm/docs/api-reference.md @@ -1624,6 +1624,7 @@ A DID conforming to the IOTA DID method specification. * [.methodId()](#IotaDID+methodId) ⇒ string * [.join(segment)](#IotaDID+join) ⇒ [IotaDIDUrl](#IotaDIDUrl) * [.toUrl()](#IotaDID+toUrl) ⇒ [IotaDIDUrl](#IotaDIDUrl) + * [.toAliasId()](#IotaDID+toAliasId) ⇒ string * [.intoUrl()](#IotaDID+intoUrl) ⇒ [IotaDIDUrl](#IotaDIDUrl) * [.toString()](#IotaDID+toString) ⇒ string * [.toJSON()](#IotaDID+toJSON) ⇒ any @@ -1717,6 +1718,12 @@ Construct a new `DIDUrl` by joining with a relative DID Url string. ### did.toUrl() ⇒ [IotaDIDUrl](#IotaDIDUrl) Clones the `DID` into a `DIDUrl`. +**Kind**: instance method of [IotaDID](#IotaDID) + + +### did.toAliasId() ⇒ string +Creates an AliasId from the DID tag. + **Kind**: instance method of [IotaDID](#IotaDID) diff --git a/bindings/wasm/examples/README.md b/bindings/wasm/examples/README.md index d0d688183c..040eeee67b 100644 --- a/bindings/wasm/examples/README.md +++ b/bindings/wasm/examples/README.md @@ -26,19 +26,35 @@ Then, run an example using: npm run example:node -- ``` -For instance, to run the `ex0_create_did` example execute: +For instance, to run the `0_create_did` example execute: ```bash -npm run example:node -- ex0_create_did +npm run example:node -- 0_create_did ``` -| # | Name | Details | -|-----|-------------------------------------------------|------------------------------------------------------------------| -| 0 | [ex0_create_did](src/ex0_create_did.ts) | Create a DID Document and publish it in a new Alias Output. | -| 1 | [ex1_update_did](src/ex1_update_did.ts) | Update a DID document in an existing Alias Output. | -| 2 | [ex2_resolve_did](src/ex2_resolve_did.ts) | Resolve an existing DID in an Alias Output. | -| 3 | [ex3_deactivate_did](src/ex3_deactivate_did.ts) | Deactivate a DID in an Alias Output. | -| 4 | [ex4_delete_did](src/ex4_delete_did.ts) | Delete a DID in an Alias Output, reclaiming the storage deposit. | +## Basic Examples + +The following basic CRUD (Create, Read, Update, Delete) examples are available: + +| Name | Information | +| :-------------------------------------------------- | :----------------------------------------------------------------------------------- | +| [0_create_did](src/0_basic/0_create_did.ts) | Demonstrates how to create a DID Document and publish it in a new Alias Output. | +| [1_update_did](src/0_basic/1_update_did.ts) | Demonstrates how to update a DID document in an existing Alias Output. | +| [2_resolve_did](src/0_basic/2_resolve_did.ts) | Demonstrates how to resolve an existing DID in an Alias Output. | +| [3_deactivate_did](src/0_basic/3_deactivate_did.ts) | Demonstrates how to deactivate a DID in an Alias Output. | +| [4_delete_did](src/0_basic/4_delete_did.ts) | Demonstrates how to delete a DID in an Alias Output, reclaiming the storage deposit. | + +## Advanced Examples + +The following advanced examples are available: + +| Name | Information | +| :----------------------------------------------------------- | :------------------------------------------------------------------------------------------------------- | +| [0_did_controls_did](src/1_advanced/0_did_controls_did.ts) | Demonstrates how an identity can control another identity. | +| [1_did_issues_nft](src/1_advanced/1_did_issues_nft.ts) | Demonstrates how an identity can issue and own NFTs, and how observers can verify the issuer of the NFT. | +| [2_nft_owns_did](src/1_advanced/2_nft_owns_did.ts) | Demonstrates how an identity can be owned by NFTs, and how observers can verify that relationship. | +| [3_did_issues_tokens](src/1_advanced/3_did_issues_tokens.ts) | Demonstrates how an identity can issue and control a Token Foundry and its tokens. | +| [4_key_exchange](src/1_advanced/4_key_exchange.ts) | Demonstrates Elliptic-curve Diffie-Hellman (ECDH) cryptographic key exchange with DID Documents. | ## Browser diff --git a/bindings/wasm/examples/src/ex0_create_did.ts b/bindings/wasm/examples/src/0_basic/0_create_did.ts similarity index 52% rename from bindings/wasm/examples/src/ex0_create_did.ts rename to bindings/wasm/examples/src/0_basic/0_create_did.ts index 500a74ce38..32b222d3f8 100644 --- a/bindings/wasm/examples/src/ex0_create_did.ts +++ b/bindings/wasm/examples/src/0_basic/0_create_did.ts @@ -9,14 +9,11 @@ import { IotaDocument, IotaIdentityClient, IotaVerificationMethod -} from '../../node'; +} from '../../../node'; import { Bech32Helper, IAliasOutput } from '@iota/iota.js'; import { Bip39 } from "@iota/crypto.js"; -import fetch from "node-fetch"; import { Client, MnemonicSecretManager, SecretManager } from "@iota/iota-client-wasm/node"; - -const API_ENDPOINT = "https://api.testnet.shimmer.network/"; -const FAUCET = "https://faucet.testnet.shimmer.network/api/enqueue"; +import { API_ENDPOINT, ensureAddressHasFunds } from '../util'; /** Demonstrate how to create a DID Document and publish it in a new Alias Output. */ export async function createIdentity(): Promise<{ @@ -75,73 +72,3 @@ export async function createIdentity(): Promise<{ did: published.id() }; } - -/** Request funds from the testnet faucet API, if needed, and wait for them to show in the wallet. */ -async function ensureAddressHasFunds(client: Client, addressBech32: string) { - let balance = await getAddressBalance(client, addressBech32); - if (balance > 0) { - return; - } - - await requestFundsFromFaucet(addressBech32); - - for (let i = 0; i < 9; i++) { - // Wait for the funds to reflect. - await new Promise(f => setTimeout(f, 5000)); - - let balance = await getAddressBalance(client, addressBech32); - if (balance > 0) { - break; - } - } -} - -/** Returns the balance of the given Bech32-encoded address. */ -async function getAddressBalance(client: Client, addressBech32: string): Promise { - // TODO: use the `addresses/ed25519/` API to get the balance? - const outputIds = await client.basicOutputIds([ - { address: addressBech32 }, - { hasExpiration: false }, - { hasTimelock: false }, - { hasStorageDepositReturn: false } - ]); - const outputs = await client.getOutputs(outputIds); - - let totalAmount = 0; - for (const output of outputs) { - totalAmount += Number(output.output.amount); - } - - return totalAmount; -} - -/** Request tokens from the testnet faucet API. */ -async function requestFundsFromFaucet(addressBech32: string) { - const requestObj = JSON.stringify({ address: addressBech32 }); - let errorMessage, data; - try { - const response = await fetch(FAUCET, { - method: "POST", - headers: { - Accept: "application/json", - "Content-Type": "application/json", - }, - body: requestObj, - }); - if (response.status === 202) { - errorMessage = "OK"; - } else if (response.status === 429) { - errorMessage = "too many requests, please try again later."; - } else { - data = await response.json(); - // @ts-ignore - errorMessage = data.error.message; - } - } catch (error) { - errorMessage = error; - } - - if (errorMessage != "OK") { - throw new Error(`failed to get funds from faucet: ${errorMessage}`); - } -} diff --git a/bindings/wasm/examples/src/ex1_update_did.ts b/bindings/wasm/examples/src/0_basic/1_update_did.ts similarity index 75% rename from bindings/wasm/examples/src/ex1_update_did.ts rename to bindings/wasm/examples/src/0_basic/1_update_did.ts index bad987fca0..bd260b52b3 100644 --- a/bindings/wasm/examples/src/ex1_update_did.ts +++ b/bindings/wasm/examples/src/0_basic/1_update_did.ts @@ -1,15 +1,27 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { MethodRelationship, IotaDocument, IotaService, Timestamp, IotaVerificationMethod, KeyPair, KeyType, MethodScope } from '../../node'; +import { MethodRelationship, IotaDocument, IotaService, Timestamp, IotaVerificationMethod, KeyPair, KeyType, MethodScope, IotaIdentityClient } from '../../../node'; import { IAliasOutput, IRent, TransactionHelper } from '@iota/iota.js'; - -import { createIdentity } from "./ex0_create_did"; +import { API_ENDPOINT, createDid } from '../util'; +import { Client, MnemonicSecretManager } from '@iota/iota-client-wasm/node'; +import { Bip39 } from '@iota/crypto.js'; /** Demonstrates how to update a DID document in an existing Alias Output. */ export async function updateIdentity() { - // Creates a new wallet and identity (see "ex0_create_did" example). - const { didClient, secretManager, did } = await createIdentity(); + const client = new Client({ + primaryNode: API_ENDPOINT, + localPow: true, + }); + const didClient = new IotaIdentityClient(client); + + // Generate a random mnemonic for our wallet. + const secretManager: MnemonicSecretManager = { + Mnemonic: Bip39.randomMnemonic() + }; + + // Creates a new wallet and identity (see "0_create_did" example). + const { did } = await createDid(client, secretManager); // Resolve the latest state of the document. // Technically this is equivalent to the document above. diff --git a/bindings/wasm/examples/src/ex2_resolve_did.ts b/bindings/wasm/examples/src/0_basic/2_resolve_did.ts similarity index 50% rename from bindings/wasm/examples/src/ex2_resolve_did.ts rename to bindings/wasm/examples/src/0_basic/2_resolve_did.ts index b48757f4c7..fa8330c244 100644 --- a/bindings/wasm/examples/src/ex2_resolve_did.ts +++ b/bindings/wasm/examples/src/0_basic/2_resolve_did.ts @@ -1,15 +1,27 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import type { IotaDocument } from '../../node'; +import { IotaDocument, IotaIdentityClient } from '../../../node'; import type { IAliasOutput } from '@iota/iota.js'; - -import { createIdentity } from "./ex0_create_did"; +import { API_ENDPOINT, createDid } from '../util'; +import { Bip39 } from '@iota/crypto.js'; +import { Client, MnemonicSecretManager } from '@iota/iota-client-wasm/node'; /** Demonstrates how to resolve an existing DID in an Alias Output. */ export async function resolveIdentity() { - // Creates a new wallet and identity (see "ex0_create_did" example). - const { didClient, did } = await createIdentity(); + const client = new Client({ + primaryNode: API_ENDPOINT, + localPow: true, + }); + const didClient = new IotaIdentityClient(client); + + // Generate a random mnemonic for our wallet. + const secretManager: MnemonicSecretManager = { + Mnemonic: Bip39.randomMnemonic() + }; + + // Creates a new wallet and identity (see "0_create_did" example). + const { did } = await createDid(client, secretManager); // Resolve the associated Alias Output and extract the DID document from it. const resolved: IotaDocument = await didClient.resolveDid(did); diff --git a/bindings/wasm/examples/src/ex3_deactivate_did.ts b/bindings/wasm/examples/src/0_basic/3_deactivate_did.ts similarity index 76% rename from bindings/wasm/examples/src/ex3_deactivate_did.ts rename to bindings/wasm/examples/src/0_basic/3_deactivate_did.ts index f9686cd926..389ac14017 100644 --- a/bindings/wasm/examples/src/ex3_deactivate_did.ts +++ b/bindings/wasm/examples/src/0_basic/3_deactivate_did.ts @@ -1,15 +1,27 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import type { IotaDocument } from '../../node'; +import { IotaDocument, IotaIdentityClient } from '../../../node'; import { IAliasOutput, IRent, TransactionHelper } from '@iota/iota.js'; - -import { createIdentity } from "./ex0_create_did"; +import { API_ENDPOINT, createDid } from '../util'; +import { Bip39 } from '@iota/crypto.js'; +import { Client, MnemonicSecretManager } from '@iota/iota-client-wasm/node'; /** Demonstrates how to deactivate a DID in an Alias Output. */ export async function deactivateIdentity() { - // Creates a new wallet and identity (see "ex0_create_did" example). - const { didClient, secretManager, did } = await createIdentity(); + const client = new Client({ + primaryNode: API_ENDPOINT, + localPow: true, + }); + const didClient = new IotaIdentityClient(client); + + // Generate a random mnemonic for our wallet. + const secretManager: MnemonicSecretManager = { + Mnemonic: Bip39.randomMnemonic() + }; + + // Creates a new wallet and identity (see "0_create_did" example). + const { did } = await createDid(client, secretManager); // Resolve the latest state of the DID document, so we can reactivate it later. let document: IotaDocument = await didClient.resolveDid(did); diff --git a/bindings/wasm/examples/src/ex4_delete_did.ts b/bindings/wasm/examples/src/0_basic/4_delete_did.ts similarity index 52% rename from bindings/wasm/examples/src/ex4_delete_did.ts rename to bindings/wasm/examples/src/0_basic/4_delete_did.ts index 976bc5977d..0117744efb 100644 --- a/bindings/wasm/examples/src/ex4_delete_did.ts +++ b/bindings/wasm/examples/src/0_basic/4_delete_did.ts @@ -1,18 +1,31 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import {createIdentity} from "./ex0_create_did"; -import {Bech32Helper} from "@iota/iota.js"; +import { Client, MnemonicSecretManager } from "@iota/iota-client-wasm/node"; +import { IotaIdentityClient } from "../../../node"; +import { API_ENDPOINT, createDid } from "../util"; +import { Bip39 } from "@iota/crypto.js"; /** Demonstrates how to delete a DID in an Alias Output, reclaiming the storage deposit. */ export async function deleteIdentity() { - // Creates a new wallet and identity (see "ex0_create_did" example). - const {didClient, secretManager, walletAddressBech32, did} = await createIdentity(); + const client = new Client({ + primaryNode: API_ENDPOINT, + localPow: true, + }); + const didClient = new IotaIdentityClient(client); + + // Generate a random mnemonic for our wallet. + const secretManager: MnemonicSecretManager = { + Mnemonic: Bip39.randomMnemonic() + }; + + // Creates a new wallet and identity (see "0_create_did" example). + const { address, did } = await createDid(client, secretManager); // Deletes the Alias Output and its contained DID Document, rendering the DID permanently destroyed. // This operation is *not* reversible. // Deletion can only be done by the governor of the Alias Output. - const destinationAddress = Bech32Helper.addressFromBech32(walletAddressBech32, await didClient.getNetworkHrp()); + const destinationAddress = address; await didClient.deleteDidOutput(secretManager, destinationAddress, did); // Attempting to resolve a deleted DID results in a `NotFound` error. diff --git a/bindings/wasm/examples/src/1_advanced/0_did_controls_did.ts b/bindings/wasm/examples/src/1_advanced/0_did_controls_did.ts new file mode 100644 index 0000000000..aecee50da7 --- /dev/null +++ b/bindings/wasm/examples/src/1_advanced/0_did_controls_did.ts @@ -0,0 +1,130 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { IotaDocument, IotaIdentityClient, MethodScope, KeyPair, KeyType, IotaVerificationMethod, IotaDID } from '../../../node'; +import { AddressTypes, IAliasOutput, IRent, TransactionHelper, ALIAS_ADDRESS_TYPE, IAliasAddress, ISSUER_FEATURE_TYPE, IStateControllerAddressUnlockCondition, STATE_CONTROLLER_ADDRESS_UNLOCK_CONDITION_TYPE } from '@iota/iota.js'; +import { API_ENDPOINT, createDid } from '../util'; +import { Client, MnemonicSecretManager } from '@iota/iota-client-wasm/node'; +import { Bip39 } from '@iota/crypto.js'; +import { Converter } from '@iota/util.js'; + +/** Demonstrates how an identity can control another identity. + +For this example, we consider the case where a parent company's DID controls the DID of a subsidiary. */ +export async function didControlsDid() { + // ======================================================== + // Create the company's and subsidiary's Alias Output DIDs. + // ======================================================== + + // Create a new client to interact with the IOTA ledger. + const client = new Client({ + primaryNode: API_ENDPOINT, + localPow: true, + }); + const didClient = new IotaIdentityClient(client); + + // Generate a random mnemonic for our wallet. + const secretManager: MnemonicSecretManager = { + Mnemonic: Bip39.randomMnemonic() + }; + + // Creates a new wallet and identity (see "0_create_did" example). + var { did: companyDid } = await createDid(client, secretManager); + + // Get the current byte costs. + const rentStructure: IRent = await didClient.getRentStructure(); + + // Get the Bech32 human-readable part (HRP) of the network. + const networkName: string = await didClient.getNetworkHrp(); + + // Construct a new DID document for the subsidiary. + var subsidiaryDocument: IotaDocument = new IotaDocument(networkName); + + // Create the Alias Address of the company. + const companyAliasAddress: AddressTypes = { + aliasId: companyDid.toAliasId(), + type: ALIAS_ADDRESS_TYPE + }; + + // Create a DID for the subsidiary that is controlled by the parent company's DID. + // This means the subsidiary's Alias Output can only be updated or destroyed by + // the state controller or governor of the company's Alias Output respectively. + var subsidiaryAlias: IAliasOutput = await didClient.newDidOutput( + companyAliasAddress, + subsidiaryDocument, + rentStructure); + + // Optionally, we can mark the company as the issuer of the subsidiary DID. + // This allows to verify trust relationships between DIDs, as a resolver can + // verify that the subsidiary DID was created by the parent company. + subsidiaryAlias = { + ...subsidiaryAlias, + immutableFeatures: [ + { + type: ISSUER_FEATURE_TYPE, + address: companyAliasAddress, + } + ] + } + + // Adding the issuer feature means we have to recalculate the required storage deposit. + subsidiaryAlias.amount = TransactionHelper.getStorageDeposit(subsidiaryAlias, rentStructure).toString(); + + // Publish the subsidiary's DID. + subsidiaryDocument = await didClient.publishDidOutput(secretManager, subsidiaryAlias); + + // ===================================== + // Update the subsidiary's Alias Output. + // ===================================== + + // Add a verification method to the subsidiary. + // This only serves as an example for updating the subsidiary DID. + const keyPair: KeyPair = new KeyPair(KeyType.Ed25519); + const method: IotaVerificationMethod = new IotaVerificationMethod( + subsidiaryDocument.id(), + KeyType.Ed25519, + keyPair.public(), + "#key-2" + ); + subsidiaryDocument.insertMethod(method, MethodScope.VerificationMethod()); + + // Update the subsidiary's Alias Output with the updated document + // and increase the storage deposit. + const subsidiaryAliasUpdate: IAliasOutput = await didClient.updateDidOutput(subsidiaryDocument); + subsidiaryAliasUpdate.amount = TransactionHelper.getStorageDeposit(subsidiaryAliasUpdate, rentStructure).toString(); + + // Publish the updated subsidiary's DID. + // + // This works because `secret_manager` can unlock the company's Alias Output, + // which is required in order to update the subsidiary's Alias Output. + subsidiaryDocument = await didClient.publishDidOutput(secretManager, subsidiaryAliasUpdate); + + // =================================================================== + // Determine the controlling company's DID given the subsidiary's DID. + // =================================================================== + + // Resolve the subsidiary's Alias Output. + const subsidiaryOutput: IAliasOutput = await didClient.resolveDidOutput(subsidiaryDocument.id()); + + // Extract the company's Alias Id from the state controller unlock condition. + // + // If instead we wanted to determine the original creator of the DID, + // we could inspect the issuer feature. This feature needs to be set when creating the DID. + // + // Non-null assertion is safe to use since every Alias Output has a state controller unlock condition. + // Cast to IStateControllerAddressUnlockCondition is safe as we check the type in find. + const stateControllerUnlockCondition: IStateControllerAddressUnlockCondition = + subsidiaryOutput.unlockConditions.find(unlockCondition => unlockCondition.type == STATE_CONTROLLER_ADDRESS_UNLOCK_CONDITION_TYPE)! as IStateControllerAddressUnlockCondition + + // Cast to IAliasAddress is safe because we set an Alias Address earlier. + const companyAliasId: string = (stateControllerUnlockCondition.address as IAliasAddress).aliasId; + + // Reconstruct the company's DID from the Alias Id and the network. + companyDid = new IotaDID(Converter.hexToBytes(companyAliasId), networkName); + + // Resolve the company's DID document. + const companyDocument: IotaDocument = await didClient.resolveDid(companyDid); + + console.log("Company ", JSON.stringify(companyDocument, null, 2)); + console.log("Subsidiary ", JSON.stringify(subsidiaryDocument, null, 2)); +} diff --git a/bindings/wasm/examples/src/1_advanced/1_did_issues_nft.ts b/bindings/wasm/examples/src/1_advanced/1_did_issues_nft.ts new file mode 100644 index 0000000000..3f6cac4c7b --- /dev/null +++ b/bindings/wasm/examples/src/1_advanced/1_did_issues_nft.ts @@ -0,0 +1,135 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { IotaDocument, IotaIdentityClient, IotaDID } from '../../../node'; +import { AddressTypes, IRent, TransactionHelper, ALIAS_ADDRESS_TYPE, ISSUER_FEATURE_TYPE, NFT_OUTPUT_TYPE, METADATA_FEATURE_TYPE, PayloadTypes, TRANSACTION_PAYLOAD_TYPE, ITransactionPayload, TRANSACTION_ESSENCE_TYPE, INftOutput, ADDRESS_UNLOCK_CONDITION_TYPE, IIssuerFeature, IOutputResponse, OutputTypes } from '@iota/iota.js'; +import { API_ENDPOINT, createDid } from '../util'; +import { Client, MnemonicSecretManager } from '@iota/iota-client-wasm/node'; +import { Bip39 } from '@iota/crypto.js'; +import { Converter } from '@iota/util.js'; + +/** Demonstrates how an identity can issue and own NFTs, +and how observers can verify the issuer of the NFT. + +For this example, we consider the case where a manufacturer issues +a digital product passport (DPP) as an NFT. */ +export async function didIssuesNft() { + // ============================================== + // Create the manufacturer's DID and the DPP NFT. + // ============================================== + + // Create a new client to interact with the IOTA ledger. + const client = new Client({ + primaryNode: API_ENDPOINT, + localPow: true, + }); + const didClient = new IotaIdentityClient(client); + + // Generate a random mnemonic for our wallet. + const secretManager: MnemonicSecretManager = { + Mnemonic: Bip39.randomMnemonic() + }; + + // Create a new DID for the manufacturer. (see "0_create_did" example). + var { did: manufacturerDid } = await createDid(client, secretManager); + + // Get the current byte costs. + const rentStructure: IRent = await didClient.getRentStructure(); + + // Get the Bech32 human-readable part (HRP) of the network. + const networkName: string = await didClient.getNetworkHrp(); + + // Create the Alias Address of the manufacturer. + var manufacturerAliasAddress: AddressTypes = { + type: ALIAS_ADDRESS_TYPE, + aliasId: manufacturerDid.toAliasId() + } + + // Create a Digital Product Passport NFT issued by the manufacturer. + let productPassportNft: INftOutput = await client.buildNftOutput({ + nftId: "0x0000000000000000000000000000000000000000000000000000000000000000", + immutableFeatures: [ + { + // Set the manufacturer as the immutable issuer. + type: ISSUER_FEATURE_TYPE, + address: manufacturerAliasAddress, + }, + { + // A proper DPP would hold its metadata here. + type: METADATA_FEATURE_TYPE, + data: Converter.utf8ToHex("Digital Product Passport Metadata", true) + } + ], + unlockConditions: [ + { + // The NFT will initially be owned by the manufacturer. + type: ADDRESS_UNLOCK_CONDITION_TYPE, + address: manufacturerAliasAddress + } + ] + }); + + // Set the appropriate storage deposit. + productPassportNft.amount = TransactionHelper.getStorageDeposit(productPassportNft, rentStructure).toString(); + + // Publish the NFT. + const [blockId, block] = await client.buildAndPostBlock(secretManager, { outputs: [productPassportNft] }); + await client.retryUntilIncluded(blockId); + + // ======================================================== + // Resolve the Digital Product Passport NFT and its issuer. + // ======================================================== + + // Extract the identifier of the NFT from the published block. + // Non-null assertion is safe because we published a block with a payload. + let nftId: string = nft_output_id(block.payload!); + + // Fetch the NFT Output. + const nftOutputId: string = await client.nftOutputId(nftId); + const outputResponse: IOutputResponse = await client.getOutput(nftOutputId); + const output: OutputTypes = outputResponse.output + + // Extract the issuer of the NFT. + let manufacturerAliasId: string; + if (output.type === NFT_OUTPUT_TYPE && output.immutableFeatures) { + const issuerFeature: IIssuerFeature = output.immutableFeatures.find(feature => feature.type === ISSUER_FEATURE_TYPE) as IIssuerFeature; + + if (issuerFeature && issuerFeature.address.type === ALIAS_ADDRESS_TYPE) { + manufacturerAliasId = issuerFeature.address.aliasId + } else { + throw new Error("expected to find issuer feature with an alias address"); + } + } else { + throw new Error("expected NFT output with immutable features"); + } + + // Reconstruct the manufacturer's DID from the Alias Id. + manufacturerDid = new IotaDID(Converter.hexToBytes(manufacturerAliasId), networkName); + + // Resolve the issuer of the NFT. + const manufacturerDocument: IotaDocument = await didClient.resolveDid(manufacturerDid); + + console.log("The issuer of the Digital Product Passport NFT is:", JSON.stringify(manufacturerDocument, null, 2)); +} + +function nft_output_id(payload: PayloadTypes): string { + if (payload.type === TRANSACTION_PAYLOAD_TYPE) { + const txPayload: ITransactionPayload = payload; + const txHash = Converter.bytesToHex(TransactionHelper.getTransactionPayloadHash(txPayload), true); + + if (txPayload.essence.type === TRANSACTION_ESSENCE_TYPE) { + const outputs = txPayload.essence.outputs; + for (let index in txPayload.essence.outputs) { + if (outputs[index].type === NFT_OUTPUT_TYPE) { + const outputId: string = TransactionHelper.outputIdFromTransactionData(txHash, parseInt(index)); + return TransactionHelper.resolveIdFromOutputId(outputId); + } + } + throw new Error("no NFT output in transaction essence"); + } else { + throw new Error("expected transaction essence"); + } + } else { + throw new Error("expected transaction payload"); + } +} diff --git a/bindings/wasm/examples/src/1_advanced/2_nft_owns_did.ts b/bindings/wasm/examples/src/1_advanced/2_nft_owns_did.ts new file mode 100644 index 0000000000..a452141c1c --- /dev/null +++ b/bindings/wasm/examples/src/1_advanced/2_nft_owns_did.ts @@ -0,0 +1,140 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { IotaDocument, IotaIdentityClient } from '../../../node'; +import { AddressTypes, IRent, TransactionHelper, NFT_OUTPUT_TYPE, PayloadTypes, TRANSACTION_PAYLOAD_TYPE, ITransactionPayload, TRANSACTION_ESSENCE_TYPE, INftOutput, ADDRESS_UNLOCK_CONDITION_TYPE, IOutputResponse, OutputTypes, Bech32Helper, NFT_ADDRESS_TYPE, IAliasOutput, IStateControllerAddressUnlockCondition, STATE_CONTROLLER_ADDRESS_UNLOCK_CONDITION_TYPE } from '@iota/iota.js'; +import { API_ENDPOINT, ensureAddressHasFunds } from '../util'; +import { Client, MnemonicSecretManager } from '@iota/iota-client-wasm/node'; +import { Bip39 } from '@iota/crypto.js'; +import { Converter } from '@iota/util.js'; + +/** Demonstrates how an identity can be owned by NFTs, +and how observers can verify that relationship. + +For this example, we consider the case where a car's NFT owns +the DID of the car, so that transferring the NFT also transfers DID ownership. */ +export async function nftOwnsDid() { + // ============================= + // Create the car's NFT and DID. + // ============================= + + // Create a new client to interact with the IOTA ledger. + const client = new Client({ + primaryNode: API_ENDPOINT, + localPow: true, + }); + const didClient = new IotaIdentityClient(client); + + // Generate a random mnemonic for our wallet. + const secretManager: MnemonicSecretManager = { + Mnemonic: Bip39.randomMnemonic() + }; + + // Get the current byte costs. + const rentStructure: IRent = await didClient.getRentStructure(); + + // Get the Bech32 human-readable part (HRP) of the network. + const networkName: string = await didClient.getNetworkHrp(); + + // Create a new address that will own the NFT. + const addressBech32 = (await client.generateAddresses(secretManager, { + accountIndex: 0, + range: { + start: 0, + end: 1, + }, + }))[0]; + const address = Bech32Helper.addressFromBech32(addressBech32, networkName); + + // Get funds for testing from the faucet. + await ensureAddressHasFunds(client, addressBech32); + + // Create the car NFT with an Ed25519 address as the unlock condition. + let carNft: INftOutput = await client.buildNftOutput({ + nftId: "0x0000000000000000000000000000000000000000000000000000000000000000", + unlockConditions: [ + { + // The NFT will initially be owned by the Ed25519 address. + type: ADDRESS_UNLOCK_CONDITION_TYPE, + address + } + ] + }); + + // Set the appropriate storage deposit. + carNft.amount = TransactionHelper.getStorageDeposit(carNft, rentStructure).toString(); + + // Publish the NFT. + const [blockId, block] = await client.buildAndPostBlock(secretManager, { outputs: [carNft] }); + await client.retryUntilIncluded(blockId); + + // Extract the identifier of the NFT from the published block. + // Non-null assertion is safe because we published a block with a payload. + var carNftId: string = nft_output_id(block.payload!) + + // Create the address of the NFT. + const nftAddress: AddressTypes = { + type: NFT_ADDRESS_TYPE, + nftId: carNftId, + }; + + // Construct a DID document for the car. + var carDocument: IotaDocument = new IotaDocument(networkName); + + // Create a new DID for the car that is owned by the car NFT. + var carDidAliasOutput: IAliasOutput = await didClient.newDidOutput(nftAddress, carDocument, rentStructure); + + // Publish the car DID. + carDocument = await didClient.publishDidOutput(secretManager, carDidAliasOutput); + + // ============================================ + // Determine the car's NFT given the car's DID. + // ============================================ + + // Resolve the Alias Output of the DID. + carDidAliasOutput = await didClient.resolveDidOutput(carDocument.id()); + + // Extract the NFT Id from the state controller unlock condition. + const stateControllerUnlockCondition: IStateControllerAddressUnlockCondition = carDidAliasOutput.unlockConditions.find(feature => feature.type === STATE_CONTROLLER_ADDRESS_UNLOCK_CONDITION_TYPE) as IStateControllerAddressUnlockCondition; + if (stateControllerUnlockCondition.address.type === NFT_ADDRESS_TYPE) { + carNftId = stateControllerUnlockCondition.address.nftId + } else { + throw new Error("expected nft address unlock condition"); + } + + // Fetch the NFT Output of the car. + const nftOutputId: string = await client.nftOutputId(carNftId); + const outputResponse: IOutputResponse = await client.getOutput(nftOutputId); + const output: OutputTypes = outputResponse.output + + if (output.type === NFT_OUTPUT_TYPE) { + carNft = output; + } else { + throw new Error("expected nft output type"); + } + + console.log("The car's DID is:", JSON.stringify(carDocument, null, 2)); + console.log("The car's NFT is:", JSON.stringify(carNft, null, 2)); +} + +function nft_output_id(payload: PayloadTypes): string { + if (payload.type === TRANSACTION_PAYLOAD_TYPE) { + const txPayload: ITransactionPayload = payload; + const txHash = Converter.bytesToHex(TransactionHelper.getTransactionPayloadHash(txPayload), true); + + if (txPayload.essence.type === TRANSACTION_ESSENCE_TYPE) { + const outputs = txPayload.essence.outputs; + for (let index in txPayload.essence.outputs) { + if (outputs[index].type === NFT_OUTPUT_TYPE) { + const outputId: string = TransactionHelper.outputIdFromTransactionData(txHash, parseInt(index)); + return TransactionHelper.resolveIdFromOutputId(outputId); + } + } + throw new Error("no NFT output in transaction essence"); + } else { + throw new Error("expected transaction essence"); + } + } else { + throw new Error("expected transaction payload"); + } +} diff --git a/bindings/wasm/examples/src/1_advanced/3_did_issues_tokens.ts b/bindings/wasm/examples/src/1_advanced/3_did_issues_tokens.ts new file mode 100644 index 0000000000..bec626fb18 --- /dev/null +++ b/bindings/wasm/examples/src/1_advanced/3_did_issues_tokens.ts @@ -0,0 +1,181 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { IotaDID, IotaDocument, IotaIdentityClient } from '../../../node'; +import { IRent, TransactionHelper, ADDRESS_UNLOCK_CONDITION_TYPE, IOutputResponse, OutputTypes, Bech32Helper, IAliasOutput, SIMPLE_TOKEN_SCHEME_TYPE, ISimpleTokenScheme, IMMUTABLE_ALIAS_UNLOCK_CONDITION_TYPE, ALIAS_ADDRESS_TYPE, FOUNDRY_OUTPUT_TYPE, IImmutableAliasUnlockCondition, IAliasAddress, IBasicOutput, EXPIRATION_UNLOCK_CONDITION_TYPE } from '@iota/iota.js'; +import { API_ENDPOINT, createDid } from '../util'; +import { Client, MnemonicSecretManager } from '@iota/iota-client-wasm/node'; +import { Converter, HexHelper } from '@iota/util.js'; +import type { IFoundryOutput } from '@iota/types'; +import bigInt from "big-integer"; +import { Bip39 } from '@iota/crypto.js'; + +/** Demonstrates how an identity can issue and control a Token Foundry and its tokens. + +For this example, we consider the case where an authority issues carbon credits +that can be used to pay for carbon emissions or traded on a marketplace. */ +export async function didIssuesTokens() { + // =========================================== + // Create the authority's DID and the foundry. + // =========================================== + + // Create a new client to interact with the IOTA ledger. + const client = new Client({ + primaryNode: API_ENDPOINT, + localPow: true, + }); + const didClient = new IotaIdentityClient(client); + + // Generate a random mnemonic for our wallet. + const secretManager: MnemonicSecretManager = { + Mnemonic: Bip39.randomMnemonic() + }; + + // Create a new DID for the authority. (see "0_create_did" example). + var { did: authorityDid } = await createDid(client, secretManager); + + // Get the current byte costs. + const rentStructure: IRent = await didClient.getRentStructure(); + + // Get the Bech32 human-readable part (HRP) of the network. + const networkName: string = await didClient.getNetworkHrp(); + + // We want to update the foundry counter of the authority's Alias Output, so we create an + // updated version of the output. We pass in the previous document, + // because we don't want to modify it in this update. + var authorityDocument: IotaDocument = await didClient.resolveDid(authorityDid); + const authorityAliasOutput: IAliasOutput = await didClient.updateDidOutput(authorityDocument); + + // We will add one foundry to this Alias Output. + authorityAliasOutput.foundryCounter += 1; + + // Create a token foundry that represents carbon credits. + const tokenScheme: ISimpleTokenScheme = { + type: SIMPLE_TOKEN_SCHEME_TYPE, + mintedTokens: HexHelper.fromBigInt256(bigInt(500_000)), + meltedTokens: HexHelper.fromBigInt256(bigInt(0)), + maximumSupply: HexHelper.fromBigInt256(bigInt(1_000_000)), + }; + + // Create the identifier of the token, which is partially derived from the Alias Address. + const tokenId: string = TransactionHelper.constructTokenId(authorityDid.toAliasId(), 1, tokenScheme.type); + + // Create a token foundry that represents carbon credits. + var carbonCreditsFoundry: IFoundryOutput = await client.buildFoundryOutput({ + tokenScheme, + serialNumber: 1, + // Initially, all carbon credits are owned by the foundry. + nativeTokens: [ + { + id: tokenId, + amount: HexHelper.fromBigInt256(bigInt(500_000)) + } + ], + // The authority is set as the immutable owner. + unlockConditions: [ + { + type: IMMUTABLE_ALIAS_UNLOCK_CONDITION_TYPE, + address: { + type: ALIAS_ADDRESS_TYPE, + aliasId: authorityDid.toAliasId() + } + } + ] + }); + + // Set the appropriate storage deposit. + carbonCreditsFoundry.amount = TransactionHelper.getStorageDeposit(carbonCreditsFoundry, rentStructure).toString(); + + // Publish the foundry. + const [blockId, block] = await client.buildAndPostBlock(secretManager, { outputs: [authorityAliasOutput, carbonCreditsFoundry] }); + await client.retryUntilIncluded(blockId); + + // =================================== + // Resolve foundry and its issuer DID. + // =================================== + + // Get the latest output that contains the foundry. + const carbonCreditsFoundryId: string = tokenId; + const outputId: string = await client.foundryOutputId(carbonCreditsFoundryId) + const outputResponse: IOutputResponse = await client.getOutput(outputId); + const output: OutputTypes = outputResponse.output; + + if (output.type === FOUNDRY_OUTPUT_TYPE) { + carbonCreditsFoundry = output; + } else { + throw new Error("expected foundry output") + } + + // Get the Alias Id of the authority that issued the carbon credits foundry. + // Non-null assertion is safe as each founry output needs to have an immutable alias unlock condition. + const aliasUnlockCondition: IImmutableAliasUnlockCondition = carbonCreditsFoundry.unlockConditions.find(unlockCondition => unlockCondition.type === IMMUTABLE_ALIAS_UNLOCK_CONDITION_TYPE)! as IImmutableAliasUnlockCondition; + + // We know the immutable alias unlock condition contains an alias address. + const authorityAliasId: string = (aliasUnlockCondition.address as IAliasAddress).aliasId + + // Reconstruct the DID of the authority. + authorityDid = new IotaDID(Converter.hexToBytes(authorityAliasId), networkName); + + // Resolve the authority's DID document. + authorityDocument = await didClient.resolveDid(authorityDid); + + console.log("The authority's DID is:", JSON.stringify(authorityDocument, null, 2)); + + // ========================================================= + // Transfer 1000 carbon credits to the address of a company. + // ========================================================= + + // Create a new address that represents the company. + const companyAddressBech32: string = (await client.generateAddresses(secretManager, { + accountIndex: 0, + range: { + start: 1, + end: 2, + }, + }))[0]; + const companyAddress = Bech32Helper.addressFromBech32(companyAddressBech32, networkName); + + // Create a timestamp 24 hours from now. + const tomorrow: number = Math.floor(Date.now() / 1000) + (60 * 60 * 24); + + // Create a basic output containing our carbon credits that we'll send to the company's address. + const basicOutput: IBasicOutput = await client.buildBasicOutput({ + nativeTokens: [ + { + amount: HexHelper.fromBigInt256(bigInt(1000)), + id: tokenId, + } + ], + // Allow the company to claim the credits within 24 hours by using an expiration unlock condition. + unlockConditions: [ + { + type: ADDRESS_UNLOCK_CONDITION_TYPE, + address: companyAddress, + }, + { + type: EXPIRATION_UNLOCK_CONDITION_TYPE, + unixTime: tomorrow, + returnAddress: { + type: ALIAS_ADDRESS_TYPE, + aliasId: authorityAliasId + } + } + ] + }); + + // Reduce the carbon credits in the foundry by the amount that is sent to the company. + carbonCreditsFoundry.nativeTokens = [ + { + amount: HexHelper.fromBigInt256(bigInt(499_000)), + id: tokenId, + } + ]; + + // Publish the Basic Output and the updated foundry. + const [blockId2, block2] = await client.buildAndPostBlock(secretManager, { + outputs: [basicOutput, carbonCreditsFoundry] + }); + await client.retryUntilIncluded(blockId2); + + console.log("Sent carbon credits to", companyAddressBech32); +} diff --git a/bindings/wasm/examples/src/1_advanced/4_key_exchange.ts b/bindings/wasm/examples/src/1_advanced/4_key_exchange.ts new file mode 100644 index 0000000000..6ff752f87e --- /dev/null +++ b/bindings/wasm/examples/src/1_advanced/4_key_exchange.ts @@ -0,0 +1,128 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { IotaDocument, IotaIdentityClient, IotaVerificationMethod, KeyPair, KeyType, MethodScope, X25519 } from '../../../node'; +import { IRent, OutputTypes, Bech32Helper, AddressTypes } from '@iota/iota.js'; +import { API_ENDPOINT, ensureAddressHasFunds } from '../util'; +import { Client, MnemonicSecretManager } from '@iota/iota-client-wasm/node'; +import { Bip39 } from '@iota/crypto.js'; + +/** Demonstrates Elliptic-curve Diffie-Hellman (ECDH) cryptographic key exchange with DID Documents. + +Alice and Bob want to communicate securely by encrypting their messages so only they +can read them. They both publish DID Documents with X25519 public keys and use them +to derive a shared secret key for encryption. */ +export async function keyExchange() { + // ============================== + // Create DIDs for Alice and Bob. + // ============================== + + // Create a new client to interact with the IOTA ledger. + const client = new Client({ + primaryNode: API_ENDPOINT, + localPow: true, + }); + const didClient = new IotaIdentityClient(client); + + // Generate a random mnemonic for our wallet. + const secretManager: MnemonicSecretManager = { + Mnemonic: Bip39.randomMnemonic() + }; + + // Get the Bech32 human-readable part (HRP) of the network. + const networkName: string = await didClient.getNetworkHrp(); + + // Create a new address with funds for testing. + const addressBech32 = (await client.generateAddresses(secretManager, { + accountIndex: 0, + range: { + start: 0, + end: 1, + }, + }))[0]; + const address: AddressTypes = Bech32Helper.addressFromBech32(addressBech32, networkName); + await ensureAddressHasFunds(client, addressBech32); + + // Get the current byte costs. + const rentStructure: IRent = await didClient.getRentStructure(); + + // Alice creates and publishes their DID Document (see 0_create_did and 1_update_did examples). + let aliceDid; + let aliceX25519; + { + // Create a DID Document. + let aliceDocument: IotaDocument = new IotaDocument(networkName); + + // Insert a new X25519 KeyAgreement verification method. + const x25519: KeyPair = new KeyPair(KeyType.X25519); + const method: IotaVerificationMethod = new IotaVerificationMethod(aliceDocument.id(), KeyType.X25519, x25519.public(), "kex-0"); + aliceDocument.insertMethod(method, MethodScope.KeyAgreement()); + + // Publish the DID document. + const aliceOutput: OutputTypes = await didClient.newDidOutput(address, aliceDocument, rentStructure); + aliceDocument = await didClient.publishDidOutput(secretManager, aliceOutput); + + aliceDid = aliceDocument.id(); + aliceX25519 = x25519; + } + + // Alice creates and publishes their DID Document (see 0_create_did and 1_update_did examples). + let bobDid; + let bobX25519; + { + // Create a DID Document. + let bobDocument: IotaDocument = new IotaDocument(networkName); + + // Insert a new X25519 KeyAgreement verification method. + const x25519: KeyPair = new KeyPair(KeyType.X25519); + const method: IotaVerificationMethod = new IotaVerificationMethod(bobDocument.id(), KeyType.X25519, x25519.public(), "kex-0"); + bobDocument.insertMethod(method, MethodScope.KeyAgreement()); + + // Publish the DID document. + const aliceOutput: OutputTypes = await didClient.newDidOutput(address, bobDocument, rentStructure); + bobDocument = await didClient.publishDidOutput(secretManager, aliceOutput); + + bobDid = bobDocument.id(); + bobX25519 = x25519; + } + + // ====================================================================== + // Alice and Bob tell each other their DIDs. They each resolve the + // DID Document of the other to obtain their X25519 public key. + // Note that in practice, they would run this code completely separately. + // ====================================================================== + + let aliceSharedSecretKey; + { + // Alice: resolves Bob's DID Document and extracts their public key. + const bobDocument: IotaDocument = await didClient.resolveDid(bobDid); + const bobMethod: IotaVerificationMethod = bobDocument.resolveMethod("kex-0", MethodScope.KeyAgreement())!; + const bobPublicKey: Uint8Array = bobMethod.data().tryDecode(); + // Compute the shared secret. + aliceSharedSecretKey = X25519.keyExchange(aliceX25519.private(), bobPublicKey); + } + + let bobSharedSecretKey; + { + // Bob: resolves Alice's DID Document and extracts their public key. + const aliceDocument: IotaDocument = await didClient.resolveDid(aliceDid); + const aliceMethod: IotaVerificationMethod = aliceDocument.resolveMethod("kex-0", MethodScope.KeyAgreement())!; + const alicePublicKey: Uint8Array = aliceMethod.data().tryDecode(); + // Compute the shared secret. + bobSharedSecretKey = X25519.keyExchange(bobX25519.private(), alicePublicKey); + } + + // Both shared secret keys computed separately by Alice and Bob will match + // and can then be used to establish encrypted communications. + if (!isArrayEqual(aliceSharedSecretKey, bobSharedSecretKey)) throw new Error("shared secret keys do not match!"); + + console.log(`Diffie-Hellman key exchange successful!`); +} + +function isArrayEqual(a: Uint8Array, b: Uint8Array): boolean { + if (a.length !== b.length) return false; + for (let i = 0; i < a.length; i++) { + if (a[i] !== b[i]) return false; + } + return true; +} diff --git a/bindings/wasm/examples/src/main.ts b/bindings/wasm/examples/src/main.ts index 7c923f40f8..e00e60fe69 100644 --- a/bindings/wasm/examples/src/main.ts +++ b/bindings/wasm/examples/src/main.ts @@ -1,30 +1,45 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import {createIdentity} from "./ex0_create_did"; -import {updateIdentity} from "./ex1_update_did"; -import {resolveIdentity} from "./ex2_resolve_did"; -import {deactivateIdentity} from "./ex3_deactivate_did"; -import {deleteIdentity} from "./ex4_delete_did"; +import { createIdentity } from "./0_basic/0_create_did"; +import { updateIdentity } from "./0_basic/1_update_did"; +import { resolveIdentity } from "./0_basic/2_resolve_did"; +import { deactivateIdentity } from "./0_basic/3_deactivate_did"; +import { deleteIdentity } from "./0_basic/4_delete_did"; +import { didControlsDid } from "./1_advanced/0_did_controls_did"; +import { didIssuesNft } from "./1_advanced/1_did_issues_nft"; +import { nftOwnsDid } from "./1_advanced/2_nft_owns_did"; +import { didIssuesTokens } from "./1_advanced/3_did_issues_tokens"; +import { keyExchange } from "./1_advanced/4_key_exchange"; async function main() { // Extract example name. if (process.argv.length != 3) { - throw "Please specify an example name, e.g. 'ex0_create_did'"; + throw "Please specify an example name, e.g. '0_create_did'"; } const argument = process.argv[2].toLowerCase(); switch (argument) { - case "ex0_create_did": + case "0_create_did": return await createIdentity(); - case "ex1_update_did": + case "1_update_did": return await updateIdentity(); - case "ex2_resolve_did": + case "2_resolve_did": return await resolveIdentity(); - case "ex3_deactivate_did": + case "3_deactivate_did": return await deactivateIdentity(); - case "ex4_delete_did": + case "4_delete_did": return await deleteIdentity(); + case "0_did_controls_did": + return await didControlsDid(); + case "1_did_issues_nft": + return await didIssuesNft(); + case "2_nft_owns_did": + return await nftOwnsDid(); + case "3_did_issues_tokens": + return await didIssuesTokens(); + case "4_key_exchange": + return await keyExchange(); default: throw "Unknown example name: '" + argument + "'"; } diff --git a/bindings/wasm/examples/src/tests/ex0_create_did.ts b/bindings/wasm/examples/src/tests/0_create_did.ts similarity index 79% rename from bindings/wasm/examples/src/tests/ex0_create_did.ts rename to bindings/wasm/examples/src/tests/0_create_did.ts index 65dbfd86b4..2c9a471233 100644 --- a/bindings/wasm/examples/src/tests/ex0_create_did.ts +++ b/bindings/wasm/examples/src/tests/0_create_did.ts @@ -1,4 +1,4 @@ -import {createIdentity} from "../ex0_create_did"; +import {createIdentity} from "../0_basic/0_create_did"; // Only verifies that no uncaught exceptions are thrown, including syntax errors etc. describe("Test node examples", function () { diff --git a/bindings/wasm/examples/src/tests/0_did_controls_did.ts b/bindings/wasm/examples/src/tests/0_did_controls_did.ts new file mode 100644 index 0000000000..b7b57437b7 --- /dev/null +++ b/bindings/wasm/examples/src/tests/0_did_controls_did.ts @@ -0,0 +1,8 @@ +import { didControlsDid } from "../1_advanced/0_did_controls_did"; + +// Only verifies that no uncaught exceptions are thrown, including syntax errors etc. +describe("Test node examples", function () { + it("Did controls Did", async () => { + await didControlsDid(); + }); +}) diff --git a/bindings/wasm/examples/src/tests/1_did_issues_nft.ts b/bindings/wasm/examples/src/tests/1_did_issues_nft.ts new file mode 100644 index 0000000000..b02a76f695 --- /dev/null +++ b/bindings/wasm/examples/src/tests/1_did_issues_nft.ts @@ -0,0 +1,8 @@ +import { didIssuesNft } from "../1_advanced/1_did_issues_nft"; + +// Only verifies that no uncaught exceptions are thrown, including syntax errors etc. +describe("Test node examples", function () { + it("Did issues Nft", async () => { + await didIssuesNft(); + }); +}) diff --git a/bindings/wasm/examples/src/tests/ex1_update_did.ts b/bindings/wasm/examples/src/tests/1_update_did.ts similarity index 78% rename from bindings/wasm/examples/src/tests/ex1_update_did.ts rename to bindings/wasm/examples/src/tests/1_update_did.ts index 2051c94764..2acbaa8b8f 100644 --- a/bindings/wasm/examples/src/tests/ex1_update_did.ts +++ b/bindings/wasm/examples/src/tests/1_update_did.ts @@ -1,4 +1,4 @@ -import {updateIdentity} from "../ex1_update_did"; +import { updateIdentity } from "../0_basic/1_update_did"; // Only verifies that no uncaught exceptions are thrown, including syntax errors etc. describe("Test node examples", function () { diff --git a/bindings/wasm/examples/src/tests/2_nft_owns_did.ts b/bindings/wasm/examples/src/tests/2_nft_owns_did.ts new file mode 100644 index 0000000000..ef52329a6b --- /dev/null +++ b/bindings/wasm/examples/src/tests/2_nft_owns_did.ts @@ -0,0 +1,8 @@ +import { nftOwnsDid } from "../1_advanced/2_nft_owns_did"; + +// Only verifies that no uncaught exceptions are thrown, including syntax errors etc. +describe("Test node examples", function () { + it("Nft owns Did", async () => { + await nftOwnsDid(); + }); +}) diff --git a/bindings/wasm/examples/src/tests/ex2_resolve_did.ts b/bindings/wasm/examples/src/tests/2_resolve_did.ts similarity index 78% rename from bindings/wasm/examples/src/tests/ex2_resolve_did.ts rename to bindings/wasm/examples/src/tests/2_resolve_did.ts index fe9b4c6c18..83ed93a7c9 100644 --- a/bindings/wasm/examples/src/tests/ex2_resolve_did.ts +++ b/bindings/wasm/examples/src/tests/2_resolve_did.ts @@ -1,4 +1,4 @@ -import {resolveIdentity} from "../ex2_resolve_did"; +import { resolveIdentity } from "../0_basic/2_resolve_did"; // Only verifies that no uncaught exceptions are thrown, including syntax errors etc. describe("Test node examples", function () { diff --git a/bindings/wasm/examples/src/tests/ex4_delete_did.ts b/bindings/wasm/examples/src/tests/3_deactivate_did.ts similarity index 61% rename from bindings/wasm/examples/src/tests/ex4_delete_did.ts rename to bindings/wasm/examples/src/tests/3_deactivate_did.ts index 7457409522..1dc0a1a164 100644 --- a/bindings/wasm/examples/src/tests/ex4_delete_did.ts +++ b/bindings/wasm/examples/src/tests/3_deactivate_did.ts @@ -1,8 +1,8 @@ -import {deactivateIdentity} from "../ex3_deactivate_did"; +import { deactivateIdentity } from "../0_basic/3_deactivate_did"; // Only verifies that no uncaught exceptions are thrown, including syntax errors etc. describe("Test node examples", function () { - it("Deactivate Identity", async () => { + it("Deactivate identity", async () => { await deactivateIdentity(); }); }) diff --git a/bindings/wasm/examples/src/tests/3_did_issues_tokens.ts b/bindings/wasm/examples/src/tests/3_did_issues_tokens.ts new file mode 100644 index 0000000000..d140a801ab --- /dev/null +++ b/bindings/wasm/examples/src/tests/3_did_issues_tokens.ts @@ -0,0 +1,8 @@ +import { didIssuesTokens } from "../1_advanced/3_did_issues_tokens"; + +// Only verifies that no uncaught exceptions are thrown, including syntax errors etc. +describe("Test node examples", function () { + it("Did issues tokens", async () => { + await didIssuesTokens(); + }); +}) diff --git a/bindings/wasm/examples/src/tests/ex3_deactivate_did.ts b/bindings/wasm/examples/src/tests/4_delete_did.ts similarity index 78% rename from bindings/wasm/examples/src/tests/ex3_deactivate_did.ts rename to bindings/wasm/examples/src/tests/4_delete_did.ts index 512178612d..0d67db36fc 100644 --- a/bindings/wasm/examples/src/tests/ex3_deactivate_did.ts +++ b/bindings/wasm/examples/src/tests/4_delete_did.ts @@ -1,4 +1,4 @@ -import {deleteIdentity} from "../ex4_delete_did"; +import { deleteIdentity } from "../0_basic/4_delete_did"; // Only verifies that no uncaught exceptions are thrown, including syntax errors etc. describe("Test node examples", function () { diff --git a/bindings/wasm/examples/src/tests/4_key_exchange.ts b/bindings/wasm/examples/src/tests/4_key_exchange.ts new file mode 100644 index 0000000000..b97c1e41f1 --- /dev/null +++ b/bindings/wasm/examples/src/tests/4_key_exchange.ts @@ -0,0 +1,8 @@ +import { keyExchange } from "../1_advanced/4_key_exchange"; + +// Only verifies that no uncaught exceptions are thrown, including syntax errors etc. +describe("Test node examples", function () { + it("Key exchange", async () => { + await keyExchange(); + }); +}) diff --git a/bindings/wasm/examples/src/util.ts b/bindings/wasm/examples/src/util.ts new file mode 100644 index 0000000000..d13615f08e --- /dev/null +++ b/bindings/wasm/examples/src/util.ts @@ -0,0 +1,127 @@ +import { + KeyPair, + KeyType, + MethodScope, + IotaDID, + IotaDocument, + IotaIdentityClient, + IotaVerificationMethod +} from '../../node'; +import type { Client, SecretManager } from "@iota/iota-client-wasm/node"; +import { AddressTypes, Bech32Helper, IAliasOutput } from '@iota/iota.js'; + +export const API_ENDPOINT = "https://api.testnet.shimmer.network/"; +export const FAUCET_ENDPOINT = "https://faucet.testnet.shimmer.network/api/enqueue"; + +/** Creates a DID Document and publishes it in a new Alias Output. + +Its functionality is equivalent to the "create DID" example +and exists for convenient calling from the other examples. */ +export async function createDid(client: Client, secretManager: SecretManager): Promise<{ + address: AddressTypes, + did: IotaDID +}> { + const didClient = new IotaIdentityClient(client); + const networkHrp: string = await didClient.getNetworkHrp(); + + const walletAddressBech32 = (await client.generateAddresses(secretManager, { + accountIndex: 0, + range: { + start: 0, + end: 1, + }, + }))[0]; + console.log("Wallet address Bech32:", walletAddressBech32); + + await ensureAddressHasFunds(client, walletAddressBech32); + + const address = Bech32Helper.addressFromBech32(walletAddressBech32, networkHrp); + + // Create a new DID document with a placeholder DID. + // The DID will be derived from the Alias Id of the Alias Output after publishing. + const document = new IotaDocument(networkHrp); + + // Insert a new Ed25519 verification method in the DID document. + let keypair = new KeyPair(KeyType.Ed25519); + let method = new IotaVerificationMethod(document.id(), keypair.type(), keypair.public(), "#key-1"); + document.insertMethod(method, MethodScope.VerificationMethod()); + + // Construct an Alias Output containing the DID document, with the wallet address + // set as both the state controller and governor. + const aliasOutput: IAliasOutput = await didClient.newDidOutput(address, document); + + // Publish the Alias Output and get the published DID document. + const published = await didClient.publishDidOutput(secretManager, aliasOutput); + + return { address, did: published.id() }; +} + +/** Request funds from the testnet faucet API, if needed, and wait for them to show in the wallet. */ +export async function ensureAddressHasFunds(client: Client, addressBech32: string) { + let balance = await getAddressBalance(client, addressBech32); + if (balance > 0) { + return; + } + + await requestFundsFromFaucet(addressBech32); + + for (let i = 0; i < 9; i++) { + // Wait for the funds to reflect. + await new Promise(f => setTimeout(f, 5000)); + + let balance = await getAddressBalance(client, addressBech32); + if (balance > 0) { + break; + } + } +} + +/** Returns the balance of the given Bech32-encoded address. */ +async function getAddressBalance(client: Client, addressBech32: string): Promise { + // TODO: use the `addresses/ed25519/` API to get the balance? + const outputIds = await client.basicOutputIds([ + { address: addressBech32 }, + { hasExpiration: false }, + { hasTimelock: false }, + { hasStorageDepositReturn: false } + ]); + const outputs = await client.getOutputs(outputIds); + + let totalAmount = 0; + for (const output of outputs) { + totalAmount += Number(output.output.amount); + } + + return totalAmount; +} + +/** Request tokens from the testnet faucet API. */ +async function requestFundsFromFaucet(addressBech32: string) { + const requestObj = JSON.stringify({ address: addressBech32 }); + let errorMessage, data; + try { + const response = await fetch(FAUCET_ENDPOINT, { + method: "POST", + headers: { + Accept: "application/json", + "Content-Type": "application/json", + }, + body: requestObj, + }); + if (response.status === 202) { + errorMessage = "OK"; + } else if (response.status === 429) { + errorMessage = "too many requests, please try again later."; + } else { + data = await response.json(); + // @ts-ignore + errorMessage = data.error.message; + } + } catch (error) { + errorMessage = error; + } + + if (errorMessage != "OK") { + throw new Error(`failed to get funds from faucet: ${errorMessage}`); + } +} diff --git a/bindings/wasm/package-lock.json b/bindings/wasm/package-lock.json index 2bf6fb25da..4ff18276d4 100644 --- a/bindings/wasm/package-lock.json +++ b/bindings/wasm/package-lock.json @@ -15,7 +15,9 @@ }, "devDependencies": { "@iota/crypto.js": "^1.9.0-stardust.6", + "@iota/util.js": "^1.9.0-stardust.5", "@types/mocha": "^9.1.0", + "big-integer": "^1.6.51", "concurrently": "^7.0.0", "copy-webpack-plugin": "^7.0.0", "cypress": "^10.6.0", @@ -30,9 +32,7 @@ "txm": "^8.0.1", "typescript": "^4.7.2", "wasm-opt": "^1.3.0", - "wasm-pack": "0.10.1", - "webpack": "^5.67.0", - "webpack-cli": "^4.9.2" + "wasm-pack": "0.10.1" }, "engines": { "node": ">=16" @@ -138,15 +138,6 @@ "ms": "^2.1.1" } }, - "node_modules/@discoveryjs/json-ext": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", - "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", - "dev": true, - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/@iota/crypto.js": { "version": "1.9.0-stardust.7", "resolved": "https://registry.npmjs.org/@iota/crypto.js/-/crypto.js-1.9.0-stardust.7.tgz", @@ -209,6 +200,7 @@ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", "dev": true, + "peer": true, "dependencies": { "@jridgewell/set-array": "^1.0.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -232,6 +224,7 @@ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", "dev": true, + "peer": true, "engines": { "node": ">=6.0.0" } @@ -241,6 +234,7 @@ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", "dev": true, + "peer": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" @@ -335,6 +329,7 @@ "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.6.tgz", "integrity": "sha512-/fqTbjxyFUaYNO7VcW5g+4npmqVACz1bB7RTHYuLj+PRjw9hrCwrUXVQFpChUS0JsyEFvMZ7U/PfmvWgxJhI9g==", "dev": true, + "peer": true, "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -345,6 +340,7 @@ "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", "dev": true, + "peer": true, "dependencies": { "@types/eslint": "*", "@types/estree": "*" @@ -354,7 +350,8 @@ "version": "0.0.51", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@types/json-schema": { "version": "7.0.11", @@ -465,6 +462,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", "dev": true, + "peer": true, "dependencies": { "@webassemblyjs/helper-numbers": "1.11.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.1" @@ -474,25 +472,29 @@ "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@webassemblyjs/helper-api-error": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@webassemblyjs/helper-buffer": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@webassemblyjs/helper-numbers": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", "dev": true, + "peer": true, "dependencies": { "@webassemblyjs/floating-point-hex-parser": "1.11.1", "@webassemblyjs/helper-api-error": "1.11.1", @@ -503,13 +505,15 @@ "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@webassemblyjs/helper-wasm-section": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", "dev": true, + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.11.1", "@webassemblyjs/helper-buffer": "1.11.1", @@ -522,6 +526,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", "dev": true, + "peer": true, "dependencies": { "@xtuc/ieee754": "^1.2.0" } @@ -531,6 +536,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", "dev": true, + "peer": true, "dependencies": { "@xtuc/long": "4.2.2" } @@ -539,13 +545,15 @@ "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@webassemblyjs/wasm-edit": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", "dev": true, + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.11.1", "@webassemblyjs/helper-buffer": "1.11.1", @@ -562,6 +570,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", "dev": true, + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.11.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.1", @@ -575,6 +584,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", "dev": true, + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.11.1", "@webassemblyjs/helper-buffer": "1.11.1", @@ -587,6 +597,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", "dev": true, + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.11.1", "@webassemblyjs/helper-api-error": "1.11.1", @@ -601,58 +612,25 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", "dev": true, + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.11.1", "@xtuc/long": "4.2.2" } }, - "node_modules/@webpack-cli/configtest": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", - "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", - "dev": true, - "peerDependencies": { - "webpack": "4.x.x || 5.x.x", - "webpack-cli": "4.x.x" - } - }, - "node_modules/@webpack-cli/info": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz", - "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", - "dev": true, - "dependencies": { - "envinfo": "^7.7.3" - }, - "peerDependencies": { - "webpack-cli": "4.x.x" - } - }, - "node_modules/@webpack-cli/serve": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz", - "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", - "dev": true, - "peerDependencies": { - "webpack-cli": "4.x.x" - }, - "peerDependenciesMeta": { - "webpack-dev-server": { - "optional": true - } - } - }, "node_modules/@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@xtuc/long": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true + "dev": true, + "peer": true }, "node_modules/acorn": { "version": "8.8.0", @@ -671,6 +649,7 @@ "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", "dev": true, + "peer": true, "peerDependencies": { "acorn": "^8" } @@ -1092,6 +1071,7 @@ "url": "https://tidelift.com/funding/github/npm/browserslist" } ], + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001400", "electron-to-chromium": "^1.4.251", @@ -1202,7 +1182,8 @@ "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/caniuse-lite" } - ] + ], + "peer": true }, "node_modules/caseless": { "version": "0.12.0", @@ -1310,6 +1291,7 @@ "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", "dev": true, + "peer": true, "engines": { "node": ">=6.0" } @@ -1383,20 +1365,6 @@ "wrap-ansi": "^7.0.0" } }, - "node_modules/clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "dev": true, - "dependencies": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/collect-all": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/collect-all/-/collect-all-1.0.4.tgz", @@ -2311,7 +2279,8 @@ "version": "1.4.253", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.253.tgz", "integrity": "sha512-1pezJ2E1UyBTGbA7fUlHdPSXQw1k+82VhTFLG5G0AUqLGvsZqFzleOblceqegZzxYX4kC7hGEEdzIQI9RZ1Cuw==", - "dev": true + "dev": true, + "peer": true }, "node_modules/emoji-regex": { "version": "8.0.0", @@ -2342,6 +2311,7 @@ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", "dev": true, + "peer": true, "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -2371,23 +2341,12 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/envinfo": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", - "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", - "dev": true, - "bin": { - "envinfo": "dist/cli.js" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/es-module-lexer": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", - "dev": true + "dev": true, + "peer": true }, "node_modules/escalade": { "version": "3.1.1", @@ -2412,6 +2371,7 @@ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, + "peer": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" @@ -2425,6 +2385,7 @@ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, + "peer": true, "dependencies": { "estraverse": "^5.2.0" }, @@ -2437,6 +2398,7 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, + "peer": true, "engines": { "node": ">=4.0" } @@ -2446,6 +2408,7 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, + "peer": true, "engines": { "node": ">=4.0" } @@ -2467,6 +2430,7 @@ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true, + "peer": true, "engines": { "node": ">=0.8.x" } @@ -2569,15 +2533,6 @@ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, - "node_modules/fastest-levenshtein": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", - "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", - "dev": true, - "engines": { - "node": ">= 4.9.1" - } - }, "node_modules/fastq": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", @@ -2788,12 +2743,6 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -2881,7 +2830,8 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true + "dev": true, + "peer": true }, "node_modules/global-dirs": { "version": "3.0.0", @@ -2954,18 +2904,6 @@ "uglify-js": "^3.1.4" } }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -3101,25 +3039,6 @@ "node": ">= 4" } }, - "node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/indent-string": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", @@ -3154,15 +3073,6 @@ "node": ">=10" } }, - "node_modules/interpret": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", - "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -3210,18 +3120,6 @@ "is-ci": "bin.js" } }, - "node_modules/is-core-module": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", - "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", - "dev": true, - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -3310,18 +3208,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -3358,15 +3244,6 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -3378,6 +3255,7 @@ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "dev": true, + "peer": true, "dependencies": { "@types/node": "*", "merge-stream": "^2.0.0", @@ -3514,7 +3392,8 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true + "dev": true, + "peer": true }, "node_modules/json-schema": { "version": "0.4.0", @@ -3573,15 +3452,6 @@ "verror": "1.10.0" } }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/klaw": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", @@ -3650,6 +3520,7 @@ "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", "dev": true, + "peer": true, "engines": { "node": ">=6.11.5" } @@ -4685,7 +4556,8 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", - "dev": true + "dev": true, + "peer": true }, "node_modules/normalize-path": { "version": "3.0.0", @@ -4843,12 +4715,6 @@ "node": ">=8" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -4874,7 +4740,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "dev": true, + "peer": true }, "node_modules/picomatch": { "version": "2.3.1", @@ -4897,70 +4764,6 @@ "node": ">=0.10.0" } }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/plimit-lit": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/plimit-lit/-/plimit-lit-1.4.0.tgz", @@ -5113,18 +4916,6 @@ "node": ">=8.10.0" } }, - "node_modules/rechoir": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", - "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", - "dev": true, - "dependencies": { - "resolve": "^1.9.0" - }, - "engines": { - "node": ">= 0.10" - } - }, "node_modules/reduce-extract": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/reduce-extract/-/reduce-extract-1.0.0.tgz", @@ -5271,52 +5062,14 @@ "lodash": "^4.17.14" } }, - "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", - "dev": true, - "dependencies": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "dev": true, "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" }, "engines": { "node": ">=8" @@ -5477,18 +5230,6 @@ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, - "node_modules/shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -5731,18 +5472,6 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/table-layout": { "version": "0.4.5", "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-0.4.5.tgz", @@ -5782,6 +5511,7 @@ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "dev": true, + "peer": true, "engines": { "node": ">=6" } @@ -5814,6 +5544,7 @@ "resolved": "https://registry.npmjs.org/terser/-/terser-5.15.0.tgz", "integrity": "sha512-L1BJiXVmheAQQy+as0oF3Pwtlo4s3Wi1X2zNZ2NxOB4wx9bdS9Vk67XQENLFdLYGCK/Z2di53mTj/hBafR+dTA==", "dev": true, + "peer": true, "dependencies": { "@jridgewell/source-map": "^0.3.2", "acorn": "^8.5.0", @@ -5832,6 +5563,7 @@ "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz", "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==", "dev": true, + "peer": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.14", "jest-worker": "^27.4.5", @@ -5866,6 +5598,7 @@ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", "dev": true, + "peer": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -5876,6 +5609,7 @@ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", "dev": true, + "peer": true, "dependencies": { "randombytes": "^2.1.0" } @@ -5884,7 +5618,8 @@ "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true + "dev": true, + "peer": true }, "node_modules/test-value": { "version": "3.0.0", @@ -6357,6 +6092,7 @@ "url": "https://tidelift.com/funding/github/npm/browserslist" } ], + "peer": true, "dependencies": { "escalade": "^3.1.1", "picocolors": "^1.0.0" @@ -6501,6 +6237,7 @@ "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", "dev": true, + "peer": true, "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -6519,6 +6256,7 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz", "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==", "dev": true, + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.3", "@types/estree": "^0.0.51", @@ -6561,80 +6299,12 @@ } } }, - "node_modules/webpack-cli": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", - "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", - "dev": true, - "dependencies": { - "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^1.2.0", - "@webpack-cli/info": "^1.5.0", - "@webpack-cli/serve": "^1.7.0", - "colorette": "^2.0.14", - "commander": "^7.0.0", - "cross-spawn": "^7.0.3", - "fastest-levenshtein": "^1.0.12", - "import-local": "^3.0.2", - "interpret": "^2.2.0", - "rechoir": "^0.7.0", - "webpack-merge": "^5.7.3" - }, - "bin": { - "webpack-cli": "bin/cli.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "4.x.x || 5.x.x" - }, - "peerDependenciesMeta": { - "@webpack-cli/generators": { - "optional": true - }, - "@webpack-cli/migrate": { - "optional": true - }, - "webpack-bundle-analyzer": { - "optional": true - }, - "webpack-dev-server": { - "optional": true - } - } - }, - "node_modules/webpack-cli/node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "dev": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/webpack-merge": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", - "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", - "dev": true, - "dependencies": { - "clone-deep": "^4.0.1", - "wildcard": "^2.0.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/webpack-sources": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", "dev": true, + "peer": true, "engines": { "node": ">=10.13.0" } @@ -6733,12 +6403,6 @@ "node": ">=4" } }, - "node_modules/wildcard": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", - "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", - "dev": true - }, "node_modules/wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", @@ -6992,12 +6656,6 @@ } } }, - "@discoveryjs/json-ext": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", - "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", - "dev": true - }, "@iota/crypto.js": { "version": "1.9.0-stardust.7", "resolved": "https://registry.npmjs.org/@iota/crypto.js/-/crypto.js-1.9.0-stardust.7.tgz", @@ -7048,6 +6706,7 @@ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", "dev": true, + "peer": true, "requires": { "@jridgewell/set-array": "^1.0.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -7064,13 +6723,15 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true + "dev": true, + "peer": true }, "@jridgewell/source-map": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", "dev": true, + "peer": true, "requires": { "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" @@ -7156,6 +6817,7 @@ "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.6.tgz", "integrity": "sha512-/fqTbjxyFUaYNO7VcW5g+4npmqVACz1bB7RTHYuLj+PRjw9hrCwrUXVQFpChUS0JsyEFvMZ7U/PfmvWgxJhI9g==", "dev": true, + "peer": true, "requires": { "@types/estree": "*", "@types/json-schema": "*" @@ -7166,6 +6828,7 @@ "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", "dev": true, + "peer": true, "requires": { "@types/eslint": "*", "@types/estree": "*" @@ -7175,7 +6838,8 @@ "version": "0.0.51", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", - "dev": true + "dev": true, + "peer": true }, "@types/json-schema": { "version": "7.0.11", @@ -7286,6 +6950,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", "dev": true, + "peer": true, "requires": { "@webassemblyjs/helper-numbers": "1.11.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.1" @@ -7295,25 +6960,29 @@ "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", - "dev": true + "dev": true, + "peer": true }, "@webassemblyjs/helper-api-error": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", - "dev": true + "dev": true, + "peer": true }, "@webassemblyjs/helper-buffer": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", - "dev": true + "dev": true, + "peer": true }, "@webassemblyjs/helper-numbers": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", "dev": true, + "peer": true, "requires": { "@webassemblyjs/floating-point-hex-parser": "1.11.1", "@webassemblyjs/helper-api-error": "1.11.1", @@ -7324,13 +6993,15 @@ "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", - "dev": true + "dev": true, + "peer": true }, "@webassemblyjs/helper-wasm-section": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", "dev": true, + "peer": true, "requires": { "@webassemblyjs/ast": "1.11.1", "@webassemblyjs/helper-buffer": "1.11.1", @@ -7343,6 +7014,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", "dev": true, + "peer": true, "requires": { "@xtuc/ieee754": "^1.2.0" } @@ -7352,6 +7024,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", "dev": true, + "peer": true, "requires": { "@xtuc/long": "4.2.2" } @@ -7360,13 +7033,15 @@ "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", - "dev": true + "dev": true, + "peer": true }, "@webassemblyjs/wasm-edit": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", "dev": true, + "peer": true, "requires": { "@webassemblyjs/ast": "1.11.1", "@webassemblyjs/helper-buffer": "1.11.1", @@ -7383,6 +7058,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", "dev": true, + "peer": true, "requires": { "@webassemblyjs/ast": "1.11.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.1", @@ -7396,6 +7072,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", "dev": true, + "peer": true, "requires": { "@webassemblyjs/ast": "1.11.1", "@webassemblyjs/helper-buffer": "1.11.1", @@ -7408,6 +7085,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", "dev": true, + "peer": true, "requires": { "@webassemblyjs/ast": "1.11.1", "@webassemblyjs/helper-api-error": "1.11.1", @@ -7422,45 +7100,25 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", "dev": true, + "peer": true, "requires": { "@webassemblyjs/ast": "1.11.1", "@xtuc/long": "4.2.2" } }, - "@webpack-cli/configtest": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", - "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", - "dev": true, - "requires": {} - }, - "@webpack-cli/info": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz", - "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", - "dev": true, - "requires": { - "envinfo": "^7.7.3" - } - }, - "@webpack-cli/serve": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz", - "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", - "dev": true, - "requires": {} - }, "@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true + "dev": true, + "peer": true }, "@xtuc/long": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true + "dev": true, + "peer": true }, "acorn": { "version": "8.8.0", @@ -7473,6 +7131,7 @@ "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", "dev": true, + "peer": true, "requires": {} }, "acorn-walk": { @@ -7776,6 +7435,7 @@ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", "dev": true, + "peer": true, "requires": { "caniuse-lite": "^1.0.30001400", "electron-to-chromium": "^1.4.251", @@ -7840,7 +7500,8 @@ "version": "1.0.30001402", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001402.tgz", "integrity": "sha512-Mx4MlhXO5NwuvXGgVb+hg65HZ+bhUYsz8QtDGDo2QmaJS2GBX47Xfi2koL86lc8K+l+htXeTEB/Aeqvezoo6Ew==", - "dev": true + "dev": true, + "peer": true }, "caseless": { "version": "0.12.0", @@ -7916,7 +7577,8 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", - "dev": true + "dev": true, + "peer": true }, "ci-info": { "version": "3.4.0", @@ -7970,17 +7632,6 @@ "wrap-ansi": "^7.0.0" } }, - "clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - } - }, "collect-all": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/collect-all/-/collect-all-1.0.4.tgz", @@ -8682,7 +8333,8 @@ "version": "1.4.253", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.253.tgz", "integrity": "sha512-1pezJ2E1UyBTGbA7fUlHdPSXQw1k+82VhTFLG5G0AUqLGvsZqFzleOblceqegZzxYX4kC7hGEEdzIQI9RZ1Cuw==", - "dev": true + "dev": true, + "peer": true }, "emoji-regex": { "version": "8.0.0", @@ -8710,6 +8362,7 @@ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", "dev": true, + "peer": true, "requires": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -8730,17 +8383,12 @@ "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", "dev": true }, - "envinfo": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", - "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", - "dev": true - }, "es-module-lexer": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", - "dev": true + "dev": true, + "peer": true }, "escalade": { "version": "3.1.1", @@ -8759,6 +8407,7 @@ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, + "peer": true, "requires": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" @@ -8769,6 +8418,7 @@ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, + "peer": true, "requires": { "estraverse": "^5.2.0" }, @@ -8777,7 +8427,8 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true + "dev": true, + "peer": true } } }, @@ -8785,7 +8436,8 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true + "dev": true, + "peer": true }, "eventemitter2": { "version": "6.4.7", @@ -8803,7 +8455,8 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true + "dev": true, + "peer": true }, "execa": { "version": "4.1.0", @@ -8880,12 +8533,6 @@ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, - "fastest-levenshtein": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", - "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", - "dev": true - }, "fastq": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", @@ -9034,12 +8681,6 @@ "dev": true, "optional": true }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -9106,7 +8747,8 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true + "dev": true, + "peer": true }, "global-dirs": { "version": "3.0.0", @@ -9156,15 +8798,6 @@ "wordwrap": "^1.0.0" } }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -9256,16 +8889,6 @@ "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "dev": true }, - "import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "requires": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - } - }, "indent-string": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", @@ -9294,12 +8917,6 @@ "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", "dev": true }, - "interpret": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", - "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", - "dev": true - }, "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -9324,15 +8941,6 @@ "ci-info": "^3.2.0" } }, - "is-core-module": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", - "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -9388,15 +8996,6 @@ "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", "dev": true }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, "is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -9421,12 +9020,6 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "dev": true - }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -9438,6 +9031,7 @@ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "dev": true, + "peer": true, "requires": { "@types/node": "*", "merge-stream": "^2.0.0", @@ -9549,7 +9143,8 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true + "dev": true, + "peer": true }, "json-schema": { "version": "0.4.0", @@ -9597,12 +9192,6 @@ "verror": "1.10.0" } }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - }, "klaw": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", @@ -9653,7 +9242,8 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", - "dev": true + "dev": true, + "peer": true }, "loader-utils": { "version": "2.0.2", @@ -10325,7 +9915,8 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", - "dev": true + "dev": true, + "peer": true }, "normalize-path": { "version": "3.0.0", @@ -10435,12 +10026,6 @@ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, "path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -10463,7 +10048,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "dev": true, + "peer": true }, "picomatch": { "version": "2.3.1", @@ -10477,54 +10063,6 @@ "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - }, - "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - } - } - }, "plimit-lit": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/plimit-lit/-/plimit-lit-1.4.0.tgz", @@ -10644,15 +10182,6 @@ "picomatch": "^2.2.1" } }, - "rechoir": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", - "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", - "dev": true, - "requires": { - "resolve": "^1.9.0" - } - }, "reduce-extract": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/reduce-extract/-/reduce-extract-1.0.0.tgz", @@ -10772,32 +10301,6 @@ "lodash": "^4.17.14" } }, - "resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", - "dev": true, - "requires": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "requires": { - "resolve-from": "^5.0.0" - } - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - }, "restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -10909,15 +10412,6 @@ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, - "shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", - "dev": true, - "requires": { - "kind-of": "^6.0.2" - } - }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -11099,12 +10593,6 @@ "has-flag": "^4.0.0" } }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true - }, "table-layout": { "version": "0.4.5", "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-0.4.5.tgz", @@ -11139,7 +10627,8 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true + "dev": true, + "peer": true }, "tar": { "version": "6.1.11", @@ -11166,6 +10655,7 @@ "resolved": "https://registry.npmjs.org/terser/-/terser-5.15.0.tgz", "integrity": "sha512-L1BJiXVmheAQQy+as0oF3Pwtlo4s3Wi1X2zNZ2NxOB4wx9bdS9Vk67XQENLFdLYGCK/Z2di53mTj/hBafR+dTA==", "dev": true, + "peer": true, "requires": { "@jridgewell/source-map": "^0.3.2", "acorn": "^8.5.0", @@ -11177,7 +10667,8 @@ "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true + "dev": true, + "peer": true } } }, @@ -11186,6 +10677,7 @@ "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz", "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==", "dev": true, + "peer": true, "requires": { "@jridgewell/trace-mapping": "^0.3.14", "jest-worker": "^27.4.5", @@ -11199,6 +10691,7 @@ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", "dev": true, + "peer": true, "requires": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -11209,6 +10702,7 @@ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", "dev": true, + "peer": true, "requires": { "randombytes": "^2.1.0" } @@ -11554,6 +11048,7 @@ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz", "integrity": "sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg==", "dev": true, + "peer": true, "requires": { "escalade": "^3.1.1", "picocolors": "^1.0.0" @@ -11661,6 +11156,7 @@ "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", "dev": true, + "peer": true, "requires": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -11676,6 +11172,7 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz", "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==", "dev": true, + "peer": true, "requires": { "@types/eslint-scope": "^3.7.3", "@types/estree": "^0.0.51", @@ -11703,49 +11200,12 @@ "webpack-sources": "^3.2.3" } }, - "webpack-cli": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", - "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", - "dev": true, - "requires": { - "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^1.2.0", - "@webpack-cli/info": "^1.5.0", - "@webpack-cli/serve": "^1.7.0", - "colorette": "^2.0.14", - "commander": "^7.0.0", - "cross-spawn": "^7.0.3", - "fastest-levenshtein": "^1.0.12", - "import-local": "^3.0.2", - "interpret": "^2.2.0", - "rechoir": "^0.7.0", - "webpack-merge": "^5.7.3" - }, - "dependencies": { - "commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "dev": true - } - } - }, - "webpack-merge": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", - "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", - "dev": true, - "requires": { - "clone-deep": "^4.0.1", - "wildcard": "^2.0.0" - } - }, "webpack-sources": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", - "dev": true + "dev": true, + "peer": true }, "whatwg-encoding": { "version": "2.0.0", @@ -11822,12 +11282,6 @@ } } }, - "wildcard": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", - "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", - "dev": true - }, "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", diff --git a/bindings/wasm/package.json b/bindings/wasm/package.json index 76988fc113..ac4598dd5e 100644 --- a/bindings/wasm/package.json +++ b/bindings/wasm/package.json @@ -19,7 +19,7 @@ "build": "npm run build:web && npm run build:nodejs && npm run build:docs", "example:browser": "http-server ./examples/dist/ -c-1 -o ", "example:account": "ts-node ./examples-account/src/node.ts", - "example:node": "ts-node ./examples/src/main.ts", + "example:node": "ts-node --project tsconfig.node.json ./examples/src/main.ts", "test": "npm run test:unit && npm run test:unit:node && npm run test:examples", "test:examples": "npm run test:node && npm run test:readme", "test:account:node": "ts-mocha -p tsconfig.node.json ./examples-account/src/tests/*.ts --parallel --jobs 4 --retries 3 --timeout 180000 --exit", @@ -54,7 +54,9 @@ "node/*" ], "devDependencies": { + "big-integer": "^1.6.51", "@iota/crypto.js": "^1.9.0-stardust.6", + "@iota/util.js": "^1.9.0-stardust.5", "@types/mocha": "^9.1.0", "concurrently": "^7.0.0", "copy-webpack-plugin": "^7.0.0", @@ -70,9 +72,7 @@ "txm": "^8.0.1", "typescript": "^4.7.2", "wasm-opt": "^1.3.0", - "wasm-pack": "0.10.1", - "webpack": "^5.67.0", - "webpack-cli": "^4.9.2" + "wasm-pack": "0.10.1" }, "dependencies": { "@iota/types": "^1.0.0-beta.11", diff --git a/bindings/wasm/src/iota/iota_did.rs b/bindings/wasm/src/iota/iota_did.rs index 7b916aa0ab..ebc7111ce3 100644 --- a/bindings/wasm/src/iota/iota_did.rs +++ b/bindings/wasm/src/iota/iota_did.rs @@ -1,6 +1,7 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use bee_block::output::AliasId; use identity_iota::did::DIDError; use identity_iota::did::DID; use identity_iota::iota::IotaDID; @@ -136,6 +137,12 @@ impl WasmIotaDID { WasmIotaDIDUrl::from(self.0.to_url()) } + /// Returns the hex-encoded AliasId with a '0x' prefix, from the DID tag. + #[wasm_bindgen(js_name = toAliasId)] + pub fn to_alias_id(&self) -> String { + AliasId::from(&self.0).to_string() + } + /// Converts the `DID` into a `DIDUrl`, consuming it. #[wasm_bindgen(js_name = intoUrl)] pub fn into_url(self) -> WasmIotaDIDUrl { diff --git a/documentation/docs/concepts/decentralized_identifiers/create.mdx b/documentation/docs/concepts/decentralized_identifiers/create.mdx index 3a76512d31..5ddc0ba0c1 100644 --- a/documentation/docs/concepts/decentralized_identifiers/create.mdx +++ b/documentation/docs/concepts/decentralized_identifiers/create.mdx @@ -11,8 +11,8 @@ keywords: - Publish --- import CodeSnippet from '../../../src/components/CodeSnippetComponent' -import createDidRustExample from '!!raw-loader!../../../../examples/0_basic/0_create_did.rs'; - +import create_did_rust from "!!raw-loader!../../../../examples/0_basic/0_create_did.rs"; +import create_did_node from "!!raw-loader!../../../../bindings/wasm/examples/src/0_basic/0_create_did.ts"; When someone or something wants to benefit from Self-Sovereign Identity, they must first create a Decentralized Identity. This identity consists of many parts that have different functions. This page will cover both the basics and the details about identity creation and publishing. @@ -26,9 +26,9 @@ Select your programming language of choice and press the green play button to ex ::: diff --git a/documentation/docs/concepts/decentralized_identifiers/delete.mdx b/documentation/docs/concepts/decentralized_identifiers/delete.mdx index 43d23752c1..fa0f1bd777 100644 --- a/documentation/docs/concepts/decentralized_identifiers/delete.mdx +++ b/documentation/docs/concepts/decentralized_identifiers/delete.mdx @@ -10,9 +10,9 @@ keywords: --- import deactivate_did_rs from '!!raw-loader!../../../../examples/0_basic/3_deactivate_did.rs'; -import deactivate_did_js from '!!raw-loader!../../../../bindings/wasm/examples/src/ex3_deactivate_did.ts'; +import deactivate_did_js from '!!raw-loader!../../../../bindings/wasm/examples/src/0_basic/3_deactivate_did.ts'; import delete_did_rs from '!!raw-loader!../../../../examples/0_basic/4_delete_did.rs'; -import delete_did_js from '!!raw-loader!../../../../bindings/wasm/examples/src/ex4_delete_did.ts'; +import delete_did_js from '!!raw-loader!../../../../bindings/wasm/examples/src/0_basic/4_delete_did.ts'; import CodeSnippet from '../../../src/components/CodeSnippetComponent' @@ -42,7 +42,7 @@ The following example demonstrates deactivating and reactivating an IOTA DID Doc @@ -69,6 +69,6 @@ The following example demonstrates a governor destroying an IOTA Identity and se diff --git a/documentation/docs/concepts/decentralized_identifiers/resolve.mdx b/documentation/docs/concepts/decentralized_identifiers/resolve.mdx index 4926acb7d0..b357e90be8 100644 --- a/documentation/docs/concepts/decentralized_identifiers/resolve.mdx +++ b/documentation/docs/concepts/decentralized_identifiers/resolve.mdx @@ -7,7 +7,7 @@ keywords: - Resolve --- import resolve_did_rs from '!!raw-loader!../../../../examples/0_basic/2_resolve_did.rs'; -import resolve_did_js from '!!raw-loader!../../../../bindings/wasm/examples/src/ex2_resolve_did.ts'; +import resolve_did_js from '!!raw-loader!../../../../bindings/wasm/examples/src/0_basic/2_resolve_did.ts'; import CodeSnippet from '../../../src/components/CodeSnippetComponent'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; @@ -154,6 +154,6 @@ The following example creates a new IOTA DID, publishes it and then resolves its diff --git a/documentation/docs/concepts/decentralized_identifiers/update.mdx b/documentation/docs/concepts/decentralized_identifiers/update.mdx index f75db1ea36..295c35a84e 100644 --- a/documentation/docs/concepts/decentralized_identifiers/update.mdx +++ b/documentation/docs/concepts/decentralized_identifiers/update.mdx @@ -12,7 +12,7 @@ keywords: --- import update_did_rust from "!!raw-loader!../../../../examples/0_basic/1_update_did.rs"; -import update_did_node from "!!raw-loader!../../../../bindings/wasm/examples/src/ex1_update_did.ts"; +import update_did_node from "!!raw-loader!../../../../bindings/wasm/examples/src/0_basic/1_update_did.ts"; import CodeSnippet from "../../../src/components/CodeSnippetComponent"; import Tabs from "@theme/Tabs"; import TabItem from "@theme/TabItem"; @@ -74,7 +74,7 @@ The following example demonstrates managing verification methods and services in diff --git a/examples/1_advanced/2_nft_owns_did.rs b/examples/1_advanced/2_nft_owns_did.rs index d474dab761..ae43a20956 100644 --- a/examples/1_advanced/2_nft_owns_did.rs +++ b/examples/1_advanced/2_nft_owns_did.rs @@ -78,20 +78,22 @@ async fn main() -> anyhow::Result<()> { let network: NetworkName = client.network_name().await?; - // Construct a DID document for the subsidiary. - let document: IotaDocument = create_did_document(&network)?; + // Construct a DID document for the car. + let car_document: IotaDocument = create_did_document(&network)?; // Create a new DID for the car that is owned by the car NFT. let car_did_output: AliasOutput = client - .new_did_output(Address::Nft(car_nft_id.into()), document, Some(rent_structure)) + .new_did_output(Address::Nft(car_nft_id.into()), car_document, Some(rent_structure)) .await?; + // Publish the car DID. let car_document: IotaDocument = client.publish_did_output(&secret_manager, car_did_output).await?; // ============================================ // Determine the car's NFT given the car's DID. // ============================================ + // Resolve the Alias Output of the DID. let output: AliasOutput = client.resolve_did_output(car_document.id()).await?; // Extract the NFT address from the state controller unlock condition. diff --git a/examples/1_advanced/3_did_issues_tokens.rs b/examples/1_advanced/3_did_issues_tokens.rs index ed931d648b..57553277f7 100644 --- a/examples/1_advanced/3_did_issues_tokens.rs +++ b/examples/1_advanced/3_did_issues_tokens.rs @@ -4,7 +4,6 @@ use std::ops::Deref; use examples::create_did; -use examples::get_address; use examples::random_stronghold_path; use examples::API_ENDPOINT; use identity_iota::core::Duration; @@ -63,7 +62,6 @@ async fn main() -> anyhow::Result<()> { ); // Create a new DID for the authority. - let (_, authority_did): (Address, IotaDID) = create_did(&client, &mut secret_manager).await?; let rent_structure: RentStructure = client.get_rent_structure().await?; @@ -147,7 +145,7 @@ async fn main() -> anyhow::Result<()> { // ========================================================= // Create a new address that represents the company. - let company_address = get_address(&client, &mut secret_manager).await?; + let company_address: Address = client.get_addresses(&secret_manager).with_range(1..2).get_raw().await?[0]; // Create the timestamp at which the basic output will expire. let tomorrow: u32 = Timestamp::now_utc() @@ -161,6 +159,7 @@ async fn main() -> anyhow::Result<()> { let basic_output: BasicOutput = BasicOutputBuilder::new_with_minimum_storage_deposit(rent_structure)? .add_unlock_condition(UnlockCondition::Address(AddressUnlockCondition::new(company_address))) .add_native_token(NativeToken::new(carbon_credits_foundry.token_id(), U256::from(1000))?) + // Allow the company to claim the credits within 24 hours by using an expiration unlock condition. .add_unlock_condition(UnlockCondition::Expiration(ExpirationUnlockCondition::new( Address::Alias(AliasAddress::new(*authority_alias_id)), tomorrow, From 41dd020673def9af07e9787fd5eeaae55c1ac661 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Mon, 19 Sep 2022 17:51:45 +0200 Subject: [PATCH 77/89] Bump versions of legacy crates (#1036) * Add bump versions to release action * Bump versions of legacy crates manually --- .../actions/release/bump-versions/action.yml | 26 +++++++++++++++++++ bindings/stronghold-nodejs/Cargo.lock | 4 +-- bindings/stronghold-nodejs/Cargo.toml | 4 +-- identity_account/Cargo.toml | 14 +++++----- identity_account_storage/Cargo.toml | 4 +-- identity_iota_client_legacy/Cargo.toml | 10 +++---- identity_iota_core_legacy/Cargo.toml | 6 ++--- 7 files changed, 47 insertions(+), 21 deletions(-) diff --git a/.github/actions/release/bump-versions/action.yml b/.github/actions/release/bump-versions/action.yml index 1c6b39828c..de041fc065 100644 --- a/.github/actions/release/bump-versions/action.yml +++ b/.github/actions/release/bump-versions/action.yml @@ -53,6 +53,32 @@ runs: run: | cargo set-version ${{ inputs.version }} + # cargo workspaces ignores the legacy crates because they are excluded from the workspace. + # Bump versions for consistency. + - name: Bump Rust legacy crates' version + shell: bash + if: ${{inputs.release-target == 'rust'}} + run: | + cargo set-version --manifest-path identity_account_storage/Cargo.toml ${{ inputs.version }} + cargo set-version --manifest-path identity_iota_client_legacy/Cargo.toml ${{ inputs.version }} + cargo set-version --manifest-path identity_iota_core_legacy/Cargo.toml ${{ inputs.version }} + cargo set-version --manifest-path identity_account/Cargo.toml ${{ inputs.version }} + cargo add --manifest-path identity_account_storage/Cargo.toml --path identity_core + cargo add --manifest-path identity_account_storage/Cargo.toml --path identity_did + cargo add --manifest-path identity_account_storage/Cargo.toml --path identity_iota_core_legacy + cargo add --manifest-path identity_account/Cargo.toml --path identity_account_storage + cargo add --manifest-path identity_account/Cargo.toml --path identity_core + cargo add --manifest-path identity_account/Cargo.toml --path identity_credential + cargo add --manifest-path identity_account/Cargo.toml --path identity_did + cargo add --manifest-path identity_account/Cargo.toml --path identity_iota_client_legacy + cargo add --manifest-path identity_account/Cargo.toml --path identity_iota_core_legacy + cargo add --manifest-path identity_iota_core_legacy/Cargo.toml --path identity_core + cargo add --manifest-path identity_iota_core_legacy/Cargo.toml --path identity_did + cargo add --manifest-path identity_iota_client_legacy/Cargo.toml --path identity_core + cargo add --manifest-path identity_iota_client_legacy/Cargo.toml --path identity_credential + cargo add --manifest-path identity_iota_client_legacy/Cargo.toml --path identity_did + cargo add --manifest-path identity_iota_client_legacy/Cargo.toml --path identity_iota_core_legacy + # cargo workspaces ignores identity_agent because it is excluded from the workspace. # Bump versions for consistency. - name: Bump Rust agent version diff --git a/bindings/stronghold-nodejs/Cargo.lock b/bindings/stronghold-nodejs/Cargo.lock index 22ffbb7e14..fd154632ec 100644 --- a/bindings/stronghold-nodejs/Cargo.lock +++ b/bindings/stronghold-nodejs/Cargo.lock @@ -740,7 +740,7 @@ dependencies = [ [[package]] name = "identity_account_storage" -version = "0.6.0" +version = "0.7.0-alpha.1" dependencies = [ "async-trait", "futures", @@ -793,7 +793,7 @@ dependencies = [ [[package]] name = "identity_iota_core_legacy" -version = "0.6.0" +version = "0.7.0-alpha.1" dependencies = [ "bee-message", "identity_core", diff --git a/bindings/stronghold-nodejs/Cargo.toml b/bindings/stronghold-nodejs/Cargo.toml index b29684cbc8..beb97110f4 100644 --- a/bindings/stronghold-nodejs/Cargo.toml +++ b/bindings/stronghold-nodejs/Cargo.toml @@ -7,10 +7,10 @@ publish = false crate-type = ["cdylib"] [dependencies] -identity_account_storage = { version = "0.6.0", path = "../../identity_account_storage", default-features = false, features = ["stronghold", "send-sync-storage", "encryption"] } +identity_account_storage = { version = "0.7.0-alpha.1", path = "../../identity_account_storage", default-features = false, features = ["stronghold", "send-sync-storage", "encryption"] } identity_core = { version = "0.7.0-alpha.1", path = "../../identity_core", default-features = false } identity_did = { version = "0.7.0-alpha.1", path = "../../identity_did", default-features = false } -identity_iota_core_legacy = { version = "0.6.0", path = "../../identity_iota_core_legacy", default-features = false } +identity_iota_core_legacy = { version = "0.7.0-alpha.1", path = "../../identity_iota_core_legacy", default-features = false } napi = { version = "2.4.3", default-features = false, features = ["napi4", "tokio_rt", "serde-json"] } napi-derive = { version = "2.4.1", default-features = false, features = ["compat-mode", "full"] } serde = { version = "1.0", default-features = false, features = ["derive"] } diff --git a/identity_account/Cargo.toml b/identity_account/Cargo.toml index 3f53b4d183..47363371fa 100644 --- a/identity_account/Cargo.toml +++ b/identity_account/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity_account" -version = "0.6.0" +version = "0.7.0-alpha.1" authors = ["IOTA Stiftung"] edition = "2021" homepage = "https://www.iota.org" @@ -14,12 +14,12 @@ description = "High-level interface for managing IOTA DID Documents." [workspace] [dependencies] -identity_account_storage = { version = "=0.6.0", path = "../identity_account_storage", default-features = false } -identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } -identity_credential = { version = "=0.6.0", path = "../identity_credential", default-features = false } -identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } -identity_iota_client_legacy = { version = "=0.6.0", path = "../identity_iota_client_legacy", default-features = false } -identity_iota_core_legacy = { version = "=0.6.0", path = "../identity_iota_core_legacy", default-features = false } +identity_account_storage = { version = "0.7.0-alpha.1", path = "../identity_account_storage", default-features = false } +identity_core = { version = "0.7.0-alpha.1", path = "../identity_core", default-features = false } +identity_credential = { version = "0.7.0-alpha.1", path = "../identity_credential", default-features = false } +identity_did = { version = "0.7.0-alpha.1", path = "../identity_did", default-features = false } +identity_iota_client_legacy = { version = "0.7.0-alpha.1", path = "../identity_iota_client_legacy", default-features = false } +identity_iota_core_legacy = { version = "0.7.0-alpha.1", path = "../identity_iota_core_legacy", default-features = false } log = { version = "0.4", default-features = false } paste = { version = "1.0" } rand = { version = "0.8", default-features = false, features = ["std", "std_rng"] } diff --git a/identity_account_storage/Cargo.toml b/identity_account_storage/Cargo.toml index 3564a6874b..da7e08fb5e 100644 --- a/identity_account_storage/Cargo.toml +++ b/identity_account_storage/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity_account_storage" -version = "0.6.0" +version = "0.7.0-alpha.1" authors = ["IOTA Stiftung"] edition = "2021" homepage = "https://www.iota.org" @@ -21,7 +21,7 @@ futures = { version = "0.3", optional = true } hashbrown = { version = "0.11", features = ["serde"] } identity_core = { version = "0.7.0-alpha.1", path = "../identity_core", default-features = false } identity_did = { version = "0.7.0-alpha.1", path = "../identity_did", default-features = false } -identity_iota_core_legacy = { version = "=0.6.0", path = "../identity_iota_core_legacy", default-features = false } +identity_iota_core_legacy = { version = "0.7.0-alpha.1", path = "../identity_iota_core_legacy", default-features = false } iota-crypto = { version = "0.12.1", default-features = false, features = ["hmac", "pbkdf", "sha", "std", "aes-gcm", "aes-kw"] } iota_stronghold = { version = "0.6.4", default-features = false, features = ["std"], optional = true } once_cell = { version = "1.7", default-features = false, features = ["std"], optional = true } diff --git a/identity_iota_client_legacy/Cargo.toml b/identity_iota_client_legacy/Cargo.toml index 224a394c2c..4e08bed50f 100644 --- a/identity_iota_client_legacy/Cargo.toml +++ b/identity_iota_client_legacy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity_iota_client_legacy" -version = "0.6.0" +version = "0.7.0-alpha.1" authors = ["IOTA Stiftung"] edition = "2021" homepage = "https://www.iota.org" @@ -19,10 +19,10 @@ bee-rest-api = { version = "0.1.7", default-features = false } brotli = { version = "3.3", default-features = false, features = ["std"] } form_urlencoded = { version = "1.0" } futures = { version = "0.3" } -identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false } -identity_credential = { version = "=0.6.0", path = "../identity_credential", default-features = false, features = ["validator"] } -identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false } -identity_iota_core_legacy = { version = "=0.6.0", path = "../identity_iota_core_legacy", default-features = false } +identity_core = { version = "0.7.0-alpha.1", path = "../identity_core", default-features = false } +identity_credential = { version = "0.7.0-alpha.1", path = "../identity_credential", default-features = false, features = ["validator"] } +identity_did = { version = "0.7.0-alpha.1", path = "../identity_did", default-features = false } +identity_iota_core_legacy = { version = "0.7.0-alpha.1", path = "../identity_iota_core_legacy", default-features = false } itertools = { version = "0.10" } lazy_static = { version = "1.4", default-features = false } log = { version = "0.4", default-features = false } diff --git a/identity_iota_core_legacy/Cargo.toml b/identity_iota_core_legacy/Cargo.toml index a6e7123e52..ac4307d339 100644 --- a/identity_iota_core_legacy/Cargo.toml +++ b/identity_iota_core_legacy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity_iota_core_legacy" -version = "0.6.0" +version = "0.7.0-alpha.1" authors = ["IOTA Stiftung"] edition = "2021" homepage = "https://www.iota.org" @@ -15,8 +15,8 @@ description = "Core data structures for the IOTA DID Method." [dependencies] bee-message = { version = "0.1.6", default-features = false, features = ["serde"] } -identity_core = { version = "=0.7.0-alpha.1", path = "../identity_core", default-features = false } -identity_did = { version = "=0.7.0-alpha.1", path = "../identity_did", default-features = false } +identity_core = { version = "0.7.0-alpha.1", path = "../identity_core", default-features = false } +identity_did = { version = "0.7.0-alpha.1", path = "../identity_did", default-features = false } lazy_static = { version = "1.4", default-features = false } serde = { version = "1.0", default-features = false, features = ["std", "derive"] } strum = { version = "0.24.0", default-features = false, features = ["std", "derive"] } From 190b7b657a3e00155a53e96bf7b80a87c201f589 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Tue, 20 Sep 2022 11:14:47 +0200 Subject: [PATCH 78/89] Format TypeScript/JavaScript files (#1038) * Add typescript `dprint` plugin * `dprint fmt` * Use indentWidth = 4 * Set `"arrayExpression.preferHanging": true` --- .../examples/src/stronghold.ts | 6 +- .../examples/src/tests/config.ts | 8 +- .../examples/src/tests/create_did.ts | 8 +- .../examples/src/tests/encryption.ts | 6 +- .../examples/src/tests/lazy.ts | 8 +- .../examples/src/tests/manipulate_did.ts | 8 +- .../examples/src/tests/multiple_identities.ts | 8 +- .../examples/src/tests/signing.ts | 6 +- .../examples/src/tests/storage_test_suite.ts | 6 +- .../examples/src/tests/unchecked.ts | 8 +- bindings/stronghold-nodejs/js/index.ts | 4 +- bindings/stronghold-nodejs/js/stronghold.ts | 92 ++++- bindings/stronghold-nodejs/rollup.config.js | 27 +- .../stronghold-nodejs/tests/txm_readme.js | 8 +- bindings/wasm/build/docs.js | 18 +- bindings/wasm/build/lints.js | 18 +- bindings/wasm/build/node.js | 23 +- bindings/wasm/build/utils/generatePackage.js | 10 +- bindings/wasm/build/web.js | 36 +- bindings/wasm/cypress.config.ts | 22 +- bindings/wasm/examples-account/src/config.ts | 9 +- .../wasm/examples-account/src/create_did.ts | 5 +- .../wasm/examples-account/src/create_vc.ts | 19 +- .../wasm/examples-account/src/create_vp.ts | 30 +- .../examples-account/src/custom_storage.ts | 55 ++- .../wasm/examples-account/src/encryption.ts | 43 ++- bindings/wasm/examples-account/src/lazy.ts | 15 +- .../examples-account/src/manipulate_did.ts | 21 +- .../src/multiple_identities.ts | 11 +- bindings/wasm/examples-account/src/node.ts | 10 +- .../wasm/examples-account/src/revoke_vc.ts | 24 +- bindings/wasm/examples-account/src/signing.ts | 15 +- .../wasm/examples-account/src/tests/config.ts | 6 +- .../examples-account/src/tests/create_did.ts | 6 +- .../examples-account/src/tests/create_vc.ts | 7 +- .../examples-account/src/tests/create_vp.ts | 5 +- .../examples-account/src/tests/encryption.ts | 6 +- .../wasm/examples-account/src/tests/lazy.ts | 6 +- .../src/tests/manipulate_did.ts | 6 +- .../src/tests/multiple_identities.ts | 6 +- .../examples-account/src/tests/revoke_vc.ts | 5 +- .../examples-account/src/tests/signing.ts | 6 +- .../examples-account/src/tests/unchecked.ts | 6 +- .../wasm/examples-account/src/unchecked.ts | 6 +- .../wasm/examples/src/0_basic/0_create_did.ts | 33 +- .../wasm/examples/src/0_basic/1_update_did.ts | 24 +- .../examples/src/0_basic/2_resolve_did.ts | 12 +- .../examples/src/0_basic/3_deactivate_did.ts | 12 +- .../wasm/examples/src/0_basic/4_delete_did.ts | 4 +- .../src/1_advanced/0_did_controls_did.ts | 263 +++++++------ .../src/1_advanced/1_did_issues_nft.ts | 245 ++++++------ .../examples/src/1_advanced/2_nft_owns_did.ts | 267 +++++++------ .../src/1_advanced/3_did_issues_tokens.ts | 357 +++++++++--------- .../examples/src/1_advanced/4_key_exchange.ts | 246 ++++++------ .../wasm/examples/src/tests/0_create_did.ts | 6 +- .../examples/src/tests/0_did_controls_did.ts | 4 +- .../examples/src/tests/1_did_issues_nft.ts | 4 +- .../wasm/examples/src/tests/1_update_did.ts | 4 +- .../wasm/examples/src/tests/2_nft_owns_did.ts | 4 +- .../wasm/examples/src/tests/2_resolve_did.ts | 4 +- .../examples/src/tests/3_deactivate_did.ts | 4 +- .../examples/src/tests/3_did_issues_tokens.ts | 4 +- .../wasm/examples/src/tests/4_delete_did.ts | 4 +- .../wasm/examples/src/tests/4_key_exchange.ts | 4 +- bindings/wasm/examples/src/util.ts | 178 ++++----- bindings/wasm/lib/index.ts | 4 +- bindings/wasm/lib/iota_identity_client.ts | 21 +- bindings/wasm/tests/core.ts | 92 +++-- bindings/wasm/tests/credentials.ts | 164 +++++--- bindings/wasm/tests/iota.ts | 66 ++-- bindings/wasm/tests/legacy/account.ts | 36 +- bindings/wasm/tests/resolver.ts | 308 +++++++-------- bindings/wasm/tests/txm_readme.js | 8 +- dprint.json | 22 +- 74 files changed, 1682 insertions(+), 1380 deletions(-) diff --git a/bindings/stronghold-nodejs/examples/src/stronghold.ts b/bindings/stronghold-nodejs/examples/src/stronghold.ts index f390106eb2..1843f17845 100644 --- a/bindings/stronghold-nodejs/examples/src/stronghold.ts +++ b/bindings/stronghold-nodejs/examples/src/stronghold.ts @@ -1,10 +1,10 @@ -import { Stronghold } from '../../dist' import * as os from "os"; import * as path from "path"; +import { Stronghold } from "../../dist"; export async function stronghold(): Promise { - const random = Math.round(Math.random() * 100_000_000) + const random = Math.round(Math.random() * 100_000_000); const strongholdPath = path.join(os.tmpdir(), "test_strongholds", `${random}.stronghodl`); const password = "my-password"; return await Stronghold.build(strongholdPath, password, true); -} \ No newline at end of file +} diff --git a/bindings/stronghold-nodejs/examples/src/tests/config.ts b/bindings/stronghold-nodejs/examples/src/tests/config.ts index daacb66859..231aa0193b 100644 --- a/bindings/stronghold-nodejs/examples/src/tests/config.ts +++ b/bindings/stronghold-nodejs/examples/src/tests/config.ts @@ -1,9 +1,9 @@ -import {config} from "../../../../wasm/examples-account/src/config"; -import { stronghold } from '../stronghold'; +import { config } from "../../../../wasm/examples-account/src/config"; +import { stronghold } from "../stronghold"; // Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test Stronghold Node.js examples", function () { +describe("Test Stronghold Node.js examples", function() { it("Config", async () => { await config(await stronghold()); }); -}) +}); diff --git a/bindings/stronghold-nodejs/examples/src/tests/create_did.ts b/bindings/stronghold-nodejs/examples/src/tests/create_did.ts index 397795dca8..a3498b446c 100644 --- a/bindings/stronghold-nodejs/examples/src/tests/create_did.ts +++ b/bindings/stronghold-nodejs/examples/src/tests/create_did.ts @@ -1,9 +1,9 @@ -import {createIdentity} from "../../../../wasm/examples-account/src/create_did"; -import { stronghold } from '../stronghold'; +import { createIdentity } from "../../../../wasm/examples-account/src/create_did"; +import { stronghold } from "../stronghold"; // Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test Stronghold Node.js examples", function () { +describe("Test Stronghold Node.js examples", function() { it("Create Identity", async () => { await createIdentity(await stronghold()); }); -}) +}); diff --git a/bindings/stronghold-nodejs/examples/src/tests/encryption.ts b/bindings/stronghold-nodejs/examples/src/tests/encryption.ts index d26b37a30b..5c22e190f3 100644 --- a/bindings/stronghold-nodejs/examples/src/tests/encryption.ts +++ b/bindings/stronghold-nodejs/examples/src/tests/encryption.ts @@ -1,9 +1,9 @@ import { encryption } from "../../../../wasm/examples-account/src/encryption"; -import { stronghold } from '../stronghold'; +import { stronghold } from "../stronghold"; // Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test Stronghold Node.js examples", function () { +describe("Test Stronghold Node.js examples", function() { it("encryption", async () => { await encryption(await stronghold()); }); -}) +}); diff --git a/bindings/stronghold-nodejs/examples/src/tests/lazy.ts b/bindings/stronghold-nodejs/examples/src/tests/lazy.ts index 150712e85e..990a11bd05 100644 --- a/bindings/stronghold-nodejs/examples/src/tests/lazy.ts +++ b/bindings/stronghold-nodejs/examples/src/tests/lazy.ts @@ -1,9 +1,9 @@ -import {lazy} from "../../../../wasm/examples-account/src/lazy"; -import { stronghold } from '../stronghold'; +import { lazy } from "../../../../wasm/examples-account/src/lazy"; +import { stronghold } from "../stronghold"; // Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test Stronghold Node.js examples", function () { +describe("Test Stronghold Node.js examples", function() { it("Lazy", async () => { await lazy(await stronghold()); }); -}) +}); diff --git a/bindings/stronghold-nodejs/examples/src/tests/manipulate_did.ts b/bindings/stronghold-nodejs/examples/src/tests/manipulate_did.ts index 7e9538213f..1e7044a92d 100644 --- a/bindings/stronghold-nodejs/examples/src/tests/manipulate_did.ts +++ b/bindings/stronghold-nodejs/examples/src/tests/manipulate_did.ts @@ -1,9 +1,9 @@ -import {manipulateIdentity} from "../../../../wasm/examples-account/src/manipulate_did"; -import { stronghold } from '../stronghold'; +import { manipulateIdentity } from "../../../../wasm/examples-account/src/manipulate_did"; +import { stronghold } from "../stronghold"; // Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test Stronghold Node.js examples", function () { +describe("Test Stronghold Node.js examples", function() { it("Manipulate DID", async () => { await manipulateIdentity(await stronghold()); }); -}) +}); diff --git a/bindings/stronghold-nodejs/examples/src/tests/multiple_identities.ts b/bindings/stronghold-nodejs/examples/src/tests/multiple_identities.ts index 14196cfda2..e8f77e37da 100644 --- a/bindings/stronghold-nodejs/examples/src/tests/multiple_identities.ts +++ b/bindings/stronghold-nodejs/examples/src/tests/multiple_identities.ts @@ -1,10 +1,10 @@ -import {multipleIdentities} from "../../../../wasm/examples-account/src/multiple_identities"; -import { stronghold } from '../stronghold'; +import { multipleIdentities } from "../../../../wasm/examples-account/src/multiple_identities"; +import { stronghold } from "../stronghold"; // Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test Stronghold Node.js examples", function () { +describe("Test Stronghold Node.js examples", function() { it("Multiple Identities", async () => { // TODO: Temporarily disabled until iotaledger/stronghold.rs#353 is fixed. // await multipleIdentities(await stronghold()); }); -}) +}); diff --git a/bindings/stronghold-nodejs/examples/src/tests/signing.ts b/bindings/stronghold-nodejs/examples/src/tests/signing.ts index a0d7ed417a..04029cc686 100644 --- a/bindings/stronghold-nodejs/examples/src/tests/signing.ts +++ b/bindings/stronghold-nodejs/examples/src/tests/signing.ts @@ -1,9 +1,9 @@ import { signing } from "../../../../wasm/examples-account/src/signing"; -import { stronghold } from '../stronghold'; +import { stronghold } from "../stronghold"; // Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test Stronghold Node.js examples", function () { +describe("Test Stronghold Node.js examples", function() { it("Signing", async () => { await signing(await stronghold()); }); -}) +}); diff --git a/bindings/stronghold-nodejs/examples/src/tests/storage_test_suite.ts b/bindings/stronghold-nodejs/examples/src/tests/storage_test_suite.ts index a7dea04176..e5434aa577 100644 --- a/bindings/stronghold-nodejs/examples/src/tests/storage_test_suite.ts +++ b/bindings/stronghold-nodejs/examples/src/tests/storage_test_suite.ts @@ -1,8 +1,8 @@ -import { stronghold } from '../stronghold'; -import { StorageTestSuite } from '@iota/identity-wasm/node'; +import { StorageTestSuite } from "@iota/identity-wasm/node"; +import { stronghold } from "../stronghold"; // Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test Stronghold Node.js", function () { +describe("Test Stronghold Node.js", function() { it("didCreateGenerateKey", async () => { await StorageTestSuite.didCreateGenerateKeyTest(await stronghold()); }); diff --git a/bindings/stronghold-nodejs/examples/src/tests/unchecked.ts b/bindings/stronghold-nodejs/examples/src/tests/unchecked.ts index 11127b2229..4bc3c1a5e8 100644 --- a/bindings/stronghold-nodejs/examples/src/tests/unchecked.ts +++ b/bindings/stronghold-nodejs/examples/src/tests/unchecked.ts @@ -1,9 +1,9 @@ -import {unchecked} from "../../../../wasm/examples-account/src/unchecked"; -import { stronghold } from '../stronghold'; +import { unchecked } from "../../../../wasm/examples-account/src/unchecked"; +import { stronghold } from "../stronghold"; // Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test Stronghold Node.js examples", function () { +describe("Test Stronghold Node.js examples", function() { it("Unchecked", async () => { await unchecked(await stronghold()); }); -}) +}); diff --git a/bindings/stronghold-nodejs/js/index.ts b/bindings/stronghold-nodejs/js/index.ts index 0917c05df1..e9cbdc4454 100644 --- a/bindings/stronghold-nodejs/js/index.ts +++ b/bindings/stronghold-nodejs/js/index.ts @@ -1,2 +1,2 @@ -export * from './stronghold'; -export * from '../napi-dist/napi'; +export * from "../napi-dist/napi"; +export * from "./stronghold"; diff --git a/bindings/stronghold-nodejs/js/stronghold.ts b/bindings/stronghold-nodejs/js/stronghold.ts index a6ffc3af58..2a549147ee 100644 --- a/bindings/stronghold-nodejs/js/stronghold.ts +++ b/bindings/stronghold-nodejs/js/stronghold.ts @@ -1,10 +1,30 @@ -import { NapiStronghold, NapiCoreDid, NapiDIDType, NapiKeyLocation, NapiKeyType, NapiDidLocation, NapiEncryptedData, NapiEncryptionAlgorithm, NapiCekAlgorithm } from '../napi-dist/napi'; -import { CoreDID, DIDType, KeyLocation, Signature, Storage, KeyType, EncryptedData, EncryptionAlgorithm, CekAlgorithm } from "@iota/identity-wasm/node"; +import { + CekAlgorithm, + CoreDID, + DIDType, + EncryptedData, + EncryptionAlgorithm, + KeyLocation, + KeyType, + Signature, + Storage, +} from "@iota/identity-wasm/node"; +import { + NapiCekAlgorithm, + NapiCoreDid, + NapiDidLocation, + NapiDIDType, + NapiEncryptedData, + NapiEncryptionAlgorithm, + NapiKeyLocation, + NapiKeyType, + NapiStronghold, +} from "../napi-dist/napi"; export class Stronghold implements Storage { private napiStronghold: NapiStronghold; - constructor() { } + constructor() {} public async init(snapshot: string, password: string, dropsave?: boolean) { this.napiStronghold = await NapiStronghold.new(snapshot, password, dropsave); @@ -12,14 +32,19 @@ export class Stronghold implements Storage { public static async build(snapshot: string, password: string, dropsave?: boolean) { const stronghold = new Stronghold(); - await stronghold.init(snapshot, password, dropsave) - return stronghold + await stronghold.init(snapshot, password, dropsave); + return stronghold; } - public async didCreate(didType: DIDType, network: string, fragment: string, private_key?: Uint8Array): Promise<[CoreDID, KeyLocation]> { + public async didCreate( + didType: DIDType, + network: string, + fragment: string, + private_key?: Uint8Array, + ): Promise<[CoreDID, KeyLocation]> { let optPrivateKey = undefined; if (private_key) { - optPrivateKey = Array.from(private_key) + optPrivateKey = Array.from(private_key); } let napiDIDType: NapiDIDType | undefined = undefined; @@ -31,7 +56,12 @@ export class Stronghold implements Storage { throw new Error("unexpected did type"); } - const napiDidLocation: NapiDidLocation = await this.napiStronghold.didCreate(napiDIDType, network, fragment, optPrivateKey); + const napiDidLocation: NapiDidLocation = await this.napiStronghold.didCreate( + napiDIDType, + network, + fragment, + optPrivateKey, + ); const did: CoreDID = CoreDID.fromJSON(napiDidLocation.did().toJSON()); const location: KeyLocation = KeyLocation.fromJSON(napiDidLocation.keyLocation().toJSON()); @@ -51,7 +81,7 @@ export class Stronghold implements Storage { public async didList(): Promise> { const napiDids: Array = await this.napiStronghold.didList(); - const dids: Array = napiDids.map((did) => CoreDID.fromJSON(did.toJSON())) + const dids: Array = napiDids.map((did) => CoreDID.fromJSON(did.toJSON())); return dids; } @@ -61,10 +91,10 @@ export class Stronghold implements Storage { let napiKeyType: NapiKeyType | undefined = undefined; switch (keyType) { case KeyType.Ed25519: - napiKeyType = NapiKeyType.Ed25519 + napiKeyType = NapiKeyType.Ed25519; break; case KeyType.X25519: - napiKeyType = NapiKeyType.X25519 + napiKeyType = NapiKeyType.X25519; break; default: throw new Error("unexpected key type"); @@ -83,7 +113,7 @@ export class Stronghold implements Storage { public async keyExists(did: CoreDID, keyLocation: KeyLocation): Promise { const napiDID: NapiCoreDid = NapiCoreDid.fromJSON(did.toJSON()); const napiKeyLocation: NapiKeyLocation = NapiKeyLocation.fromJSON(keyLocation.toJSON()); - return this.napiStronghold.keyExists(napiDID, napiKeyLocation) + return this.napiStronghold.keyExists(napiDID, napiKeyLocation); } public async keyPublic(did: CoreDID, keyLocation: KeyLocation): Promise { @@ -106,21 +136,47 @@ export class Stronghold implements Storage { return Signature.fromJSON(napiSignature.toJSON()); } - public async dataEncrypt(did: CoreDID, plaintext: Uint8Array, associatedData: Uint8Array, encryptionAlgorithm: EncryptionAlgorithm, cekAlgorithm: CekAlgorithm, publicKey: Uint8Array): Promise { + public async dataEncrypt( + did: CoreDID, + plaintext: Uint8Array, + associatedData: Uint8Array, + encryptionAlgorithm: EncryptionAlgorithm, + cekAlgorithm: CekAlgorithm, + publicKey: Uint8Array, + ): Promise { const napiDID: NapiCoreDid = NapiCoreDid.fromJSON(did.toJSON()); const napiCekAlgorithm = NapiCekAlgorithm.fromJSON(cekAlgorithm.toJSON()); const napiEncryptionAlgorithm = NapiEncryptionAlgorithm.fromJSON(encryptionAlgorithm.toJSON()); - const napiEncryptedData = await this.napiStronghold.dataEncrypt(napiDID, Array.from(plaintext), Array.from(associatedData), napiEncryptionAlgorithm, napiCekAlgorithm, Array.from(publicKey)); + const napiEncryptedData = await this.napiStronghold.dataEncrypt( + napiDID, + Array.from(plaintext), + Array.from(associatedData), + napiEncryptionAlgorithm, + napiCekAlgorithm, + Array.from(publicKey), + ); return EncryptedData.fromJSON(napiEncryptedData.toJSON()); } - public async dataDecrypt(did: CoreDID, data: EncryptedData, encryptionAlgorithm: EncryptionAlgorithm, cekAlgorithm: CekAlgorithm, privateKey: KeyLocation): Promise { + public async dataDecrypt( + did: CoreDID, + data: EncryptedData, + encryptionAlgorithm: EncryptionAlgorithm, + cekAlgorithm: CekAlgorithm, + privateKey: KeyLocation, + ): Promise { const napiDID: NapiCoreDid = NapiCoreDid.fromJSON(did.toJSON()); const napiPrivateKeyLocation = NapiKeyLocation.fromJSON(privateKey.toJSON()); const napiCekAlgorithm = NapiCekAlgorithm.fromJSON(cekAlgorithm.toJSON()); const napiEncryptionAlgorithm = NapiEncryptionAlgorithm.fromJSON(encryptionAlgorithm.toJSON()); const napiEncryptedData = NapiEncryptedData.fromJSON(data.toJSON()); - const decryptedData = await this.napiStronghold.dataDecrypt(napiDID, napiEncryptedData, napiEncryptionAlgorithm, napiCekAlgorithm, napiPrivateKeyLocation); + const decryptedData = await this.napiStronghold.dataDecrypt( + napiDID, + napiEncryptedData, + napiEncryptionAlgorithm, + napiCekAlgorithm, + napiPrivateKeyLocation, + ); return Uint8Array.from(decryptedData); } @@ -129,7 +185,7 @@ export class Stronghold implements Storage { const value: number[] | undefined = await this.napiStronghold.blobGet(napiDID); if (value) { - return Uint8Array.from(value) + return Uint8Array.from(value); } else { return undefined; } @@ -141,6 +197,6 @@ export class Stronghold implements Storage { } public async flushChanges() { - return this.napiStronghold.flushChanges() + return this.napiStronghold.flushChanges(); } } diff --git a/bindings/stronghold-nodejs/rollup.config.js b/bindings/stronghold-nodejs/rollup.config.js index d3602ae2cd..079751e7ac 100644 --- a/bindings/stronghold-nodejs/rollup.config.js +++ b/bindings/stronghold-nodejs/rollup.config.js @@ -1,28 +1,27 @@ -import typescript from '@rollup/plugin-typescript'; -import commonjs from '@rollup/plugin-commonjs'; +import commonjs from "@rollup/plugin-commonjs"; +import typescript from "@rollup/plugin-typescript"; +import copy from "rollup-plugin-copy"; import dts from "rollup-plugin-dts"; -import copy from 'rollup-plugin-copy' export default [{ - input: 'js/index.ts', + input: "js/index.ts", output: { - dir: 'dist', - format: 'cjs' + dir: "dist", + format: "cjs", }, - external: ['@iota/identity-wasm/node', 'fs', 'path'], // so it's not included + external: ["@iota/identity-wasm/node", "fs", "path"], // so it's not included plugins: [ typescript(), commonjs(), copy({ targets: [ - { src: 'napi-dist/*.node', dest: 'dist' } - ] - }) + { src: "napi-dist/*.node", dest: "dist" }, + ], + }), ], }, { // path to your declaration files root - input: './dist/index.d.ts', - output: [{ file: 'dist/index.d.ts', format: 'es' }], + input: "./dist/index.d.ts", + output: [{ file: "dist/index.d.ts", format: "es" }], plugins: [dts()], -}, -]; +}]; diff --git a/bindings/stronghold-nodejs/tests/txm_readme.js b/bindings/stronghold-nodejs/tests/txm_readme.js index a784b1828c..0f1ed5d0c0 100644 --- a/bindings/stronghold-nodejs/tests/txm_readme.js +++ b/bindings/stronghold-nodejs/tests/txm_readme.js @@ -1,7 +1,7 @@ -const {execSync} = require('child_process') +const { execSync } = require("child_process"); -describe("Test TXM", function () { +describe("Test TXM", function() { it("README examples pass", async () => { - execSync("txm README.md") + execSync("txm README.md"); }); -}) +}); diff --git a/bindings/wasm/build/docs.js b/bindings/wasm/build/docs.js index 5504feb30a..b496ccd7d0 100644 --- a/bindings/wasm/build/docs.js +++ b/bindings/wasm/build/docs.js @@ -1,15 +1,15 @@ -const fs = require('fs') -const path = require('path') -const jsdoc2md = require('jsdoc-to-markdown') +const fs = require("fs"); +const path = require("path"); +const jsdoc2md = require("jsdoc-to-markdown"); -const importFile = path.join(__dirname, '../node/identity_wasm.js') -const exportFile = path.join(__dirname, '../docs/api-reference.md') +const importFile = path.join(__dirname, "../node/identity_wasm.js"); +const exportFile = path.join(__dirname, "../docs/api-reference.md"); -const docsRoot = path.join(__dirname, '../docs') -const docsData = jsdoc2md.renderSync({ files: importFile }) +const docsRoot = path.join(__dirname, "../docs"); +const docsData = jsdoc2md.renderSync({ files: importFile }); if (!fs.existsSync(docsRoot)) { - fs.mkdirSync(docsRoot) + fs.mkdirSync(docsRoot); } -fs.writeFileSync(exportFile, docsData) +fs.writeFileSync(exportFile, docsData); diff --git a/bindings/wasm/build/lints.js b/bindings/wasm/build/lints.js index a98e5aafec..0bb4759737 100644 --- a/bindings/wasm/build/lints.js +++ b/bindings/wasm/build/lints.js @@ -1,11 +1,11 @@ /** Aborts the build process if disallowed occurrences are found in identity_wasm.js **/ function lintBigInt(content) { if (content.includes("BigInt64Array") || content.includes("BigUint64Array")) { - throw( - "Build artifacts should not include BigInt64Array/BigUint64Array imports\n" + - "to ensure React Native/WebKit compatibility.\n" + - "Remove any u64 and i64 occurrence from the public Wasm interface.\n" + - "See: https://github.com/iotaledger/identity.rs/issues/362\n" + throw ( + "Build artifacts should not include BigInt64Array/BigUint64Array imports\n" + + "to ensure React Native/WebKit compatibility.\n" + + "Remove any u64 and i64 occurrence from the public Wasm interface.\n" + + "See: https://github.com/iotaledger/identity.rs/issues/362\n" ); } } @@ -18,11 +18,11 @@ function lintBigInt(content) { * * Functions which take owned parameters cause this situation; the solution is to borrow and clone the parameter * instead. - **/ + */ function lintPtrNullWithoutFree(content) { // Find line numbers of offending code. const lines = content.split(/\r?\n/); - const matches = lines.flatMap(function (line, number) { + const matches = lines.flatMap(function(line, number) { if (/(? 0) { - throw(`ERROR: generated Javascript should not include 'obj.ptr = 0;'. + throw (`ERROR: generated Javascript should not include 'obj.ptr = 0;'. When weak references are enabled with '--weak-refs', WasmRefCell in wasm-bindgen causes runtime errors from automatic garbage collection trying to free objects taken as owned parameters. @@ -47,7 +47,7 @@ See: https://github.com/rustwasm/wasm-bindgen/pull/2677`); * This is typically due to Wasm compatibility features not being enabled on crate dependencies. **/ function lintImportEnv(content) { if (content.includes("imports['env']") || content.includes("require('env')") || content.includes("from 'env'")) { - throw(`ERROR: generated Javascript should not include any imports for 'env', e.g.: + throw (`ERROR: generated Javascript should not include any imports for 'env', e.g.: - imports['env'] = require('env'); - imports['env'] = __wbg_star0; diff --git a/bindings/wasm/build/node.js b/bindings/wasm/build/node.js index c23f8eeaaf..376b3f5be1 100644 --- a/bindings/wasm/build/node.js +++ b/bindings/wasm/build/node.js @@ -1,10 +1,10 @@ -const path = require('path') -const fs = require('fs') -const { lintAll } = require('./lints') -const generatePackage = require('./utils/generatePackage'); +const path = require("path"); +const fs = require("fs"); +const { lintAll } = require("./lints"); +const generatePackage = require("./utils/generatePackage"); -const RELEASE_FOLDER = path.join(__dirname, '../node/'); -const entryFilePathNode = path.join(RELEASE_FOLDER, 'identity_wasm.js'); +const RELEASE_FOLDER = path.join(__dirname, "../node/"); +const entryFilePathNode = path.join(RELEASE_FOLDER, "identity_wasm.js"); const entryFileNode = fs.readFileSync(entryFilePathNode).toString(); lintAll(entryFileNode); @@ -19,16 +19,17 @@ let changedFileNode = entryFileNode.replace( globalThis.Response = fetch.Response globalThis.fetch = fetch } -let imports = {};`); +let imports = {};`, +); fs.writeFileSync( entryFilePathNode, - changedFileNode + changedFileNode, ); // Generate `package.json`. const newPackage = generatePackage({ - main: 'index.js', - types: 'index.d.ts', + main: "index.js", + types: "index.d.ts", }); -fs.writeFileSync(path.join(RELEASE_FOLDER, 'package.json'), JSON.stringify(newPackage, null, 2)); +fs.writeFileSync(path.join(RELEASE_FOLDER, "package.json"), JSON.stringify(newPackage, null, 2)); diff --git a/bindings/wasm/build/utils/generatePackage.js b/bindings/wasm/build/utils/generatePackage.js index 15609a2963..25109c2b06 100644 --- a/bindings/wasm/build/utils/generatePackage.js +++ b/bindings/wasm/build/utils/generatePackage.js @@ -1,7 +1,6 @@ -const rootPackage = require('../../package.json') +const rootPackage = require("../../package.json"); module.exports = (options) => { - const newPackage = { name: rootPackage.name, description: rootPackage.description, @@ -12,11 +11,10 @@ module.exports = (options) => { module: options.module, main: options.main, types: options.types, - } + }; // remove empty keys - Object.keys(newPackage).forEach(key => newPackage[key] === undefined && delete newPackage[key]) + Object.keys(newPackage).forEach(key => newPackage[key] === undefined && delete newPackage[key]); return newPackage; - -} +}; diff --git a/bindings/wasm/build/web.js b/bindings/wasm/build/web.js index 700f45b945..fd8c25174c 100644 --- a/bindings/wasm/build/web.js +++ b/bindings/wasm/build/web.js @@ -1,11 +1,11 @@ -const path = require('path'); -const fs = require('fs'); -const fse = require('fs-extra'); -const { lintAll } = require('./lints'); -const generatePackage = require('./utils/generatePackage'); +const path = require("path"); +const fs = require("fs"); +const fse = require("fs-extra"); +const { lintAll } = require("./lints"); +const generatePackage = require("./utils/generatePackage"); -const RELEASE_FOLDER = path.join(__dirname, '../web/'); -const entryFilePath = path.join(RELEASE_FOLDER, 'identity_wasm.js'); +const RELEASE_FOLDER = path.join(__dirname, "../web/"); +const entryFilePath = path.join(RELEASE_FOLDER, "identity_wasm.js"); const entryFile = fs.readFileSync(entryFilePath).toString(); lintAll(entryFile); @@ -15,42 +15,42 @@ let changedFile = entryFile // Regex to avoid hard-coding 'identity_wasm_bg.wasm'. .replace( /input = new URL\((.*), import\.meta\.url\);/i, - "// input = new URL($1, import.meta.url);" + "// input = new URL($1, import.meta.url);", ) // Rename original init function, because we want to use the name for our own function. .replace( "async function init(input) {", - "async function initWasm(input) {" + "async function initWasm(input) {", ) .replace( "init.__wbindgen_wasm_module = module;", - "initWasm.__wbindgen_wasm_module = module;" + "initWasm.__wbindgen_wasm_module = module;", ) // Create an init function which imports the wasm file. .replace( "export default init;", - "let __initializedIotaWasm = false\r\n\r\nexport function init(path) {\r\n if (__initializedIotaWasm) {\r\n return Promise.resolve(wasm)\r\n }\r\n return initWasm(path || \'identity_wasm_bg.wasm\').then(() => {\r\n __initializedIotaWasm = true\r\n return wasm\r\n })\r\n}\r\n" + "let __initializedIotaWasm = false\r\n\r\nexport function init(path) {\r\n if (__initializedIotaWasm) {\r\n return Promise.resolve(wasm)\r\n }\r\n return initWasm(path || 'identity_wasm_bg.wasm').then(() => {\r\n __initializedIotaWasm = true\r\n return wasm\r\n })\r\n}\r\n", ); fs.writeFileSync( entryFilePath, - changedFile + changedFile, ); -const entryFilePathTs = path.join(RELEASE_FOLDER, 'identity_wasm.d.ts'); +const entryFilePathTs = path.join(RELEASE_FOLDER, "identity_wasm.d.ts"); const entryFileTs = fs.readFileSync(entryFilePathTs).toString(); // Replace the init function in the ts file. let changedFileTs = entryFileTs.replace( "/**\n* If `module_or_path` is {RequestInfo} or {URL}, makes a request and\n* for everything else, calls `WebAssembly.instantiate` directly.\n*\n* @param {InitInput | Promise} module_or_path\n*\n* @returns {Promise}\n*/\nexport default function init (module_or_path?: InitInput | Promise): Promise;", - "\/**\r\n* Loads the Wasm file so the lib can be used, relative path to Wasm file\r\n* @param {string | undefined} path\r\n*\/\r\nexport function init (path?: string): Promise;" + "\/**\r\n* Loads the Wasm file so the lib can be used, relative path to Wasm file\r\n* @param {string | undefined} path\r\n*\/\r\nexport function init (path?: string): Promise;", ); fs.writeFileSync( entryFilePathTs, - changedFileTs + changedFileTs, ); // Generate `package.json`. const newPackage = generatePackage({ - module: 'index.js', - types: 'index.d.ts', + module: "index.js", + types: "index.d.ts", }); -fs.writeFileSync(path.join(RELEASE_FOLDER, 'package.json'), JSON.stringify(newPackage, null, 2)); +fs.writeFileSync(path.join(RELEASE_FOLDER, "package.json"), JSON.stringify(newPackage, null, 2)); diff --git a/bindings/wasm/cypress.config.ts b/bindings/wasm/cypress.config.ts index a07a23445c..bc54cc17fa 100644 --- a/bindings/wasm/cypress.config.ts +++ b/bindings/wasm/cypress.config.ts @@ -1,13 +1,13 @@ -import { defineConfig } from 'cypress' +import { defineConfig } from "cypress"; export default defineConfig({ - screenshotOnRunFailure: false, - video: false, - retries: { - runMode: 3, - }, - e2e: { - setupNodeEvents(on, config) {}, - supportFile: false, - }, -}) + screenshotOnRunFailure: false, + video: false, + retries: { + runMode: 3, + }, + e2e: { + setupNodeEvents(on, config) {}, + supportFile: false, + }, +}); diff --git a/bindings/wasm/examples-account/src/config.ts b/bindings/wasm/examples-account/src/config.ts index 9512d3de6b..c2fd58efe1 100644 --- a/bindings/wasm/examples-account/src/config.ts +++ b/bindings/wasm/examples-account/src/config.ts @@ -1,13 +1,12 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { AccountBuilder, Network, ExplorerUrl, AutoSave, Storage } from '../../node'; +import { AccountBuilder, AutoSave, ExplorerUrl, Network, Storage } from "../../node"; /** * This example demonstrates some of the configuration options for the account. */ async function config(storage?: Storage) { - // Set-up for a private Tangle // You can use https://github.com/iotaledger/one-click-tangle for a local setup. // The `network_name` needs to match the id of the network or a part of it. @@ -16,7 +15,7 @@ async function config(storage?: Storage) { // private tangle is `private-tangle`, but we can only use 6 characters. // Keep in mind, there are easier ways to change to devnet via `Network::Devnet` const network_name = "dev"; - let network = Network.tryFromName(network_name) + let network = Network.tryFromName(network_name); // If you deployed an explorer locally this would usually be `http://127.0.0.1:8082` const explorer = ExplorerUrl.parse("https://explorer.iota.org/devnet"); @@ -24,7 +23,6 @@ async function config(storage?: Storage) { // In a locally running one-click tangle, this would usually be `http://127.0.0.1:14265` let private_node_url = "https://api.lb-0.h.chrysalis-devnet.iota.cafe"; - // The creation step generates a keypair, builds an identity // and publishes it to the IOTA mainnet. const builder = new AccountBuilder({ @@ -34,7 +32,7 @@ async function config(storage?: Storage) { autopublish: true, // publish to the tangle automatically on every update clientConfig: { network: network, - primaryNode: { url: private_node_url } + primaryNode: { url: private_node_url }, }, storage, }); @@ -48,7 +46,6 @@ async function config(storage?: Storage) { // Prints the Identity Resolver Explorer URL. // The entire history can be observed on this page by clicking "Loading History". console.log(`[Example] Explore the DID Document = ${explorer.resolverUrl(did)}`); - } catch (e) { if (e instanceof Error) { console.log(`[Example] Error: ${e.message}`); diff --git a/bindings/wasm/examples-account/src/create_did.ts b/bindings/wasm/examples-account/src/create_did.ts index c1016af0c8..9f35977fa4 100644 --- a/bindings/wasm/examples-account/src/create_did.ts +++ b/bindings/wasm/examples-account/src/create_did.ts @@ -1,14 +1,13 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { AccountBuilder, ExplorerUrl, Storage } from '../../node'; +import { AccountBuilder, ExplorerUrl, Storage } from "../../node"; /** * This example shows a basic introduction on how to create a basic DID Document and upload it to the Tangle * using the Account. */ async function createIdentity(storage?: Storage) { - // The creation step generates a keypair, builds an identity // and publishes it to the IOTA mainnet. let builder = new AccountBuilder({ @@ -20,7 +19,7 @@ async function createIdentity(storage?: Storage) { let did = account.did(); // Print the DID of the created Identity. - console.log(did.toString()) + console.log(did.toString()); // Print the local state of the DID Document console.log(account.document()); diff --git a/bindings/wasm/examples-account/src/create_vc.ts b/bindings/wasm/examples-account/src/create_vc.ts index 6d78543bcd..91a81a0265 100644 --- a/bindings/wasm/examples-account/src/create_vc.ts +++ b/bindings/wasm/examples-account/src/create_vc.ts @@ -2,15 +2,15 @@ // SPDX-License-Identifier: Apache-2.0 import { + AccountBuilder, Credential, CredentialValidationOptions, CredentialValidator, FailFast, + MethodContent, ProofOptions, - AccountBuilder, Storage, - MethodContent, -} from './../../node/identity_wasm.js'; +} from "./../../node/identity_wasm.js"; /** This example shows how to create a Verifiable Credential and validate it. @@ -29,8 +29,8 @@ async function createVC(storage?: Storage) { // Add verification method to the issuer. await issuer.createMethod({ content: MethodContent.GenerateEd25519(), - fragment: "#issuerKey" - }) + fragment: "#issuerKey", + }); // Create an identity for the holder, in this case also the subject. const alice = await builder.createIdentity(); @@ -41,7 +41,7 @@ async function createVC(storage?: Storage) { name: "Alice", degreeName: "Bachelor of Science and Arts", degreeType: "BachelorDegree", - GPA: "4.0" + GPA: "4.0", }; // Create an unsigned `UniversityDegree` credential for Alice @@ -49,7 +49,7 @@ async function createVC(storage?: Storage) { id: "https://example.edu/credentials/3732", type: "UniversityDegreeCredential", issuer: issuer.document().id(), - credentialSubject: subject + credentialSubject: subject, }); // Created a signed credential by the issuer. @@ -62,7 +62,6 @@ async function createVC(storage?: Storage) { // Before sending this credential to the holder the issuer wants to validate that some properties // of the credential satisfy their expectations. - // Validate the credential's signature, the credential's semantic structure, // check that the issuance date is not in the future and that the expiration date is not in the past. // TODO: uncomment when ported to Stardust. @@ -80,7 +79,7 @@ async function createVC(storage?: Storage) { // The credential is then serialized to JSON and transmitted to the holder in a secure manner. // Note that the credential is NOT published to the IOTA Tangle. It is sent and stored off-chain. const credentialJSON = signedVc.toJSON(); - return {alice, issuer, credentialJSON}; + return { alice, issuer, credentialJSON }; } -export {createVC}; +export { createVC }; diff --git a/bindings/wasm/examples-account/src/create_vp.ts b/bindings/wasm/examples-account/src/create_vp.ts index ebb4cc378a..558bdb3432 100644 --- a/bindings/wasm/examples-account/src/create_vp.ts +++ b/bindings/wasm/examples-account/src/create_vp.ts @@ -7,17 +7,17 @@ import { CredentialValidationOptions, Duration, FailFast, + MethodContent, + Network, Presentation, PresentationValidationOptions, ProofOptions, Resolver, + Storage, SubjectHolderRelationship, Timestamp, VerifierOptions, - Storage, - MethodContent, - Network -} from './../../node/identity_wasm.js'; +} from "./../../node/identity_wasm.js"; /** This example shows how to create a Verifiable Presentation and validate it. @@ -40,8 +40,8 @@ async function createVP(storage?: Storage) { // Add a dedicated verification method to the issuer, with which to sign credentials. await issuer.createMethod({ content: MethodContent.GenerateEd25519(), - fragment: "issuerKey" - }) + fragment: "issuerKey", + }); // Create an identity for the holder, in this case also the subject. const alice = await builder.createIdentity(); @@ -49,8 +49,8 @@ async function createVP(storage?: Storage) { // Add verification method to the holder. await alice.createMethod({ content: MethodContent.GenerateEd25519(), - fragment: "aliceKey" - }) + fragment: "aliceKey", + }); // =========================================================================== // Step 2: Issuer creates and signs a Verifiable Credential. @@ -62,7 +62,7 @@ async function createVP(storage?: Storage) { name: "Alice", degreeName: "Bachelor of Science and Arts", degreeType: "BachelorDegree", - GPA: "4.0" + GPA: "4.0", }; // Create an unsigned `UniversityDegree` credential for Alice @@ -70,7 +70,7 @@ async function createVP(storage?: Storage) { id: "https://example.edu/credentials/3732", type: "UniversityDegreeCredential", issuer: issuer.document().id(), - credentialSubject: subject + credentialSubject: subject, }); // Created a signed credential by the issuer. @@ -89,7 +89,6 @@ async function createVP(storage?: Storage) { const signedVcJson = signedVc.toJSON(); console.log(`Credential JSON >`, JSON.stringify(signedVcJson, null, 2)); - // =========================================================================== // Step 4: Verifier sends the holder a challenge and requests a signed Verifiable Presentation. // =========================================================================== @@ -101,7 +100,6 @@ async function createVP(storage?: Storage) { // 10 minutes from now. const expires = Timestamp.nowUTC().checkedAdd(Duration.minutes(10)); - // =========================================================================== // Step 5: Holder creates a verifiable presentation from the issued credential for the verifier to validate. // =========================================================================== @@ -112,8 +110,8 @@ async function createVP(storage?: Storage) { // Create a Verifiable Presentation from the Credential const unsignedVp = new Presentation({ holder: alice.did(), - verifiableCredential: receivedVc - }) + verifiableCredential: receivedVc, + }); // Sign the verifiable presentation using the holder's verification method // and include the requested challenge and expiry timestamp. @@ -122,8 +120,8 @@ async function createVP(storage?: Storage) { unsignedVp, new ProofOptions({ challenge: challenge, - expires - }) + expires, + }), ); // =========================================================================== diff --git a/bindings/wasm/examples-account/src/custom_storage.ts b/bindings/wasm/examples-account/src/custom_storage.ts index b0b3b2856d..b595645dc7 100644 --- a/bindings/wasm/examples-account/src/custom_storage.ts +++ b/bindings/wasm/examples-account/src/custom_storage.ts @@ -1,7 +1,19 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { DID, Ed25519, KeyLocation, KeyPair, KeyType, Signature, Storage, StorageTestSuite, EncryptionAlgorithm, CekAlgorithm, EncryptedData } from '../../node/identity_wasm.js'; +import { + CekAlgorithm, + DID, + Ed25519, + EncryptedData, + EncryptionAlgorithm, + KeyLocation, + KeyPair, + KeyType, + Signature, + Storage, + StorageTestSuite, +} from "../../node/identity_wasm.js"; /** An insecure, in-memory `Storage` implementation that serves as an example. This can be passed to the `AccountBuilder` to create accounts with this as the storage. */ @@ -127,7 +139,7 @@ export class MemStore implements Storage { if (vault) { return vault.has(keyLocation.canonical()); } else { - return false + return false; } } @@ -139,12 +151,12 @@ export class MemStore implements Storage { if (vault) { const keyPair: KeyPair | undefined = vault.get(keyLocation.canonical()); if (keyPair) { - return keyPair.public() + return keyPair.public(); } else { - throw new Error('Key location not found') + throw new Error("Key location not found"); } } else { - throw new Error('DID not found') + throw new Error("DID not found"); } } @@ -163,7 +175,7 @@ export class MemStore implements Storage { public async keySign(did: DID, keyLocation: KeyLocation, data: Uint8Array): Promise { if (keyLocation.keyType() !== KeyType.Ed25519) { - throw new Error('Unsupported Method') + throw new Error("Unsupported Method"); } // Get the vault for the given DID. @@ -176,21 +188,34 @@ export class MemStore implements Storage { // Use the `Ed25519` API to sign the given data with the private key. const signature: Uint8Array = Ed25519.sign(data, keyPair.private()); // Construct a new `Signature` wrapper with the returned signature bytes. - return new Signature(signature) + return new Signature(signature); } else { - throw new Error('Key location not found') + throw new Error("Key location not found"); } } else { - throw new Error('DID not found') + throw new Error("DID not found"); } } - public async dataEncrypt(did: DID, plaintext: Uint8Array, associatedData: Uint8Array, encryptionAlgorithm: EncryptionAlgorithm, cekAlgorithm: CekAlgorithm, publicKey: Uint8Array): Promise { - throw new Error('not yet implemented') - } - - public async dataDecrypt(did: DID, data: EncryptedData, encryptionAlgorithm: EncryptionAlgorithm, cekAlgorithm: CekAlgorithm, privateKey: KeyLocation): Promise { - throw new Error('not yet implemented') + public async dataEncrypt( + did: DID, + plaintext: Uint8Array, + associatedData: Uint8Array, + encryptionAlgorithm: EncryptionAlgorithm, + cekAlgorithm: CekAlgorithm, + publicKey: Uint8Array, + ): Promise { + throw new Error("not yet implemented"); + } + + public async dataDecrypt( + did: DID, + data: EncryptedData, + encryptionAlgorithm: EncryptionAlgorithm, + cekAlgorithm: CekAlgorithm, + privateKey: KeyLocation, + ): Promise { + throw new Error("not yet implemented"); } public async blobGet(did: DID): Promise { diff --git a/bindings/wasm/examples-account/src/encryption.ts b/bindings/wasm/examples-account/src/encryption.ts index 3d55de305f..3472e9be3b 100644 --- a/bindings/wasm/examples-account/src/encryption.ts +++ b/bindings/wasm/examples-account/src/encryption.ts @@ -1,4 +1,14 @@ -import {AgreementInfo, AccountBuilder, Client, MethodContent, Storage, MethodScope, EncryptionAlgorithm, CekAlgorithm, Resolver} from './../../node/identity_wasm.js'; +import { + AccountBuilder, + AgreementInfo, + CekAlgorithm, + Client, + EncryptionAlgorithm, + MethodContent, + MethodScope, + Resolver, + Storage, +} from "./../../node/identity_wasm.js"; /** * This example demonstrates Elliptic-curve Diffie-Hellman (ECDH) cryptographic key exchange @@ -8,14 +18,14 @@ async function encryption(storage?: Storage) { let builder = new AccountBuilder({ storage, }); - + // Alice creates and publishes their DID Document (see create_did and manipulate_did examples). let aliceAccount = await builder.createIdentity(); await aliceAccount.createMethod({ fragment: "kex-0", scope: MethodScope.KeyAgreement(), content: MethodContent.GenerateX25519(), - }) + }); // Bob creates and publishes their DID Document (see create_did and manipulate_did examples). let bobAccount = await builder.createIdentity(); @@ -23,7 +33,7 @@ async function encryption(storage?: Storage) { fragment: "kex-0", scope: MethodScope.KeyAgreement(), content: MethodContent.GenerateX25519(), - }) + }); // Alice and Bob tell each other their DIDs. They each resolve the DID Document of the other // to obtain their X25519 public key. Note that in practice, they would run this code completely @@ -36,27 +46,38 @@ async function encryption(storage?: Storage) { const bobPublicKey = bobMethod.data().tryDecode(); // Alice encrypts the data using Diffie-Hellman key exchange - const agreementInfo = new AgreementInfo(Buffer.from("Alice"), Buffer.from("Bob"), new Uint8Array(0), new Uint8Array(0)); + const agreementInfo = new AgreementInfo( + Buffer.from("Alice"), + Buffer.from("Bob"), + new Uint8Array(0), + new Uint8Array(0), + ); const encryptionAlgorithm = EncryptionAlgorithm.A256GCM(); const cekAlgorithm = CekAlgorithm.EcdhEs(agreementInfo); const message = Buffer.from("This msg will be encrypted and decrypted"); const associatedData = Buffer.from("associatedData"); - const encryptedData = await aliceAccount.encryptData(message, associatedData, encryptionAlgorithm, cekAlgorithm, bobPublicKey); + const encryptedData = await aliceAccount.encryptData( + message, + associatedData, + encryptionAlgorithm, + cekAlgorithm, + bobPublicKey, + ); // Bob must be able to decrypt the message using the shared secret. const decryptedMessage = await bobAccount.decryptData(encryptedData, encryptionAlgorithm, cekAlgorithm, "kex-0"); - if(!isArrayEqual(message, decryptedMessage)) throw new Error("decrypted message does not match original message!"); + if (!isArrayEqual(message, decryptedMessage)) throw new Error("decrypted message does not match original message!"); console.log(`Diffie-Hellman key exchange successful!`); } function isArrayEqual(a: Buffer, b: Uint8Array) { - if(a.length !== b.length) return false; - for(let i = 0; i < a.length; i++) { - if(a[i] !== b[i]) return false; + if (a.length !== b.length) return false; + for (let i = 0; i < a.length; i++) { + if (a[i] !== b[i]) return false; } return true; } -export {encryption}; \ No newline at end of file +export { encryption }; diff --git a/bindings/wasm/examples-account/src/lazy.ts b/bindings/wasm/examples-account/src/lazy.ts index 900889e3c9..46a88c238e 100644 --- a/bindings/wasm/examples-account/src/lazy.ts +++ b/bindings/wasm/examples-account/src/lazy.ts @@ -1,18 +1,13 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { - AccountBuilder, - ExplorerUrl, - Storage -} from '../../node'; +import { AccountBuilder, ExplorerUrl, Storage } from "../../node"; /** * This example demonstrates how to take control over publishing DID updates manually, * instead of the default automated behavior. */ async function lazy(storage?: Storage) { - // Create a new Account with auto publishing set to false. // This means updates are not pushed to the tangle automatically. // Rather, when we publish, multiple updates are batched together. @@ -26,8 +21,8 @@ async function lazy(storage?: Storage) { await account.createService({ fragment: "example-service", type: "LinkedDomains", - endpoint: "https://example.org" - }) + endpoint: "https://example.org", + }); // Publish the newly created DID document, // including the new service, to the tangle. @@ -37,12 +32,12 @@ async function lazy(storage?: Storage) { await account.createService({ fragment: "another-service", type: "LinkedDomains", - endpoint: "https://example.org" + endpoint: "https://example.org", }); // Delete the previously added service. await account.deleteService({ - fragment: "example-service" + fragment: "example-service", }); // Publish the updates as one message to the tangle. diff --git a/bindings/wasm/examples-account/src/manipulate_did.ts b/bindings/wasm/examples-account/src/manipulate_did.ts index 1cd7df5692..9cc42eab96 100644 --- a/bindings/wasm/examples-account/src/manipulate_did.ts +++ b/bindings/wasm/examples-account/src/manipulate_did.ts @@ -1,14 +1,13 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import {AccountBuilder, ExplorerUrl, MethodContent, MethodRelationship, Storage} from './../../node/identity_wasm.js'; +import { AccountBuilder, ExplorerUrl, MethodContent, MethodRelationship, Storage } from "./../../node/identity_wasm.js"; /** * This example demonstrates how to manipulate a DID Document by adding/removing * Verification Methods and Services. */ async function manipulateIdentity(storage?: Storage) { - // =========================================================================== // Create Identity - Similar to create_did example // =========================================================================== @@ -26,27 +25,27 @@ async function manipulateIdentity(storage?: Storage) { // Add another Ed25519 verification method to the identity. await account.createMethod({ content: MethodContent.GenerateEd25519(), - fragment: "my-next-key" - }) + fragment: "my-next-key", + }); // Associate the newly created method with additional verification relationships. await account.attachMethodRelationships({ fragment: "my-next-key", relationships: [ MethodRelationship.CapabilityDelegation, - MethodRelationship.CapabilityInvocation - ] - }) + MethodRelationship.CapabilityInvocation, + ], + }); // Add a new service to the identity. await account.createService({ fragment: "my-service-1", type: "MyCustomService", - endpoint: "https://example.com" - }) + endpoint: "https://example.com", + }); // Remove the Ed25519 verification method. - await account.deleteMethod({fragment: "my-next-key"}) + await account.deleteMethod({ fragment: "my-next-key" }); // Retrieve the DID of the newly created identity. let did = account.did(); @@ -55,4 +54,4 @@ async function manipulateIdentity(storage?: Storage) { console.log(`Explorer Url:`, ExplorerUrl.mainnet().resolverUrl(did)); } -export {manipulateIdentity}; +export { manipulateIdentity }; diff --git a/bindings/wasm/examples-account/src/multiple_identities.ts b/bindings/wasm/examples-account/src/multiple_identities.ts index fc8fe2a76d..978a0c6104 100644 --- a/bindings/wasm/examples-account/src/multiple_identities.ts +++ b/bindings/wasm/examples-account/src/multiple_identities.ts @@ -1,14 +1,13 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import {AccountBuilder, ExplorerUrl, MethodContent, Storage} from './../../node/identity_wasm.js'; +import { AccountBuilder, ExplorerUrl, MethodContent, Storage } from "./../../node/identity_wasm.js"; /** * This example demonstrates how to create multiple identities from a builder * and how to load existing identities into an account. */ async function multipleIdentities(storage?: Storage) { - // Create an AccountBuilder to make it easier to create multiple identities. // Every account created from the builder will use the same storage. let builder = new AccountBuilder({ @@ -36,12 +35,12 @@ async function multipleIdentities(storage?: Storage) { // We can even do so concurrently. const account1Promise = account1Reconstructed.createMethod({ content: MethodContent.GenerateEd25519(), - fragment: "my_key" - }) + fragment: "my_key", + }); const account2Promise = account2.createMethod({ content: MethodContent.GenerateX25519(), - fragment: "my_other_key" - }) + fragment: "my_other_key", + }); await Promise.all([account1Promise, account2Promise]); diff --git a/bindings/wasm/examples-account/src/node.ts b/bindings/wasm/examples-account/src/node.ts index 5b893b78c5..34c2c10ef0 100644 --- a/bindings/wasm/examples-account/src/node.ts +++ b/bindings/wasm/examples-account/src/node.ts @@ -5,22 +5,22 @@ import { config } from "./config"; import { createIdentity } from "./create_did"; import { createVC } from "./create_vc"; import { createVP } from "./create_vp"; +import { storageTestSuite } from "./custom_storage"; +import { encryption } from "./encryption"; import { lazy } from "./lazy"; import { manipulateIdentity } from "./manipulate_did"; import { multipleIdentities } from "./multiple_identities"; import { revokeVC } from "./revoke_vc"; import { signing } from "./signing"; -import { storageTestSuite } from "./custom_storage"; import { unchecked } from "./unchecked"; -import { encryption } from "./encryption"; async function main() { - //Check if an example is mentioned + // Check if an example is mentioned if (process.argv.length != 3) { throw "Please provide one command line argument with the example name."; } - //Take out the argument + // Take out the argument let argument = process.argv[2]; switch (argument) { case "create_did": @@ -46,7 +46,7 @@ async function main() { case "revoke_vc": return await revokeVC(); case "custom_storage": - return await storageTestSuite() + return await storageTestSuite(); default: throw "Unknown example name"; } diff --git a/bindings/wasm/examples-account/src/revoke_vc.ts b/bindings/wasm/examples-account/src/revoke_vc.ts index 52454b803f..b3cc563b1f 100644 --- a/bindings/wasm/examples-account/src/revoke_vc.ts +++ b/bindings/wasm/examples-account/src/revoke_vc.ts @@ -7,11 +7,11 @@ import { CredentialValidationOptions, CredentialValidator, FailFast, - Resolver, - Storage, MethodContent, ProofOptions, - RevocationBitmap + Resolver, + RevocationBitmap, + Storage, } from "./../../node/identity_wasm.js"; /** @@ -30,7 +30,7 @@ async function revokeVC(storage?: Storage) { // =========================================================================== const builder = new AccountBuilder({ - storage + storage, }); // Create an identity for the issuer. @@ -39,7 +39,7 @@ async function revokeVC(storage?: Storage) { // Add a dedicated verification method to the issuer, with which to sign credentials. await issuer.createMethod({ content: MethodContent.GenerateEd25519(), - fragment: "key-1" + fragment: "key-1", }); // Add a RevocationBitmap service to the issuer's DID Document. @@ -48,7 +48,7 @@ async function revokeVC(storage?: Storage) { await issuer.createService({ fragment: "my-revocation-service", type: RevocationBitmap.type(), - endpoint: revocationBitmap.toEndpoint() + endpoint: revocationBitmap.toEndpoint(), }); // Create a credential subject indicating the degree earned by Alice, linked to their DID. @@ -56,7 +56,7 @@ async function revokeVC(storage?: Storage) { id: "did:iota:B8DucnzULJ9E8cmaReYoePU2b7UKE9WKxyEVov8tQA7H", name: "Alice", degree: "Bachelor of Science and Arts", - GPA: "4.0" + GPA: "4.0", }; // Create an unsigned `UniversityDegree` credential for Alice. @@ -67,17 +67,17 @@ async function revokeVC(storage?: Storage) { credentialStatus: { id: issuer.did() + "#my-revocation-service", type: RevocationBitmap.type(), - revocationBitmapIndex: "5" + revocationBitmapIndex: "5", }, issuer: issuer.document().id(), - credentialSubject: subject + credentialSubject: subject, }); // Created a signed credential by the issuer. const signedVc = await issuer.createSignedCredential( "#key-1", unsignedVc, - ProofOptions.default() + ProofOptions.default(), ); // =========================================================================== @@ -108,7 +108,7 @@ async function revokeVC(storage?: Storage) { // By removing the verification method, that signed the credential, from the issuer's DID document, // we effectively revoke the credential, as it will no longer be possible to validate the signature. await issuer.deleteMethod({ - fragment: "key-1" + fragment: "key-1", }); // We expect the verifiable credential to be revoked. @@ -116,7 +116,7 @@ async function revokeVC(storage?: Storage) { try { // Resolve the issuer's updated DID Document to ensure the key was revoked successfully. const resolvedIssuerDoc = await resolver.resolveCredentialIssuer( - signedVc + signedVc, ); // TODO: uncomment when ported to Stardust. // CredentialValidator.validate( diff --git a/bindings/wasm/examples-account/src/signing.ts b/bindings/wasm/examples-account/src/signing.ts index ebf16c1c11..f3bd6d3121 100644 --- a/bindings/wasm/examples-account/src/signing.ts +++ b/bindings/wasm/examples-account/src/signing.ts @@ -11,14 +11,13 @@ import { MethodContent, ProofOptions, Storage, - VerifierOptions -} from './../../node/identity_wasm.js'; + VerifierOptions, +} from "./../../node/identity_wasm.js"; /** * This example demonstrates how to issue and sign Verifiable Credentials using the account. */ async function signing(storage?: Storage) { - // =========================================================================== // Create Identity - Similar to create_did example // =========================================================================== @@ -37,8 +36,8 @@ async function signing(storage?: Storage) { // Add a new Ed25519 Verification Method to the identity for signing issued verifiable credentials. await account.createMethod({ content: MethodContent.GenerateEd25519(), - fragment: "key_1" - }) + fragment: "key_1", + }); // Prepare a credential subject indicating the degree earned by Alice, linked to their DID. const subject = { @@ -46,8 +45,8 @@ async function signing(storage?: Storage) { name: "Alice", degree: { type: "BachelorDegree", - name: "Bachelor of Science and Arts" - } + name: "Bachelor of Science and Arts", + }, }; // Issue an unsigned Credential... @@ -81,4 +80,4 @@ async function signing(storage?: Storage) { console.log("[Example] Credential Verified = ", verified); } -export {signing}; +export { signing }; diff --git a/bindings/wasm/examples-account/src/tests/config.ts b/bindings/wasm/examples-account/src/tests/config.ts index 459ebe6423..67243aa1b4 100644 --- a/bindings/wasm/examples-account/src/tests/config.ts +++ b/bindings/wasm/examples-account/src/tests/config.ts @@ -1,8 +1,8 @@ -import {config} from "../config"; +import { config } from "../config"; // Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test node examples", function () { +describe("Test node examples", function() { it("Config", async () => { await config(); }); -}) +}); diff --git a/bindings/wasm/examples-account/src/tests/create_did.ts b/bindings/wasm/examples-account/src/tests/create_did.ts index d4e483bf43..01faea13c4 100644 --- a/bindings/wasm/examples-account/src/tests/create_did.ts +++ b/bindings/wasm/examples-account/src/tests/create_did.ts @@ -1,8 +1,8 @@ -import {createIdentity} from "../create_did"; +import { createIdentity } from "../create_did"; // Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test node examples", function () { +describe("Test node examples", function() { it("Create Identity", async () => { await createIdentity(); }); -}) +}); diff --git a/bindings/wasm/examples-account/src/tests/create_vc.ts b/bindings/wasm/examples-account/src/tests/create_vc.ts index b98fdcbccf..d95afc5611 100644 --- a/bindings/wasm/examples-account/src/tests/create_vc.ts +++ b/bindings/wasm/examples-account/src/tests/create_vc.ts @@ -1,9 +1,8 @@ -import {createVC} from "../create_vc"; +import { createVC } from "../create_vc"; // Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test node examples", function () { +describe("Test node examples", function() { it("Create verifiable credential", async () => { await createVC(); }); -}) - +}); diff --git a/bindings/wasm/examples-account/src/tests/create_vp.ts b/bindings/wasm/examples-account/src/tests/create_vp.ts index 6cf3c73459..3db9452285 100644 --- a/bindings/wasm/examples-account/src/tests/create_vp.ts +++ b/bindings/wasm/examples-account/src/tests/create_vp.ts @@ -1,9 +1,8 @@ import { createVP } from "../create_vp"; // Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test node examples", function () { +describe("Test node examples", function() { it("Create verifiable presentation", async () => { await createVP(); }); -}) - +}); diff --git a/bindings/wasm/examples-account/src/tests/encryption.ts b/bindings/wasm/examples-account/src/tests/encryption.ts index b24b1ed05b..fd940db16f 100644 --- a/bindings/wasm/examples-account/src/tests/encryption.ts +++ b/bindings/wasm/examples-account/src/tests/encryption.ts @@ -1,8 +1,8 @@ -import {encryption} from "../encryption"; +import { encryption } from "../encryption"; // Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test node examples", function () { +describe("Test node examples", function() { it("encryption", async () => { await encryption(); }); -}) +}); diff --git a/bindings/wasm/examples-account/src/tests/lazy.ts b/bindings/wasm/examples-account/src/tests/lazy.ts index 1235dd0b91..9f46c180df 100644 --- a/bindings/wasm/examples-account/src/tests/lazy.ts +++ b/bindings/wasm/examples-account/src/tests/lazy.ts @@ -1,8 +1,8 @@ -import {lazy} from "../lazy"; +import { lazy } from "../lazy"; // Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test node examples", function () { +describe("Test node examples", function() { it("Lazy", async () => { await lazy(); }); -}) +}); diff --git a/bindings/wasm/examples-account/src/tests/manipulate_did.ts b/bindings/wasm/examples-account/src/tests/manipulate_did.ts index c1a99d413b..2b56079d12 100644 --- a/bindings/wasm/examples-account/src/tests/manipulate_did.ts +++ b/bindings/wasm/examples-account/src/tests/manipulate_did.ts @@ -1,8 +1,8 @@ -import {manipulateIdentity} from "../manipulate_did"; +import { manipulateIdentity } from "../manipulate_did"; // Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test node examples", function () { +describe("Test node examples", function() { it("Manipulate DID", async () => { await manipulateIdentity(); }); -}) +}); diff --git a/bindings/wasm/examples-account/src/tests/multiple_identities.ts b/bindings/wasm/examples-account/src/tests/multiple_identities.ts index 007bd75717..ce835591f1 100644 --- a/bindings/wasm/examples-account/src/tests/multiple_identities.ts +++ b/bindings/wasm/examples-account/src/tests/multiple_identities.ts @@ -1,8 +1,8 @@ -import {multipleIdentities} from "../multiple_identities"; +import { multipleIdentities } from "../multiple_identities"; // Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test node examples", function () { +describe("Test node examples", function() { it("Multiple Identities", async () => { await multipleIdentities(); }); -}) +}); diff --git a/bindings/wasm/examples-account/src/tests/revoke_vc.ts b/bindings/wasm/examples-account/src/tests/revoke_vc.ts index 330c349224..63e1394849 100644 --- a/bindings/wasm/examples-account/src/tests/revoke_vc.ts +++ b/bindings/wasm/examples-account/src/tests/revoke_vc.ts @@ -1,9 +1,8 @@ import { revokeVC } from "../revoke_vc"; // Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test node examples", function () { +describe("Test node examples", function() { it("Revoke verifiable credential", async () => { await revokeVC(); }); -}) - +}); diff --git a/bindings/wasm/examples-account/src/tests/signing.ts b/bindings/wasm/examples-account/src/tests/signing.ts index 47a6a1eccf..f62e1169be 100644 --- a/bindings/wasm/examples-account/src/tests/signing.ts +++ b/bindings/wasm/examples-account/src/tests/signing.ts @@ -1,8 +1,8 @@ -import {signing} from "../signing"; +import { signing } from "../signing"; // Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test node examples", function () { +describe("Test node examples", function() { it("Signing", async () => { await signing(); }); -}) +}); diff --git a/bindings/wasm/examples-account/src/tests/unchecked.ts b/bindings/wasm/examples-account/src/tests/unchecked.ts index a046c3e540..686e5b9c95 100644 --- a/bindings/wasm/examples-account/src/tests/unchecked.ts +++ b/bindings/wasm/examples-account/src/tests/unchecked.ts @@ -1,8 +1,8 @@ -import {unchecked} from "../unchecked"; +import { unchecked } from "../unchecked"; // Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test node examples", function () { +describe("Test node examples", function() { it("Unchecked", async () => { await unchecked(); }); -}) +}); diff --git a/bindings/wasm/examples-account/src/unchecked.ts b/bindings/wasm/examples-account/src/unchecked.ts index d1ede7f5ec..4ae5010836 100644 --- a/bindings/wasm/examples-account/src/unchecked.ts +++ b/bindings/wasm/examples-account/src/unchecked.ts @@ -1,14 +1,13 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { AccountBuilder, Timestamp, ExplorerUrl, Storage } from './../../node/identity_wasm.js'; +import { AccountBuilder, ExplorerUrl, Storage, Timestamp } from "./../../node/identity_wasm.js"; /** * This example demonstrates how to update the custom properties of a DID document directly * and publish it without performing validation. */ async function unchecked(storage?: Storage) { - // The creation step generates a keypair, builds an identity // and publishes it to the IOTA mainnet. let builder = new AccountBuilder({ @@ -41,7 +40,6 @@ async function unchecked(storage?: Storage) { // Print the Explorer URL for the DID. console.log(`Explorer Url:`, ExplorerUrl.mainnet().resolverUrl(account.did())); - } -export { unchecked } +export { unchecked }; diff --git a/bindings/wasm/examples/src/0_basic/0_create_did.ts b/bindings/wasm/examples/src/0_basic/0_create_did.ts index 32b222d3f8..f4d07335e0 100644 --- a/bindings/wasm/examples/src/0_basic/0_create_did.ts +++ b/bindings/wasm/examples/src/0_basic/0_create_did.ts @@ -1,26 +1,26 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +import { Bip39 } from "@iota/crypto.js"; +import { Client, MnemonicSecretManager, SecretManager } from "@iota/iota-client-wasm/node"; +import { Bech32Helper, IAliasOutput } from "@iota/iota.js"; import { - KeyPair, - KeyType, - MethodScope, IotaDID, IotaDocument, IotaIdentityClient, - IotaVerificationMethod -} from '../../../node'; -import { Bech32Helper, IAliasOutput } from '@iota/iota.js'; -import { Bip39 } from "@iota/crypto.js"; -import { Client, MnemonicSecretManager, SecretManager } from "@iota/iota-client-wasm/node"; -import { API_ENDPOINT, ensureAddressHasFunds } from '../util'; + IotaVerificationMethod, + KeyPair, + KeyType, + MethodScope, +} from "../../../node"; +import { API_ENDPOINT, ensureAddressHasFunds } from "../util"; /** Demonstrate how to create a DID Document and publish it in a new Alias Output. */ export async function createIdentity(): Promise<{ - didClient: IotaIdentityClient, - secretManager: SecretManager, - walletAddressBech32: string, - did: IotaDID + didClient: IotaIdentityClient; + secretManager: SecretManager; + walletAddressBech32: string; + did: IotaDID; }> { const client = new Client({ primaryNode: API_ENDPOINT, @@ -33,7 +33,7 @@ export async function createIdentity(): Promise<{ // Generate a random mnemonic for our wallet. const secretManager: MnemonicSecretManager = { - Mnemonic: Bip39.randomMnemonic() + Mnemonic: Bip39.randomMnemonic(), }; const walletAddressBech32 = (await client.generateAddresses(secretManager, { accountIndex: 0, @@ -67,8 +67,9 @@ export async function createIdentity(): Promise<{ console.log("Published DID document:", JSON.stringify(published, null, 2)); return { - didClient, secretManager, + didClient, + secretManager, walletAddressBech32, - did: published.id() + did: published.id(), }; } diff --git a/bindings/wasm/examples/src/0_basic/1_update_did.ts b/bindings/wasm/examples/src/0_basic/1_update_did.ts index bd260b52b3..5cccf0d226 100644 --- a/bindings/wasm/examples/src/0_basic/1_update_did.ts +++ b/bindings/wasm/examples/src/0_basic/1_update_did.ts @@ -1,11 +1,21 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { MethodRelationship, IotaDocument, IotaService, Timestamp, IotaVerificationMethod, KeyPair, KeyType, MethodScope, IotaIdentityClient } from '../../../node'; -import { IAliasOutput, IRent, TransactionHelper } from '@iota/iota.js'; -import { API_ENDPOINT, createDid } from '../util'; -import { Client, MnemonicSecretManager } from '@iota/iota-client-wasm/node'; -import { Bip39 } from '@iota/crypto.js'; +import { Bip39 } from "@iota/crypto.js"; +import { Client, MnemonicSecretManager } from "@iota/iota-client-wasm/node"; +import { IAliasOutput, IRent, TransactionHelper } from "@iota/iota.js"; +import { + IotaDocument, + IotaIdentityClient, + IotaService, + IotaVerificationMethod, + KeyPair, + KeyType, + MethodRelationship, + MethodScope, + Timestamp, +} from "../../../node"; +import { API_ENDPOINT, createDid } from "../util"; /** Demonstrates how to update a DID document in an existing Alias Output. */ export async function updateIdentity() { @@ -17,7 +27,7 @@ export async function updateIdentity() { // Generate a random mnemonic for our wallet. const secretManager: MnemonicSecretManager = { - Mnemonic: Bip39.randomMnemonic() + Mnemonic: Bip39.randomMnemonic(), }; // Creates a new wallet and identity (see "0_create_did" example). @@ -39,7 +49,7 @@ export async function updateIdentity() { const service: IotaService = new IotaService({ id: did.join("#linked-domain"), type: "LinkedDomains", - serviceEndpoint: "https://iota.org/" + serviceEndpoint: "https://iota.org/", }); document.insertService(service); document.setMetadataUpdated(Timestamp.nowUTC()); diff --git a/bindings/wasm/examples/src/0_basic/2_resolve_did.ts b/bindings/wasm/examples/src/0_basic/2_resolve_did.ts index fa8330c244..b33125555a 100644 --- a/bindings/wasm/examples/src/0_basic/2_resolve_did.ts +++ b/bindings/wasm/examples/src/0_basic/2_resolve_did.ts @@ -1,11 +1,11 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { IotaDocument, IotaIdentityClient } from '../../../node'; -import type { IAliasOutput } from '@iota/iota.js'; -import { API_ENDPOINT, createDid } from '../util'; -import { Bip39 } from '@iota/crypto.js'; -import { Client, MnemonicSecretManager } from '@iota/iota-client-wasm/node'; +import { Bip39 } from "@iota/crypto.js"; +import { Client, MnemonicSecretManager } from "@iota/iota-client-wasm/node"; +import type { IAliasOutput } from "@iota/iota.js"; +import { IotaDocument, IotaIdentityClient } from "../../../node"; +import { API_ENDPOINT, createDid } from "../util"; /** Demonstrates how to resolve an existing DID in an Alias Output. */ export async function resolveIdentity() { @@ -17,7 +17,7 @@ export async function resolveIdentity() { // Generate a random mnemonic for our wallet. const secretManager: MnemonicSecretManager = { - Mnemonic: Bip39.randomMnemonic() + Mnemonic: Bip39.randomMnemonic(), }; // Creates a new wallet and identity (see "0_create_did" example). diff --git a/bindings/wasm/examples/src/0_basic/3_deactivate_did.ts b/bindings/wasm/examples/src/0_basic/3_deactivate_did.ts index 389ac14017..c688fe28c9 100644 --- a/bindings/wasm/examples/src/0_basic/3_deactivate_did.ts +++ b/bindings/wasm/examples/src/0_basic/3_deactivate_did.ts @@ -1,11 +1,11 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { IotaDocument, IotaIdentityClient } from '../../../node'; -import { IAliasOutput, IRent, TransactionHelper } from '@iota/iota.js'; -import { API_ENDPOINT, createDid } from '../util'; -import { Bip39 } from '@iota/crypto.js'; -import { Client, MnemonicSecretManager } from '@iota/iota-client-wasm/node'; +import { Bip39 } from "@iota/crypto.js"; +import { Client, MnemonicSecretManager } from "@iota/iota-client-wasm/node"; +import { IAliasOutput, IRent, TransactionHelper } from "@iota/iota.js"; +import { IotaDocument, IotaIdentityClient } from "../../../node"; +import { API_ENDPOINT, createDid } from "../util"; /** Demonstrates how to deactivate a DID in an Alias Output. */ export async function deactivateIdentity() { @@ -17,7 +17,7 @@ export async function deactivateIdentity() { // Generate a random mnemonic for our wallet. const secretManager: MnemonicSecretManager = { - Mnemonic: Bip39.randomMnemonic() + Mnemonic: Bip39.randomMnemonic(), }; // Creates a new wallet and identity (see "0_create_did" example). diff --git a/bindings/wasm/examples/src/0_basic/4_delete_did.ts b/bindings/wasm/examples/src/0_basic/4_delete_did.ts index 0117744efb..ab9c8a2289 100644 --- a/bindings/wasm/examples/src/0_basic/4_delete_did.ts +++ b/bindings/wasm/examples/src/0_basic/4_delete_did.ts @@ -1,10 +1,10 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +import { Bip39 } from "@iota/crypto.js"; import { Client, MnemonicSecretManager } from "@iota/iota-client-wasm/node"; import { IotaIdentityClient } from "../../../node"; import { API_ENDPOINT, createDid } from "../util"; -import { Bip39 } from "@iota/crypto.js"; /** Demonstrates how to delete a DID in an Alias Output, reclaiming the storage deposit. */ export async function deleteIdentity() { @@ -16,7 +16,7 @@ export async function deleteIdentity() { // Generate a random mnemonic for our wallet. const secretManager: MnemonicSecretManager = { - Mnemonic: Bip39.randomMnemonic() + Mnemonic: Bip39.randomMnemonic(), }; // Creates a new wallet and identity (see "0_create_did" example). diff --git a/bindings/wasm/examples/src/1_advanced/0_did_controls_did.ts b/bindings/wasm/examples/src/1_advanced/0_did_controls_did.ts index aecee50da7..efb0831eab 100644 --- a/bindings/wasm/examples/src/1_advanced/0_did_controls_did.ts +++ b/bindings/wasm/examples/src/1_advanced/0_did_controls_did.ts @@ -1,130 +1,151 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { IotaDocument, IotaIdentityClient, MethodScope, KeyPair, KeyType, IotaVerificationMethod, IotaDID } from '../../../node'; -import { AddressTypes, IAliasOutput, IRent, TransactionHelper, ALIAS_ADDRESS_TYPE, IAliasAddress, ISSUER_FEATURE_TYPE, IStateControllerAddressUnlockCondition, STATE_CONTROLLER_ADDRESS_UNLOCK_CONDITION_TYPE } from '@iota/iota.js'; -import { API_ENDPOINT, createDid } from '../util'; -import { Client, MnemonicSecretManager } from '@iota/iota-client-wasm/node'; -import { Bip39 } from '@iota/crypto.js'; -import { Converter } from '@iota/util.js'; +import { Bip39 } from "@iota/crypto.js"; +import { Client, MnemonicSecretManager } from "@iota/iota-client-wasm/node"; +import { + AddressTypes, + ALIAS_ADDRESS_TYPE, + IAliasAddress, + IAliasOutput, + IRent, + ISSUER_FEATURE_TYPE, + IStateControllerAddressUnlockCondition, + STATE_CONTROLLER_ADDRESS_UNLOCK_CONDITION_TYPE, + TransactionHelper, +} from "@iota/iota.js"; +import { Converter } from "@iota/util.js"; +import { + IotaDID, + IotaDocument, + IotaIdentityClient, + IotaVerificationMethod, + KeyPair, + KeyType, + MethodScope, +} from "../../../node"; +import { API_ENDPOINT, createDid } from "../util"; /** Demonstrates how an identity can control another identity. For this example, we consider the case where a parent company's DID controls the DID of a subsidiary. */ export async function didControlsDid() { - // ======================================================== - // Create the company's and subsidiary's Alias Output DIDs. - // ======================================================== - - // Create a new client to interact with the IOTA ledger. - const client = new Client({ - primaryNode: API_ENDPOINT, - localPow: true, - }); - const didClient = new IotaIdentityClient(client); - - // Generate a random mnemonic for our wallet. - const secretManager: MnemonicSecretManager = { - Mnemonic: Bip39.randomMnemonic() - }; - - // Creates a new wallet and identity (see "0_create_did" example). - var { did: companyDid } = await createDid(client, secretManager); - - // Get the current byte costs. - const rentStructure: IRent = await didClient.getRentStructure(); - - // Get the Bech32 human-readable part (HRP) of the network. - const networkName: string = await didClient.getNetworkHrp(); - - // Construct a new DID document for the subsidiary. - var subsidiaryDocument: IotaDocument = new IotaDocument(networkName); - - // Create the Alias Address of the company. - const companyAliasAddress: AddressTypes = { - aliasId: companyDid.toAliasId(), - type: ALIAS_ADDRESS_TYPE - }; - - // Create a DID for the subsidiary that is controlled by the parent company's DID. - // This means the subsidiary's Alias Output can only be updated or destroyed by - // the state controller or governor of the company's Alias Output respectively. - var subsidiaryAlias: IAliasOutput = await didClient.newDidOutput( - companyAliasAddress, - subsidiaryDocument, - rentStructure); - - // Optionally, we can mark the company as the issuer of the subsidiary DID. - // This allows to verify trust relationships between DIDs, as a resolver can - // verify that the subsidiary DID was created by the parent company. - subsidiaryAlias = { - ...subsidiaryAlias, - immutableFeatures: [ - { - type: ISSUER_FEATURE_TYPE, - address: companyAliasAddress, - } - ] - } - - // Adding the issuer feature means we have to recalculate the required storage deposit. - subsidiaryAlias.amount = TransactionHelper.getStorageDeposit(subsidiaryAlias, rentStructure).toString(); - - // Publish the subsidiary's DID. - subsidiaryDocument = await didClient.publishDidOutput(secretManager, subsidiaryAlias); - - // ===================================== - // Update the subsidiary's Alias Output. - // ===================================== - - // Add a verification method to the subsidiary. - // This only serves as an example for updating the subsidiary DID. - const keyPair: KeyPair = new KeyPair(KeyType.Ed25519); - const method: IotaVerificationMethod = new IotaVerificationMethod( - subsidiaryDocument.id(), - KeyType.Ed25519, - keyPair.public(), - "#key-2" - ); - subsidiaryDocument.insertMethod(method, MethodScope.VerificationMethod()); - - // Update the subsidiary's Alias Output with the updated document - // and increase the storage deposit. - const subsidiaryAliasUpdate: IAliasOutput = await didClient.updateDidOutput(subsidiaryDocument); - subsidiaryAliasUpdate.amount = TransactionHelper.getStorageDeposit(subsidiaryAliasUpdate, rentStructure).toString(); - - // Publish the updated subsidiary's DID. - // - // This works because `secret_manager` can unlock the company's Alias Output, - // which is required in order to update the subsidiary's Alias Output. - subsidiaryDocument = await didClient.publishDidOutput(secretManager, subsidiaryAliasUpdate); - - // =================================================================== - // Determine the controlling company's DID given the subsidiary's DID. - // =================================================================== - - // Resolve the subsidiary's Alias Output. - const subsidiaryOutput: IAliasOutput = await didClient.resolveDidOutput(subsidiaryDocument.id()); - - // Extract the company's Alias Id from the state controller unlock condition. - // - // If instead we wanted to determine the original creator of the DID, - // we could inspect the issuer feature. This feature needs to be set when creating the DID. - // - // Non-null assertion is safe to use since every Alias Output has a state controller unlock condition. - // Cast to IStateControllerAddressUnlockCondition is safe as we check the type in find. - const stateControllerUnlockCondition: IStateControllerAddressUnlockCondition = - subsidiaryOutput.unlockConditions.find(unlockCondition => unlockCondition.type == STATE_CONTROLLER_ADDRESS_UNLOCK_CONDITION_TYPE)! as IStateControllerAddressUnlockCondition - - // Cast to IAliasAddress is safe because we set an Alias Address earlier. - const companyAliasId: string = (stateControllerUnlockCondition.address as IAliasAddress).aliasId; - - // Reconstruct the company's DID from the Alias Id and the network. - companyDid = new IotaDID(Converter.hexToBytes(companyAliasId), networkName); - - // Resolve the company's DID document. - const companyDocument: IotaDocument = await didClient.resolveDid(companyDid); - - console.log("Company ", JSON.stringify(companyDocument, null, 2)); - console.log("Subsidiary ", JSON.stringify(subsidiaryDocument, null, 2)); + // ======================================================== + // Create the company's and subsidiary's Alias Output DIDs. + // ======================================================== + + // Create a new client to interact with the IOTA ledger. + const client = new Client({ + primaryNode: API_ENDPOINT, + localPow: true, + }); + const didClient = new IotaIdentityClient(client); + + // Generate a random mnemonic for our wallet. + const secretManager: MnemonicSecretManager = { + Mnemonic: Bip39.randomMnemonic(), + }; + + // Creates a new wallet and identity (see "0_create_did" example). + var { did: companyDid } = await createDid(client, secretManager); + + // Get the current byte costs. + const rentStructure: IRent = await didClient.getRentStructure(); + + // Get the Bech32 human-readable part (HRP) of the network. + const networkName: string = await didClient.getNetworkHrp(); + + // Construct a new DID document for the subsidiary. + var subsidiaryDocument: IotaDocument = new IotaDocument(networkName); + + // Create the Alias Address of the company. + const companyAliasAddress: AddressTypes = { + aliasId: companyDid.toAliasId(), + type: ALIAS_ADDRESS_TYPE, + }; + + // Create a DID for the subsidiary that is controlled by the parent company's DID. + // This means the subsidiary's Alias Output can only be updated or destroyed by + // the state controller or governor of the company's Alias Output respectively. + var subsidiaryAlias: IAliasOutput = await didClient.newDidOutput( + companyAliasAddress, + subsidiaryDocument, + rentStructure, + ); + + // Optionally, we can mark the company as the issuer of the subsidiary DID. + // This allows to verify trust relationships between DIDs, as a resolver can + // verify that the subsidiary DID was created by the parent company. + subsidiaryAlias = { + ...subsidiaryAlias, + immutableFeatures: [ + { + type: ISSUER_FEATURE_TYPE, + address: companyAliasAddress, + }, + ], + }; + + // Adding the issuer feature means we have to recalculate the required storage deposit. + subsidiaryAlias.amount = TransactionHelper.getStorageDeposit(subsidiaryAlias, rentStructure).toString(); + + // Publish the subsidiary's DID. + subsidiaryDocument = await didClient.publishDidOutput(secretManager, subsidiaryAlias); + + // ===================================== + // Update the subsidiary's Alias Output. + // ===================================== + + // Add a verification method to the subsidiary. + // This only serves as an example for updating the subsidiary DID. + const keyPair: KeyPair = new KeyPair(KeyType.Ed25519); + const method: IotaVerificationMethod = new IotaVerificationMethod( + subsidiaryDocument.id(), + KeyType.Ed25519, + keyPair.public(), + "#key-2", + ); + subsidiaryDocument.insertMethod(method, MethodScope.VerificationMethod()); + + // Update the subsidiary's Alias Output with the updated document + // and increase the storage deposit. + const subsidiaryAliasUpdate: IAliasOutput = await didClient.updateDidOutput(subsidiaryDocument); + subsidiaryAliasUpdate.amount = TransactionHelper.getStorageDeposit(subsidiaryAliasUpdate, rentStructure).toString(); + + // Publish the updated subsidiary's DID. + // + // This works because `secret_manager` can unlock the company's Alias Output, + // which is required in order to update the subsidiary's Alias Output. + subsidiaryDocument = await didClient.publishDidOutput(secretManager, subsidiaryAliasUpdate); + + // =================================================================== + // Determine the controlling company's DID given the subsidiary's DID. + // =================================================================== + + // Resolve the subsidiary's Alias Output. + const subsidiaryOutput: IAliasOutput = await didClient.resolveDidOutput(subsidiaryDocument.id()); + + // Extract the company's Alias Id from the state controller unlock condition. + // + // If instead we wanted to determine the original creator of the DID, + // we could inspect the issuer feature. This feature needs to be set when creating the DID. + // + // Non-null assertion is safe to use since every Alias Output has a state controller unlock condition. + // Cast to IStateControllerAddressUnlockCondition is safe as we check the type in find. + const stateControllerUnlockCondition: IStateControllerAddressUnlockCondition = subsidiaryOutput.unlockConditions + .find( + unlockCondition => unlockCondition.type == STATE_CONTROLLER_ADDRESS_UNLOCK_CONDITION_TYPE, + )! as IStateControllerAddressUnlockCondition; + + // Cast to IAliasAddress is safe because we set an Alias Address earlier. + const companyAliasId: string = (stateControllerUnlockCondition.address as IAliasAddress).aliasId; + + // Reconstruct the company's DID from the Alias Id and the network. + companyDid = new IotaDID(Converter.hexToBytes(companyAliasId), networkName); + + // Resolve the company's DID document. + const companyDocument: IotaDocument = await didClient.resolveDid(companyDid); + + console.log("Company ", JSON.stringify(companyDocument, null, 2)); + console.log("Subsidiary ", JSON.stringify(subsidiaryDocument, null, 2)); } diff --git a/bindings/wasm/examples/src/1_advanced/1_did_issues_nft.ts b/bindings/wasm/examples/src/1_advanced/1_did_issues_nft.ts index 3f6cac4c7b..d837a18290 100644 --- a/bindings/wasm/examples/src/1_advanced/1_did_issues_nft.ts +++ b/bindings/wasm/examples/src/1_advanced/1_did_issues_nft.ts @@ -1,12 +1,29 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { IotaDocument, IotaIdentityClient, IotaDID } from '../../../node'; -import { AddressTypes, IRent, TransactionHelper, ALIAS_ADDRESS_TYPE, ISSUER_FEATURE_TYPE, NFT_OUTPUT_TYPE, METADATA_FEATURE_TYPE, PayloadTypes, TRANSACTION_PAYLOAD_TYPE, ITransactionPayload, TRANSACTION_ESSENCE_TYPE, INftOutput, ADDRESS_UNLOCK_CONDITION_TYPE, IIssuerFeature, IOutputResponse, OutputTypes } from '@iota/iota.js'; -import { API_ENDPOINT, createDid } from '../util'; -import { Client, MnemonicSecretManager } from '@iota/iota-client-wasm/node'; -import { Bip39 } from '@iota/crypto.js'; -import { Converter } from '@iota/util.js'; +import { Bip39 } from "@iota/crypto.js"; +import { Client, MnemonicSecretManager } from "@iota/iota-client-wasm/node"; +import { + ADDRESS_UNLOCK_CONDITION_TYPE, + AddressTypes, + ALIAS_ADDRESS_TYPE, + IIssuerFeature, + INftOutput, + IOutputResponse, + IRent, + ISSUER_FEATURE_TYPE, + ITransactionPayload, + METADATA_FEATURE_TYPE, + NFT_OUTPUT_TYPE, + OutputTypes, + PayloadTypes, + TRANSACTION_ESSENCE_TYPE, + TRANSACTION_PAYLOAD_TYPE, + TransactionHelper, +} from "@iota/iota.js"; +import { Converter } from "@iota/util.js"; +import { IotaDID, IotaDocument, IotaIdentityClient } from "../../../node"; +import { API_ENDPOINT, createDid } from "../util"; /** Demonstrates how an identity can issue and own NFTs, and how observers can verify the issuer of the NFT. @@ -14,122 +31,124 @@ and how observers can verify the issuer of the NFT. For this example, we consider the case where a manufacturer issues a digital product passport (DPP) as an NFT. */ export async function didIssuesNft() { - // ============================================== - // Create the manufacturer's DID and the DPP NFT. - // ============================================== - - // Create a new client to interact with the IOTA ledger. - const client = new Client({ - primaryNode: API_ENDPOINT, - localPow: true, - }); - const didClient = new IotaIdentityClient(client); - - // Generate a random mnemonic for our wallet. - const secretManager: MnemonicSecretManager = { - Mnemonic: Bip39.randomMnemonic() - }; - - // Create a new DID for the manufacturer. (see "0_create_did" example). - var { did: manufacturerDid } = await createDid(client, secretManager); - - // Get the current byte costs. - const rentStructure: IRent = await didClient.getRentStructure(); - - // Get the Bech32 human-readable part (HRP) of the network. - const networkName: string = await didClient.getNetworkHrp(); - - // Create the Alias Address of the manufacturer. - var manufacturerAliasAddress: AddressTypes = { - type: ALIAS_ADDRESS_TYPE, - aliasId: manufacturerDid.toAliasId() - } - - // Create a Digital Product Passport NFT issued by the manufacturer. - let productPassportNft: INftOutput = await client.buildNftOutput({ - nftId: "0x0000000000000000000000000000000000000000000000000000000000000000", - immutableFeatures: [ - { - // Set the manufacturer as the immutable issuer. - type: ISSUER_FEATURE_TYPE, - address: manufacturerAliasAddress, - }, - { - // A proper DPP would hold its metadata here. - type: METADATA_FEATURE_TYPE, - data: Converter.utf8ToHex("Digital Product Passport Metadata", true) - } - ], - unlockConditions: [ - { - // The NFT will initially be owned by the manufacturer. - type: ADDRESS_UNLOCK_CONDITION_TYPE, - address: manufacturerAliasAddress - } - ] - }); - - // Set the appropriate storage deposit. - productPassportNft.amount = TransactionHelper.getStorageDeposit(productPassportNft, rentStructure).toString(); - - // Publish the NFT. - const [blockId, block] = await client.buildAndPostBlock(secretManager, { outputs: [productPassportNft] }); - await client.retryUntilIncluded(blockId); - - // ======================================================== - // Resolve the Digital Product Passport NFT and its issuer. - // ======================================================== - - // Extract the identifier of the NFT from the published block. - // Non-null assertion is safe because we published a block with a payload. - let nftId: string = nft_output_id(block.payload!); - - // Fetch the NFT Output. - const nftOutputId: string = await client.nftOutputId(nftId); - const outputResponse: IOutputResponse = await client.getOutput(nftOutputId); - const output: OutputTypes = outputResponse.output - - // Extract the issuer of the NFT. - let manufacturerAliasId: string; - if (output.type === NFT_OUTPUT_TYPE && output.immutableFeatures) { - const issuerFeature: IIssuerFeature = output.immutableFeatures.find(feature => feature.type === ISSUER_FEATURE_TYPE) as IIssuerFeature; - - if (issuerFeature && issuerFeature.address.type === ALIAS_ADDRESS_TYPE) { - manufacturerAliasId = issuerFeature.address.aliasId + // ============================================== + // Create the manufacturer's DID and the DPP NFT. + // ============================================== + + // Create a new client to interact with the IOTA ledger. + const client = new Client({ + primaryNode: API_ENDPOINT, + localPow: true, + }); + const didClient = new IotaIdentityClient(client); + + // Generate a random mnemonic for our wallet. + const secretManager: MnemonicSecretManager = { + Mnemonic: Bip39.randomMnemonic(), + }; + + // Create a new DID for the manufacturer. (see "0_create_did" example). + var { did: manufacturerDid } = await createDid(client, secretManager); + + // Get the current byte costs. + const rentStructure: IRent = await didClient.getRentStructure(); + + // Get the Bech32 human-readable part (HRP) of the network. + const networkName: string = await didClient.getNetworkHrp(); + + // Create the Alias Address of the manufacturer. + var manufacturerAliasAddress: AddressTypes = { + type: ALIAS_ADDRESS_TYPE, + aliasId: manufacturerDid.toAliasId(), + }; + + // Create a Digital Product Passport NFT issued by the manufacturer. + let productPassportNft: INftOutput = await client.buildNftOutput({ + nftId: "0x0000000000000000000000000000000000000000000000000000000000000000", + immutableFeatures: [ + { + // Set the manufacturer as the immutable issuer. + type: ISSUER_FEATURE_TYPE, + address: manufacturerAliasAddress, + }, + { + // A proper DPP would hold its metadata here. + type: METADATA_FEATURE_TYPE, + data: Converter.utf8ToHex("Digital Product Passport Metadata", true), + }, + ], + unlockConditions: [ + { + // The NFT will initially be owned by the manufacturer. + type: ADDRESS_UNLOCK_CONDITION_TYPE, + address: manufacturerAliasAddress, + }, + ], + }); + + // Set the appropriate storage deposit. + productPassportNft.amount = TransactionHelper.getStorageDeposit(productPassportNft, rentStructure).toString(); + + // Publish the NFT. + const [blockId, block] = await client.buildAndPostBlock(secretManager, { outputs: [productPassportNft] }); + await client.retryUntilIncluded(blockId); + + // ======================================================== + // Resolve the Digital Product Passport NFT and its issuer. + // ======================================================== + + // Extract the identifier of the NFT from the published block. + // Non-null assertion is safe because we published a block with a payload. + let nftId: string = nft_output_id(block.payload!); + + // Fetch the NFT Output. + const nftOutputId: string = await client.nftOutputId(nftId); + const outputResponse: IOutputResponse = await client.getOutput(nftOutputId); + const output: OutputTypes = outputResponse.output; + + // Extract the issuer of the NFT. + let manufacturerAliasId: string; + if (output.type === NFT_OUTPUT_TYPE && output.immutableFeatures) { + const issuerFeature: IIssuerFeature = output.immutableFeatures.find(feature => + feature.type === ISSUER_FEATURE_TYPE + ) as IIssuerFeature; + + if (issuerFeature && issuerFeature.address.type === ALIAS_ADDRESS_TYPE) { + manufacturerAliasId = issuerFeature.address.aliasId; + } else { + throw new Error("expected to find issuer feature with an alias address"); + } } else { - throw new Error("expected to find issuer feature with an alias address"); + throw new Error("expected NFT output with immutable features"); } - } else { - throw new Error("expected NFT output with immutable features"); - } - // Reconstruct the manufacturer's DID from the Alias Id. - manufacturerDid = new IotaDID(Converter.hexToBytes(manufacturerAliasId), networkName); + // Reconstruct the manufacturer's DID from the Alias Id. + manufacturerDid = new IotaDID(Converter.hexToBytes(manufacturerAliasId), networkName); - // Resolve the issuer of the NFT. - const manufacturerDocument: IotaDocument = await didClient.resolveDid(manufacturerDid); + // Resolve the issuer of the NFT. + const manufacturerDocument: IotaDocument = await didClient.resolveDid(manufacturerDid); - console.log("The issuer of the Digital Product Passport NFT is:", JSON.stringify(manufacturerDocument, null, 2)); + console.log("The issuer of the Digital Product Passport NFT is:", JSON.stringify(manufacturerDocument, null, 2)); } function nft_output_id(payload: PayloadTypes): string { - if (payload.type === TRANSACTION_PAYLOAD_TYPE) { - const txPayload: ITransactionPayload = payload; - const txHash = Converter.bytesToHex(TransactionHelper.getTransactionPayloadHash(txPayload), true); - - if (txPayload.essence.type === TRANSACTION_ESSENCE_TYPE) { - const outputs = txPayload.essence.outputs; - for (let index in txPayload.essence.outputs) { - if (outputs[index].type === NFT_OUTPUT_TYPE) { - const outputId: string = TransactionHelper.outputIdFromTransactionData(txHash, parseInt(index)); - return TransactionHelper.resolveIdFromOutputId(outputId); + if (payload.type === TRANSACTION_PAYLOAD_TYPE) { + const txPayload: ITransactionPayload = payload; + const txHash = Converter.bytesToHex(TransactionHelper.getTransactionPayloadHash(txPayload), true); + + if (txPayload.essence.type === TRANSACTION_ESSENCE_TYPE) { + const outputs = txPayload.essence.outputs; + for (let index in txPayload.essence.outputs) { + if (outputs[index].type === NFT_OUTPUT_TYPE) { + const outputId: string = TransactionHelper.outputIdFromTransactionData(txHash, parseInt(index)); + return TransactionHelper.resolveIdFromOutputId(outputId); + } + } + throw new Error("no NFT output in transaction essence"); + } else { + throw new Error("expected transaction essence"); } - } - throw new Error("no NFT output in transaction essence"); } else { - throw new Error("expected transaction essence"); + throw new Error("expected transaction payload"); } - } else { - throw new Error("expected transaction payload"); - } } diff --git a/bindings/wasm/examples/src/1_advanced/2_nft_owns_did.ts b/bindings/wasm/examples/src/1_advanced/2_nft_owns_did.ts index a452141c1c..d0e29dddbe 100644 --- a/bindings/wasm/examples/src/1_advanced/2_nft_owns_did.ts +++ b/bindings/wasm/examples/src/1_advanced/2_nft_owns_did.ts @@ -1,12 +1,30 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { IotaDocument, IotaIdentityClient } from '../../../node'; -import { AddressTypes, IRent, TransactionHelper, NFT_OUTPUT_TYPE, PayloadTypes, TRANSACTION_PAYLOAD_TYPE, ITransactionPayload, TRANSACTION_ESSENCE_TYPE, INftOutput, ADDRESS_UNLOCK_CONDITION_TYPE, IOutputResponse, OutputTypes, Bech32Helper, NFT_ADDRESS_TYPE, IAliasOutput, IStateControllerAddressUnlockCondition, STATE_CONTROLLER_ADDRESS_UNLOCK_CONDITION_TYPE } from '@iota/iota.js'; -import { API_ENDPOINT, ensureAddressHasFunds } from '../util'; -import { Client, MnemonicSecretManager } from '@iota/iota-client-wasm/node'; -import { Bip39 } from '@iota/crypto.js'; -import { Converter } from '@iota/util.js'; +import { Bip39 } from "@iota/crypto.js"; +import { Client, MnemonicSecretManager } from "@iota/iota-client-wasm/node"; +import { + ADDRESS_UNLOCK_CONDITION_TYPE, + AddressTypes, + Bech32Helper, + IAliasOutput, + INftOutput, + IOutputResponse, + IRent, + IStateControllerAddressUnlockCondition, + ITransactionPayload, + NFT_ADDRESS_TYPE, + NFT_OUTPUT_TYPE, + OutputTypes, + PayloadTypes, + STATE_CONTROLLER_ADDRESS_UNLOCK_CONDITION_TYPE, + TRANSACTION_ESSENCE_TYPE, + TRANSACTION_PAYLOAD_TYPE, + TransactionHelper, +} from "@iota/iota.js"; +import { Converter } from "@iota/util.js"; +import { IotaDocument, IotaIdentityClient } from "../../../node"; +import { API_ENDPOINT, ensureAddressHasFunds } from "../util"; /** Demonstrates how an identity can be owned by NFTs, and how observers can verify that relationship. @@ -14,127 +32,130 @@ and how observers can verify that relationship. For this example, we consider the case where a car's NFT owns the DID of the car, so that transferring the NFT also transfers DID ownership. */ export async function nftOwnsDid() { - // ============================= - // Create the car's NFT and DID. - // ============================= - - // Create a new client to interact with the IOTA ledger. - const client = new Client({ - primaryNode: API_ENDPOINT, - localPow: true, - }); - const didClient = new IotaIdentityClient(client); - - // Generate a random mnemonic for our wallet. - const secretManager: MnemonicSecretManager = { - Mnemonic: Bip39.randomMnemonic() - }; - - // Get the current byte costs. - const rentStructure: IRent = await didClient.getRentStructure(); - - // Get the Bech32 human-readable part (HRP) of the network. - const networkName: string = await didClient.getNetworkHrp(); - - // Create a new address that will own the NFT. - const addressBech32 = (await client.generateAddresses(secretManager, { - accountIndex: 0, - range: { - start: 0, - end: 1, - }, - }))[0]; - const address = Bech32Helper.addressFromBech32(addressBech32, networkName); - - // Get funds for testing from the faucet. - await ensureAddressHasFunds(client, addressBech32); - - // Create the car NFT with an Ed25519 address as the unlock condition. - let carNft: INftOutput = await client.buildNftOutput({ - nftId: "0x0000000000000000000000000000000000000000000000000000000000000000", - unlockConditions: [ - { - // The NFT will initially be owned by the Ed25519 address. - type: ADDRESS_UNLOCK_CONDITION_TYPE, - address - } - ] - }); - - // Set the appropriate storage deposit. - carNft.amount = TransactionHelper.getStorageDeposit(carNft, rentStructure).toString(); - - // Publish the NFT. - const [blockId, block] = await client.buildAndPostBlock(secretManager, { outputs: [carNft] }); - await client.retryUntilIncluded(blockId); - - // Extract the identifier of the NFT from the published block. - // Non-null assertion is safe because we published a block with a payload. - var carNftId: string = nft_output_id(block.payload!) - - // Create the address of the NFT. - const nftAddress: AddressTypes = { - type: NFT_ADDRESS_TYPE, - nftId: carNftId, - }; - - // Construct a DID document for the car. - var carDocument: IotaDocument = new IotaDocument(networkName); - - // Create a new DID for the car that is owned by the car NFT. - var carDidAliasOutput: IAliasOutput = await didClient.newDidOutput(nftAddress, carDocument, rentStructure); - - // Publish the car DID. - carDocument = await didClient.publishDidOutput(secretManager, carDidAliasOutput); - - // ============================================ - // Determine the car's NFT given the car's DID. - // ============================================ - - // Resolve the Alias Output of the DID. - carDidAliasOutput = await didClient.resolveDidOutput(carDocument.id()); - - // Extract the NFT Id from the state controller unlock condition. - const stateControllerUnlockCondition: IStateControllerAddressUnlockCondition = carDidAliasOutput.unlockConditions.find(feature => feature.type === STATE_CONTROLLER_ADDRESS_UNLOCK_CONDITION_TYPE) as IStateControllerAddressUnlockCondition; - if (stateControllerUnlockCondition.address.type === NFT_ADDRESS_TYPE) { - carNftId = stateControllerUnlockCondition.address.nftId - } else { - throw new Error("expected nft address unlock condition"); - } - - // Fetch the NFT Output of the car. - const nftOutputId: string = await client.nftOutputId(carNftId); - const outputResponse: IOutputResponse = await client.getOutput(nftOutputId); - const output: OutputTypes = outputResponse.output - - if (output.type === NFT_OUTPUT_TYPE) { - carNft = output; - } else { - throw new Error("expected nft output type"); - } - - console.log("The car's DID is:", JSON.stringify(carDocument, null, 2)); - console.log("The car's NFT is:", JSON.stringify(carNft, null, 2)); + // ============================= + // Create the car's NFT and DID. + // ============================= + + // Create a new client to interact with the IOTA ledger. + const client = new Client({ + primaryNode: API_ENDPOINT, + localPow: true, + }); + const didClient = new IotaIdentityClient(client); + + // Generate a random mnemonic for our wallet. + const secretManager: MnemonicSecretManager = { + Mnemonic: Bip39.randomMnemonic(), + }; + + // Get the current byte costs. + const rentStructure: IRent = await didClient.getRentStructure(); + + // Get the Bech32 human-readable part (HRP) of the network. + const networkName: string = await didClient.getNetworkHrp(); + + // Create a new address that will own the NFT. + const addressBech32 = (await client.generateAddresses(secretManager, { + accountIndex: 0, + range: { + start: 0, + end: 1, + }, + }))[0]; + const address = Bech32Helper.addressFromBech32(addressBech32, networkName); + + // Get funds for testing from the faucet. + await ensureAddressHasFunds(client, addressBech32); + + // Create the car NFT with an Ed25519 address as the unlock condition. + let carNft: INftOutput = await client.buildNftOutput({ + nftId: "0x0000000000000000000000000000000000000000000000000000000000000000", + unlockConditions: [ + { + // The NFT will initially be owned by the Ed25519 address. + type: ADDRESS_UNLOCK_CONDITION_TYPE, + address, + }, + ], + }); + + // Set the appropriate storage deposit. + carNft.amount = TransactionHelper.getStorageDeposit(carNft, rentStructure).toString(); + + // Publish the NFT. + const [blockId, block] = await client.buildAndPostBlock(secretManager, { outputs: [carNft] }); + await client.retryUntilIncluded(blockId); + + // Extract the identifier of the NFT from the published block. + // Non-null assertion is safe because we published a block with a payload. + var carNftId: string = nft_output_id(block.payload!); + + // Create the address of the NFT. + const nftAddress: AddressTypes = { + type: NFT_ADDRESS_TYPE, + nftId: carNftId, + }; + + // Construct a DID document for the car. + var carDocument: IotaDocument = new IotaDocument(networkName); + + // Create a new DID for the car that is owned by the car NFT. + var carDidAliasOutput: IAliasOutput = await didClient.newDidOutput(nftAddress, carDocument, rentStructure); + + // Publish the car DID. + carDocument = await didClient.publishDidOutput(secretManager, carDidAliasOutput); + + // ============================================ + // Determine the car's NFT given the car's DID. + // ============================================ + + // Resolve the Alias Output of the DID. + carDidAliasOutput = await didClient.resolveDidOutput(carDocument.id()); + + // Extract the NFT Id from the state controller unlock condition. + const stateControllerUnlockCondition: IStateControllerAddressUnlockCondition = carDidAliasOutput.unlockConditions + .find(feature => + feature.type === STATE_CONTROLLER_ADDRESS_UNLOCK_CONDITION_TYPE + ) as IStateControllerAddressUnlockCondition; + if (stateControllerUnlockCondition.address.type === NFT_ADDRESS_TYPE) { + carNftId = stateControllerUnlockCondition.address.nftId; + } else { + throw new Error("expected nft address unlock condition"); + } + + // Fetch the NFT Output of the car. + const nftOutputId: string = await client.nftOutputId(carNftId); + const outputResponse: IOutputResponse = await client.getOutput(nftOutputId); + const output: OutputTypes = outputResponse.output; + + if (output.type === NFT_OUTPUT_TYPE) { + carNft = output; + } else { + throw new Error("expected nft output type"); + } + + console.log("The car's DID is:", JSON.stringify(carDocument, null, 2)); + console.log("The car's NFT is:", JSON.stringify(carNft, null, 2)); } function nft_output_id(payload: PayloadTypes): string { - if (payload.type === TRANSACTION_PAYLOAD_TYPE) { - const txPayload: ITransactionPayload = payload; - const txHash = Converter.bytesToHex(TransactionHelper.getTransactionPayloadHash(txPayload), true); - - if (txPayload.essence.type === TRANSACTION_ESSENCE_TYPE) { - const outputs = txPayload.essence.outputs; - for (let index in txPayload.essence.outputs) { - if (outputs[index].type === NFT_OUTPUT_TYPE) { - const outputId: string = TransactionHelper.outputIdFromTransactionData(txHash, parseInt(index)); - return TransactionHelper.resolveIdFromOutputId(outputId); + if (payload.type === TRANSACTION_PAYLOAD_TYPE) { + const txPayload: ITransactionPayload = payload; + const txHash = Converter.bytesToHex(TransactionHelper.getTransactionPayloadHash(txPayload), true); + + if (txPayload.essence.type === TRANSACTION_ESSENCE_TYPE) { + const outputs = txPayload.essence.outputs; + for (let index in txPayload.essence.outputs) { + if (outputs[index].type === NFT_OUTPUT_TYPE) { + const outputId: string = TransactionHelper.outputIdFromTransactionData(txHash, parseInt(index)); + return TransactionHelper.resolveIdFromOutputId(outputId); + } + } + throw new Error("no NFT output in transaction essence"); + } else { + throw new Error("expected transaction essence"); } - } - throw new Error("no NFT output in transaction essence"); } else { - throw new Error("expected transaction essence"); + throw new Error("expected transaction payload"); } - } else { - throw new Error("expected transaction payload"); - } } diff --git a/bindings/wasm/examples/src/1_advanced/3_did_issues_tokens.ts b/bindings/wasm/examples/src/1_advanced/3_did_issues_tokens.ts index bec626fb18..1eeedc8c25 100644 --- a/bindings/wasm/examples/src/1_advanced/3_did_issues_tokens.ts +++ b/bindings/wasm/examples/src/1_advanced/3_did_issues_tokens.ts @@ -1,181 +1,202 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { IotaDID, IotaDocument, IotaIdentityClient } from '../../../node'; -import { IRent, TransactionHelper, ADDRESS_UNLOCK_CONDITION_TYPE, IOutputResponse, OutputTypes, Bech32Helper, IAliasOutput, SIMPLE_TOKEN_SCHEME_TYPE, ISimpleTokenScheme, IMMUTABLE_ALIAS_UNLOCK_CONDITION_TYPE, ALIAS_ADDRESS_TYPE, FOUNDRY_OUTPUT_TYPE, IImmutableAliasUnlockCondition, IAliasAddress, IBasicOutput, EXPIRATION_UNLOCK_CONDITION_TYPE } from '@iota/iota.js'; -import { API_ENDPOINT, createDid } from '../util'; -import { Client, MnemonicSecretManager } from '@iota/iota-client-wasm/node'; -import { Converter, HexHelper } from '@iota/util.js'; -import type { IFoundryOutput } from '@iota/types'; +import { Bip39 } from "@iota/crypto.js"; +import { Client, MnemonicSecretManager } from "@iota/iota-client-wasm/node"; +import { + ADDRESS_UNLOCK_CONDITION_TYPE, + ALIAS_ADDRESS_TYPE, + Bech32Helper, + EXPIRATION_UNLOCK_CONDITION_TYPE, + FOUNDRY_OUTPUT_TYPE, + IAliasAddress, + IAliasOutput, + IBasicOutput, + IImmutableAliasUnlockCondition, + IMMUTABLE_ALIAS_UNLOCK_CONDITION_TYPE, + IOutputResponse, + IRent, + ISimpleTokenScheme, + OutputTypes, + SIMPLE_TOKEN_SCHEME_TYPE, + TransactionHelper, +} from "@iota/iota.js"; +import type { IFoundryOutput } from "@iota/types"; +import { Converter, HexHelper } from "@iota/util.js"; import bigInt from "big-integer"; -import { Bip39 } from '@iota/crypto.js'; +import { IotaDID, IotaDocument, IotaIdentityClient } from "../../../node"; +import { API_ENDPOINT, createDid } from "../util"; /** Demonstrates how an identity can issue and control a Token Foundry and its tokens. For this example, we consider the case where an authority issues carbon credits that can be used to pay for carbon emissions or traded on a marketplace. */ export async function didIssuesTokens() { - // =========================================== - // Create the authority's DID and the foundry. - // =========================================== - - // Create a new client to interact with the IOTA ledger. - const client = new Client({ - primaryNode: API_ENDPOINT, - localPow: true, - }); - const didClient = new IotaIdentityClient(client); - - // Generate a random mnemonic for our wallet. - const secretManager: MnemonicSecretManager = { - Mnemonic: Bip39.randomMnemonic() - }; - - // Create a new DID for the authority. (see "0_create_did" example). - var { did: authorityDid } = await createDid(client, secretManager); - - // Get the current byte costs. - const rentStructure: IRent = await didClient.getRentStructure(); - - // Get the Bech32 human-readable part (HRP) of the network. - const networkName: string = await didClient.getNetworkHrp(); - - // We want to update the foundry counter of the authority's Alias Output, so we create an - // updated version of the output. We pass in the previous document, - // because we don't want to modify it in this update. - var authorityDocument: IotaDocument = await didClient.resolveDid(authorityDid); - const authorityAliasOutput: IAliasOutput = await didClient.updateDidOutput(authorityDocument); - - // We will add one foundry to this Alias Output. - authorityAliasOutput.foundryCounter += 1; - - // Create a token foundry that represents carbon credits. - const tokenScheme: ISimpleTokenScheme = { - type: SIMPLE_TOKEN_SCHEME_TYPE, - mintedTokens: HexHelper.fromBigInt256(bigInt(500_000)), - meltedTokens: HexHelper.fromBigInt256(bigInt(0)), - maximumSupply: HexHelper.fromBigInt256(bigInt(1_000_000)), - }; - - // Create the identifier of the token, which is partially derived from the Alias Address. - const tokenId: string = TransactionHelper.constructTokenId(authorityDid.toAliasId(), 1, tokenScheme.type); - - // Create a token foundry that represents carbon credits. - var carbonCreditsFoundry: IFoundryOutput = await client.buildFoundryOutput({ - tokenScheme, - serialNumber: 1, - // Initially, all carbon credits are owned by the foundry. - nativeTokens: [ - { - id: tokenId, - amount: HexHelper.fromBigInt256(bigInt(500_000)) - } - ], - // The authority is set as the immutable owner. - unlockConditions: [ - { - type: IMMUTABLE_ALIAS_UNLOCK_CONDITION_TYPE, - address: { - type: ALIAS_ADDRESS_TYPE, - aliasId: authorityDid.toAliasId() - } - } - ] - }); - - // Set the appropriate storage deposit. - carbonCreditsFoundry.amount = TransactionHelper.getStorageDeposit(carbonCreditsFoundry, rentStructure).toString(); - - // Publish the foundry. - const [blockId, block] = await client.buildAndPostBlock(secretManager, { outputs: [authorityAliasOutput, carbonCreditsFoundry] }); - await client.retryUntilIncluded(blockId); - - // =================================== - // Resolve foundry and its issuer DID. - // =================================== - - // Get the latest output that contains the foundry. - const carbonCreditsFoundryId: string = tokenId; - const outputId: string = await client.foundryOutputId(carbonCreditsFoundryId) - const outputResponse: IOutputResponse = await client.getOutput(outputId); - const output: OutputTypes = outputResponse.output; - - if (output.type === FOUNDRY_OUTPUT_TYPE) { - carbonCreditsFoundry = output; - } else { - throw new Error("expected foundry output") - } - - // Get the Alias Id of the authority that issued the carbon credits foundry. - // Non-null assertion is safe as each founry output needs to have an immutable alias unlock condition. - const aliasUnlockCondition: IImmutableAliasUnlockCondition = carbonCreditsFoundry.unlockConditions.find(unlockCondition => unlockCondition.type === IMMUTABLE_ALIAS_UNLOCK_CONDITION_TYPE)! as IImmutableAliasUnlockCondition; - - // We know the immutable alias unlock condition contains an alias address. - const authorityAliasId: string = (aliasUnlockCondition.address as IAliasAddress).aliasId - - // Reconstruct the DID of the authority. - authorityDid = new IotaDID(Converter.hexToBytes(authorityAliasId), networkName); - - // Resolve the authority's DID document. - authorityDocument = await didClient.resolveDid(authorityDid); - - console.log("The authority's DID is:", JSON.stringify(authorityDocument, null, 2)); - - // ========================================================= - // Transfer 1000 carbon credits to the address of a company. - // ========================================================= - - // Create a new address that represents the company. - const companyAddressBech32: string = (await client.generateAddresses(secretManager, { - accountIndex: 0, - range: { - start: 1, - end: 2, - }, - }))[0]; - const companyAddress = Bech32Helper.addressFromBech32(companyAddressBech32, networkName); - - // Create a timestamp 24 hours from now. - const tomorrow: number = Math.floor(Date.now() / 1000) + (60 * 60 * 24); - - // Create a basic output containing our carbon credits that we'll send to the company's address. - const basicOutput: IBasicOutput = await client.buildBasicOutput({ - nativeTokens: [ - { - amount: HexHelper.fromBigInt256(bigInt(1000)), - id: tokenId, - } - ], - // Allow the company to claim the credits within 24 hours by using an expiration unlock condition. - unlockConditions: [ - { - type: ADDRESS_UNLOCK_CONDITION_TYPE, - address: companyAddress, - }, - { - type: EXPIRATION_UNLOCK_CONDITION_TYPE, - unixTime: tomorrow, - returnAddress: { - type: ALIAS_ADDRESS_TYPE, - aliasId: authorityAliasId - } - } - ] - }); - - // Reduce the carbon credits in the foundry by the amount that is sent to the company. - carbonCreditsFoundry.nativeTokens = [ - { - amount: HexHelper.fromBigInt256(bigInt(499_000)), - id: tokenId, + // =========================================== + // Create the authority's DID and the foundry. + // =========================================== + + // Create a new client to interact with the IOTA ledger. + const client = new Client({ + primaryNode: API_ENDPOINT, + localPow: true, + }); + const didClient = new IotaIdentityClient(client); + + // Generate a random mnemonic for our wallet. + const secretManager: MnemonicSecretManager = { + Mnemonic: Bip39.randomMnemonic(), + }; + + // Create a new DID for the authority. (see "0_create_did" example). + var { did: authorityDid } = await createDid(client, secretManager); + + // Get the current byte costs. + const rentStructure: IRent = await didClient.getRentStructure(); + + // Get the Bech32 human-readable part (HRP) of the network. + const networkName: string = await didClient.getNetworkHrp(); + + // We want to update the foundry counter of the authority's Alias Output, so we create an + // updated version of the output. We pass in the previous document, + // because we don't want to modify it in this update. + var authorityDocument: IotaDocument = await didClient.resolveDid(authorityDid); + const authorityAliasOutput: IAliasOutput = await didClient.updateDidOutput(authorityDocument); + + // We will add one foundry to this Alias Output. + authorityAliasOutput.foundryCounter += 1; + + // Create a token foundry that represents carbon credits. + const tokenScheme: ISimpleTokenScheme = { + type: SIMPLE_TOKEN_SCHEME_TYPE, + mintedTokens: HexHelper.fromBigInt256(bigInt(500_000)), + meltedTokens: HexHelper.fromBigInt256(bigInt(0)), + maximumSupply: HexHelper.fromBigInt256(bigInt(1_000_000)), + }; + + // Create the identifier of the token, which is partially derived from the Alias Address. + const tokenId: string = TransactionHelper.constructTokenId(authorityDid.toAliasId(), 1, tokenScheme.type); + + // Create a token foundry that represents carbon credits. + var carbonCreditsFoundry: IFoundryOutput = await client.buildFoundryOutput({ + tokenScheme, + serialNumber: 1, + // Initially, all carbon credits are owned by the foundry. + nativeTokens: [ + { + id: tokenId, + amount: HexHelper.fromBigInt256(bigInt(500_000)), + }, + ], + // The authority is set as the immutable owner. + unlockConditions: [ + { + type: IMMUTABLE_ALIAS_UNLOCK_CONDITION_TYPE, + address: { + type: ALIAS_ADDRESS_TYPE, + aliasId: authorityDid.toAliasId(), + }, + }, + ], + }); + + // Set the appropriate storage deposit. + carbonCreditsFoundry.amount = TransactionHelper.getStorageDeposit(carbonCreditsFoundry, rentStructure).toString(); + + // Publish the foundry. + const [blockId, block] = await client.buildAndPostBlock(secretManager, { + outputs: [authorityAliasOutput, carbonCreditsFoundry], + }); + await client.retryUntilIncluded(blockId); + + // =================================== + // Resolve foundry and its issuer DID. + // =================================== + + // Get the latest output that contains the foundry. + const carbonCreditsFoundryId: string = tokenId; + const outputId: string = await client.foundryOutputId(carbonCreditsFoundryId); + const outputResponse: IOutputResponse = await client.getOutput(outputId); + const output: OutputTypes = outputResponse.output; + + if (output.type === FOUNDRY_OUTPUT_TYPE) { + carbonCreditsFoundry = output; + } else { + throw new Error("expected foundry output"); } - ]; - // Publish the Basic Output and the updated foundry. - const [blockId2, block2] = await client.buildAndPostBlock(secretManager, { - outputs: [basicOutput, carbonCreditsFoundry] - }); - await client.retryUntilIncluded(blockId2); - - console.log("Sent carbon credits to", companyAddressBech32); + // Get the Alias Id of the authority that issued the carbon credits foundry. + // Non-null assertion is safe as each founry output needs to have an immutable alias unlock condition. + const aliasUnlockCondition: IImmutableAliasUnlockCondition = carbonCreditsFoundry.unlockConditions.find( + unlockCondition => unlockCondition.type === IMMUTABLE_ALIAS_UNLOCK_CONDITION_TYPE, + )! as IImmutableAliasUnlockCondition; + + // We know the immutable alias unlock condition contains an alias address. + const authorityAliasId: string = (aliasUnlockCondition.address as IAliasAddress).aliasId; + + // Reconstruct the DID of the authority. + authorityDid = new IotaDID(Converter.hexToBytes(authorityAliasId), networkName); + + // Resolve the authority's DID document. + authorityDocument = await didClient.resolveDid(authorityDid); + + console.log("The authority's DID is:", JSON.stringify(authorityDocument, null, 2)); + + // ========================================================= + // Transfer 1000 carbon credits to the address of a company. + // ========================================================= + + // Create a new address that represents the company. + const companyAddressBech32: string = (await client.generateAddresses(secretManager, { + accountIndex: 0, + range: { + start: 1, + end: 2, + }, + }))[0]; + const companyAddress = Bech32Helper.addressFromBech32(companyAddressBech32, networkName); + + // Create a timestamp 24 hours from now. + const tomorrow: number = Math.floor(Date.now() / 1000) + (60 * 60 * 24); + + // Create a basic output containing our carbon credits that we'll send to the company's address. + const basicOutput: IBasicOutput = await client.buildBasicOutput({ + nativeTokens: [ + { + amount: HexHelper.fromBigInt256(bigInt(1000)), + id: tokenId, + }, + ], + // Allow the company to claim the credits within 24 hours by using an expiration unlock condition. + unlockConditions: [ + { + type: ADDRESS_UNLOCK_CONDITION_TYPE, + address: companyAddress, + }, + { + type: EXPIRATION_UNLOCK_CONDITION_TYPE, + unixTime: tomorrow, + returnAddress: { + type: ALIAS_ADDRESS_TYPE, + aliasId: authorityAliasId, + }, + }, + ], + }); + + // Reduce the carbon credits in the foundry by the amount that is sent to the company. + carbonCreditsFoundry.nativeTokens = [ + { + amount: HexHelper.fromBigInt256(bigInt(499_000)), + id: tokenId, + }, + ]; + + // Publish the Basic Output and the updated foundry. + const [blockId2, block2] = await client.buildAndPostBlock(secretManager, { + outputs: [basicOutput, carbonCreditsFoundry], + }); + await client.retryUntilIncluded(blockId2); + + console.log("Sent carbon credits to", companyAddressBech32); } diff --git a/bindings/wasm/examples/src/1_advanced/4_key_exchange.ts b/bindings/wasm/examples/src/1_advanced/4_key_exchange.ts index 6ff752f87e..da098ba116 100644 --- a/bindings/wasm/examples/src/1_advanced/4_key_exchange.ts +++ b/bindings/wasm/examples/src/1_advanced/4_key_exchange.ts @@ -1,11 +1,19 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { IotaDocument, IotaIdentityClient, IotaVerificationMethod, KeyPair, KeyType, MethodScope, X25519 } from '../../../node'; -import { IRent, OutputTypes, Bech32Helper, AddressTypes } from '@iota/iota.js'; -import { API_ENDPOINT, ensureAddressHasFunds } from '../util'; -import { Client, MnemonicSecretManager } from '@iota/iota-client-wasm/node'; -import { Bip39 } from '@iota/crypto.js'; +import { Bip39 } from "@iota/crypto.js"; +import { Client, MnemonicSecretManager } from "@iota/iota-client-wasm/node"; +import { AddressTypes, Bech32Helper, IRent, OutputTypes } from "@iota/iota.js"; +import { + IotaDocument, + IotaIdentityClient, + IotaVerificationMethod, + KeyPair, + KeyType, + MethodScope, + X25519, +} from "../../../node"; +import { API_ENDPOINT, ensureAddressHasFunds } from "../util"; /** Demonstrates Elliptic-curve Diffie-Hellman (ECDH) cryptographic key exchange with DID Documents. @@ -13,116 +21,126 @@ Alice and Bob want to communicate securely by encrypting their messages so only can read them. They both publish DID Documents with X25519 public keys and use them to derive a shared secret key for encryption. */ export async function keyExchange() { - // ============================== - // Create DIDs for Alice and Bob. - // ============================== - - // Create a new client to interact with the IOTA ledger. - const client = new Client({ - primaryNode: API_ENDPOINT, - localPow: true, - }); - const didClient = new IotaIdentityClient(client); - - // Generate a random mnemonic for our wallet. - const secretManager: MnemonicSecretManager = { - Mnemonic: Bip39.randomMnemonic() - }; - - // Get the Bech32 human-readable part (HRP) of the network. - const networkName: string = await didClient.getNetworkHrp(); - - // Create a new address with funds for testing. - const addressBech32 = (await client.generateAddresses(secretManager, { - accountIndex: 0, - range: { - start: 0, - end: 1, - }, - }))[0]; - const address: AddressTypes = Bech32Helper.addressFromBech32(addressBech32, networkName); - await ensureAddressHasFunds(client, addressBech32); - - // Get the current byte costs. - const rentStructure: IRent = await didClient.getRentStructure(); - - // Alice creates and publishes their DID Document (see 0_create_did and 1_update_did examples). - let aliceDid; - let aliceX25519; - { - // Create a DID Document. - let aliceDocument: IotaDocument = new IotaDocument(networkName); - - // Insert a new X25519 KeyAgreement verification method. - const x25519: KeyPair = new KeyPair(KeyType.X25519); - const method: IotaVerificationMethod = new IotaVerificationMethod(aliceDocument.id(), KeyType.X25519, x25519.public(), "kex-0"); - aliceDocument.insertMethod(method, MethodScope.KeyAgreement()); - - // Publish the DID document. - const aliceOutput: OutputTypes = await didClient.newDidOutput(address, aliceDocument, rentStructure); - aliceDocument = await didClient.publishDidOutput(secretManager, aliceOutput); - - aliceDid = aliceDocument.id(); - aliceX25519 = x25519; - } - - // Alice creates and publishes their DID Document (see 0_create_did and 1_update_did examples). - let bobDid; - let bobX25519; - { - // Create a DID Document. - let bobDocument: IotaDocument = new IotaDocument(networkName); - - // Insert a new X25519 KeyAgreement verification method. - const x25519: KeyPair = new KeyPair(KeyType.X25519); - const method: IotaVerificationMethod = new IotaVerificationMethod(bobDocument.id(), KeyType.X25519, x25519.public(), "kex-0"); - bobDocument.insertMethod(method, MethodScope.KeyAgreement()); - - // Publish the DID document. - const aliceOutput: OutputTypes = await didClient.newDidOutput(address, bobDocument, rentStructure); - bobDocument = await didClient.publishDidOutput(secretManager, aliceOutput); - - bobDid = bobDocument.id(); - bobX25519 = x25519; - } - - // ====================================================================== - // Alice and Bob tell each other their DIDs. They each resolve the - // DID Document of the other to obtain their X25519 public key. - // Note that in practice, they would run this code completely separately. - // ====================================================================== - - let aliceSharedSecretKey; - { - // Alice: resolves Bob's DID Document and extracts their public key. - const bobDocument: IotaDocument = await didClient.resolveDid(bobDid); - const bobMethod: IotaVerificationMethod = bobDocument.resolveMethod("kex-0", MethodScope.KeyAgreement())!; - const bobPublicKey: Uint8Array = bobMethod.data().tryDecode(); - // Compute the shared secret. - aliceSharedSecretKey = X25519.keyExchange(aliceX25519.private(), bobPublicKey); - } - - let bobSharedSecretKey; - { - // Bob: resolves Alice's DID Document and extracts their public key. - const aliceDocument: IotaDocument = await didClient.resolveDid(aliceDid); - const aliceMethod: IotaVerificationMethod = aliceDocument.resolveMethod("kex-0", MethodScope.KeyAgreement())!; - const alicePublicKey: Uint8Array = aliceMethod.data().tryDecode(); - // Compute the shared secret. - bobSharedSecretKey = X25519.keyExchange(bobX25519.private(), alicePublicKey); - } - - // Both shared secret keys computed separately by Alice and Bob will match - // and can then be used to establish encrypted communications. - if (!isArrayEqual(aliceSharedSecretKey, bobSharedSecretKey)) throw new Error("shared secret keys do not match!"); - - console.log(`Diffie-Hellman key exchange successful!`); + // ============================== + // Create DIDs for Alice and Bob. + // ============================== + + // Create a new client to interact with the IOTA ledger. + const client = new Client({ + primaryNode: API_ENDPOINT, + localPow: true, + }); + const didClient = new IotaIdentityClient(client); + + // Generate a random mnemonic for our wallet. + const secretManager: MnemonicSecretManager = { + Mnemonic: Bip39.randomMnemonic(), + }; + + // Get the Bech32 human-readable part (HRP) of the network. + const networkName: string = await didClient.getNetworkHrp(); + + // Create a new address with funds for testing. + const addressBech32 = (await client.generateAddresses(secretManager, { + accountIndex: 0, + range: { + start: 0, + end: 1, + }, + }))[0]; + const address: AddressTypes = Bech32Helper.addressFromBech32(addressBech32, networkName); + await ensureAddressHasFunds(client, addressBech32); + + // Get the current byte costs. + const rentStructure: IRent = await didClient.getRentStructure(); + + // Alice creates and publishes their DID Document (see 0_create_did and 1_update_did examples). + let aliceDid; + let aliceX25519; + { + // Create a DID Document. + let aliceDocument: IotaDocument = new IotaDocument(networkName); + + // Insert a new X25519 KeyAgreement verification method. + const x25519: KeyPair = new KeyPair(KeyType.X25519); + const method: IotaVerificationMethod = new IotaVerificationMethod( + aliceDocument.id(), + KeyType.X25519, + x25519.public(), + "kex-0", + ); + aliceDocument.insertMethod(method, MethodScope.KeyAgreement()); + + // Publish the DID document. + const aliceOutput: OutputTypes = await didClient.newDidOutput(address, aliceDocument, rentStructure); + aliceDocument = await didClient.publishDidOutput(secretManager, aliceOutput); + + aliceDid = aliceDocument.id(); + aliceX25519 = x25519; + } + + // Alice creates and publishes their DID Document (see 0_create_did and 1_update_did examples). + let bobDid; + let bobX25519; + { + // Create a DID Document. + let bobDocument: IotaDocument = new IotaDocument(networkName); + + // Insert a new X25519 KeyAgreement verification method. + const x25519: KeyPair = new KeyPair(KeyType.X25519); + const method: IotaVerificationMethod = new IotaVerificationMethod( + bobDocument.id(), + KeyType.X25519, + x25519.public(), + "kex-0", + ); + bobDocument.insertMethod(method, MethodScope.KeyAgreement()); + + // Publish the DID document. + const aliceOutput: OutputTypes = await didClient.newDidOutput(address, bobDocument, rentStructure); + bobDocument = await didClient.publishDidOutput(secretManager, aliceOutput); + + bobDid = bobDocument.id(); + bobX25519 = x25519; + } + + // ====================================================================== + // Alice and Bob tell each other their DIDs. They each resolve the + // DID Document of the other to obtain their X25519 public key. + // Note that in practice, they would run this code completely separately. + // ====================================================================== + + let aliceSharedSecretKey; + { + // Alice: resolves Bob's DID Document and extracts their public key. + const bobDocument: IotaDocument = await didClient.resolveDid(bobDid); + const bobMethod: IotaVerificationMethod = bobDocument.resolveMethod("kex-0", MethodScope.KeyAgreement())!; + const bobPublicKey: Uint8Array = bobMethod.data().tryDecode(); + // Compute the shared secret. + aliceSharedSecretKey = X25519.keyExchange(aliceX25519.private(), bobPublicKey); + } + + let bobSharedSecretKey; + { + // Bob: resolves Alice's DID Document and extracts their public key. + const aliceDocument: IotaDocument = await didClient.resolveDid(aliceDid); + const aliceMethod: IotaVerificationMethod = aliceDocument.resolveMethod("kex-0", MethodScope.KeyAgreement())!; + const alicePublicKey: Uint8Array = aliceMethod.data().tryDecode(); + // Compute the shared secret. + bobSharedSecretKey = X25519.keyExchange(bobX25519.private(), alicePublicKey); + } + + // Both shared secret keys computed separately by Alice and Bob will match + // and can then be used to establish encrypted communications. + if (!isArrayEqual(aliceSharedSecretKey, bobSharedSecretKey)) throw new Error("shared secret keys do not match!"); + + console.log(`Diffie-Hellman key exchange successful!`); } function isArrayEqual(a: Uint8Array, b: Uint8Array): boolean { - if (a.length !== b.length) return false; - for (let i = 0; i < a.length; i++) { - if (a[i] !== b[i]) return false; - } - return true; + if (a.length !== b.length) return false; + for (let i = 0; i < a.length; i++) { + if (a[i] !== b[i]) return false; + } + return true; } diff --git a/bindings/wasm/examples/src/tests/0_create_did.ts b/bindings/wasm/examples/src/tests/0_create_did.ts index 2c9a471233..096c2894df 100644 --- a/bindings/wasm/examples/src/tests/0_create_did.ts +++ b/bindings/wasm/examples/src/tests/0_create_did.ts @@ -1,8 +1,8 @@ -import {createIdentity} from "../0_basic/0_create_did"; +import { createIdentity } from "../0_basic/0_create_did"; // Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test node examples", function () { +describe("Test node examples", function() { it("Create Identity", async () => { await createIdentity(); }); -}) +}); diff --git a/bindings/wasm/examples/src/tests/0_did_controls_did.ts b/bindings/wasm/examples/src/tests/0_did_controls_did.ts index b7b57437b7..1ec2bb4012 100644 --- a/bindings/wasm/examples/src/tests/0_did_controls_did.ts +++ b/bindings/wasm/examples/src/tests/0_did_controls_did.ts @@ -1,8 +1,8 @@ import { didControlsDid } from "../1_advanced/0_did_controls_did"; // Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test node examples", function () { +describe("Test node examples", function() { it("Did controls Did", async () => { await didControlsDid(); }); -}) +}); diff --git a/bindings/wasm/examples/src/tests/1_did_issues_nft.ts b/bindings/wasm/examples/src/tests/1_did_issues_nft.ts index b02a76f695..90660df3ae 100644 --- a/bindings/wasm/examples/src/tests/1_did_issues_nft.ts +++ b/bindings/wasm/examples/src/tests/1_did_issues_nft.ts @@ -1,8 +1,8 @@ import { didIssuesNft } from "../1_advanced/1_did_issues_nft"; // Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test node examples", function () { +describe("Test node examples", function() { it("Did issues Nft", async () => { await didIssuesNft(); }); -}) +}); diff --git a/bindings/wasm/examples/src/tests/1_update_did.ts b/bindings/wasm/examples/src/tests/1_update_did.ts index 2acbaa8b8f..24f3847e1e 100644 --- a/bindings/wasm/examples/src/tests/1_update_did.ts +++ b/bindings/wasm/examples/src/tests/1_update_did.ts @@ -1,8 +1,8 @@ import { updateIdentity } from "../0_basic/1_update_did"; // Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test node examples", function () { +describe("Test node examples", function() { it("Update Identity", async () => { await updateIdentity(); }); -}) +}); diff --git a/bindings/wasm/examples/src/tests/2_nft_owns_did.ts b/bindings/wasm/examples/src/tests/2_nft_owns_did.ts index ef52329a6b..f148462eb3 100644 --- a/bindings/wasm/examples/src/tests/2_nft_owns_did.ts +++ b/bindings/wasm/examples/src/tests/2_nft_owns_did.ts @@ -1,8 +1,8 @@ import { nftOwnsDid } from "../1_advanced/2_nft_owns_did"; // Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test node examples", function () { +describe("Test node examples", function() { it("Nft owns Did", async () => { await nftOwnsDid(); }); -}) +}); diff --git a/bindings/wasm/examples/src/tests/2_resolve_did.ts b/bindings/wasm/examples/src/tests/2_resolve_did.ts index 83ed93a7c9..bc4b96d32b 100644 --- a/bindings/wasm/examples/src/tests/2_resolve_did.ts +++ b/bindings/wasm/examples/src/tests/2_resolve_did.ts @@ -1,8 +1,8 @@ import { resolveIdentity } from "../0_basic/2_resolve_did"; // Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test node examples", function () { +describe("Test node examples", function() { it("Resolve Identity", async () => { await resolveIdentity(); }); -}) +}); diff --git a/bindings/wasm/examples/src/tests/3_deactivate_did.ts b/bindings/wasm/examples/src/tests/3_deactivate_did.ts index 1dc0a1a164..1126c5e218 100644 --- a/bindings/wasm/examples/src/tests/3_deactivate_did.ts +++ b/bindings/wasm/examples/src/tests/3_deactivate_did.ts @@ -1,8 +1,8 @@ import { deactivateIdentity } from "../0_basic/3_deactivate_did"; // Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test node examples", function () { +describe("Test node examples", function() { it("Deactivate identity", async () => { await deactivateIdentity(); }); -}) +}); diff --git a/bindings/wasm/examples/src/tests/3_did_issues_tokens.ts b/bindings/wasm/examples/src/tests/3_did_issues_tokens.ts index d140a801ab..a3dc41e1d9 100644 --- a/bindings/wasm/examples/src/tests/3_did_issues_tokens.ts +++ b/bindings/wasm/examples/src/tests/3_did_issues_tokens.ts @@ -1,8 +1,8 @@ import { didIssuesTokens } from "../1_advanced/3_did_issues_tokens"; // Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test node examples", function () { +describe("Test node examples", function() { it("Did issues tokens", async () => { await didIssuesTokens(); }); -}) +}); diff --git a/bindings/wasm/examples/src/tests/4_delete_did.ts b/bindings/wasm/examples/src/tests/4_delete_did.ts index 0d67db36fc..e22e6e27e2 100644 --- a/bindings/wasm/examples/src/tests/4_delete_did.ts +++ b/bindings/wasm/examples/src/tests/4_delete_did.ts @@ -1,8 +1,8 @@ import { deleteIdentity } from "../0_basic/4_delete_did"; // Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test node examples", function () { +describe("Test node examples", function() { it("Delete Identity", async () => { await deleteIdentity(); }); -}) +}); diff --git a/bindings/wasm/examples/src/tests/4_key_exchange.ts b/bindings/wasm/examples/src/tests/4_key_exchange.ts index b97c1e41f1..27c7561dcc 100644 --- a/bindings/wasm/examples/src/tests/4_key_exchange.ts +++ b/bindings/wasm/examples/src/tests/4_key_exchange.ts @@ -1,8 +1,8 @@ import { keyExchange } from "../1_advanced/4_key_exchange"; // Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test node examples", function () { +describe("Test node examples", function() { it("Key exchange", async () => { await keyExchange(); }); -}) +}); diff --git a/bindings/wasm/examples/src/util.ts b/bindings/wasm/examples/src/util.ts index d13615f08e..f9f5fe5ee4 100644 --- a/bindings/wasm/examples/src/util.ts +++ b/bindings/wasm/examples/src/util.ts @@ -1,14 +1,14 @@ -import { - KeyPair, - KeyType, - MethodScope, - IotaDID, - IotaDocument, - IotaIdentityClient, - IotaVerificationMethod -} from '../../node'; import type { Client, SecretManager } from "@iota/iota-client-wasm/node"; -import { AddressTypes, Bech32Helper, IAliasOutput } from '@iota/iota.js'; +import { AddressTypes, Bech32Helper, IAliasOutput } from "@iota/iota.js"; +import { + IotaDID, + IotaDocument, + IotaIdentityClient, + IotaVerificationMethod, + KeyPair, + KeyType, + MethodScope, +} from "../../node"; export const API_ENDPOINT = "https://api.testnet.shimmer.network/"; export const FAUCET_ENDPOINT = "https://faucet.testnet.shimmer.network/api/enqueue"; @@ -18,110 +18,110 @@ export const FAUCET_ENDPOINT = "https://faucet.testnet.shimmer.network/api/enque Its functionality is equivalent to the "create DID" example and exists for convenient calling from the other examples. */ export async function createDid(client: Client, secretManager: SecretManager): Promise<{ - address: AddressTypes, - did: IotaDID + address: AddressTypes; + did: IotaDID; }> { - const didClient = new IotaIdentityClient(client); - const networkHrp: string = await didClient.getNetworkHrp(); + const didClient = new IotaIdentityClient(client); + const networkHrp: string = await didClient.getNetworkHrp(); - const walletAddressBech32 = (await client.generateAddresses(secretManager, { - accountIndex: 0, - range: { - start: 0, - end: 1, - }, - }))[0]; - console.log("Wallet address Bech32:", walletAddressBech32); + const walletAddressBech32 = (await client.generateAddresses(secretManager, { + accountIndex: 0, + range: { + start: 0, + end: 1, + }, + }))[0]; + console.log("Wallet address Bech32:", walletAddressBech32); - await ensureAddressHasFunds(client, walletAddressBech32); + await ensureAddressHasFunds(client, walletAddressBech32); - const address = Bech32Helper.addressFromBech32(walletAddressBech32, networkHrp); + const address = Bech32Helper.addressFromBech32(walletAddressBech32, networkHrp); - // Create a new DID document with a placeholder DID. - // The DID will be derived from the Alias Id of the Alias Output after publishing. - const document = new IotaDocument(networkHrp); + // Create a new DID document with a placeholder DID. + // The DID will be derived from the Alias Id of the Alias Output after publishing. + const document = new IotaDocument(networkHrp); - // Insert a new Ed25519 verification method in the DID document. - let keypair = new KeyPair(KeyType.Ed25519); - let method = new IotaVerificationMethod(document.id(), keypair.type(), keypair.public(), "#key-1"); - document.insertMethod(method, MethodScope.VerificationMethod()); + // Insert a new Ed25519 verification method in the DID document. + let keypair = new KeyPair(KeyType.Ed25519); + let method = new IotaVerificationMethod(document.id(), keypair.type(), keypair.public(), "#key-1"); + document.insertMethod(method, MethodScope.VerificationMethod()); - // Construct an Alias Output containing the DID document, with the wallet address - // set as both the state controller and governor. - const aliasOutput: IAliasOutput = await didClient.newDidOutput(address, document); + // Construct an Alias Output containing the DID document, with the wallet address + // set as both the state controller and governor. + const aliasOutput: IAliasOutput = await didClient.newDidOutput(address, document); - // Publish the Alias Output and get the published DID document. - const published = await didClient.publishDidOutput(secretManager, aliasOutput); + // Publish the Alias Output and get the published DID document. + const published = await didClient.publishDidOutput(secretManager, aliasOutput); - return { address, did: published.id() }; + return { address, did: published.id() }; } /** Request funds from the testnet faucet API, if needed, and wait for them to show in the wallet. */ export async function ensureAddressHasFunds(client: Client, addressBech32: string) { - let balance = await getAddressBalance(client, addressBech32); - if (balance > 0) { - return; - } + let balance = await getAddressBalance(client, addressBech32); + if (balance > 0) { + return; + } - await requestFundsFromFaucet(addressBech32); + await requestFundsFromFaucet(addressBech32); - for (let i = 0; i < 9; i++) { - // Wait for the funds to reflect. - await new Promise(f => setTimeout(f, 5000)); + for (let i = 0; i < 9; i++) { + // Wait for the funds to reflect. + await new Promise(f => setTimeout(f, 5000)); - let balance = await getAddressBalance(client, addressBech32); - if (balance > 0) { - break; + let balance = await getAddressBalance(client, addressBech32); + if (balance > 0) { + break; + } } - } } /** Returns the balance of the given Bech32-encoded address. */ async function getAddressBalance(client: Client, addressBech32: string): Promise { - // TODO: use the `addresses/ed25519/` API to get the balance? - const outputIds = await client.basicOutputIds([ - { address: addressBech32 }, - { hasExpiration: false }, - { hasTimelock: false }, - { hasStorageDepositReturn: false } - ]); - const outputs = await client.getOutputs(outputIds); - - let totalAmount = 0; - for (const output of outputs) { - totalAmount += Number(output.output.amount); - } - - return totalAmount; + // TODO: use the `addresses/ed25519/` API to get the balance? + const outputIds = await client.basicOutputIds([ + { address: addressBech32 }, + { hasExpiration: false }, + { hasTimelock: false }, + { hasStorageDepositReturn: false }, + ]); + const outputs = await client.getOutputs(outputIds); + + let totalAmount = 0; + for (const output of outputs) { + totalAmount += Number(output.output.amount); + } + + return totalAmount; } /** Request tokens from the testnet faucet API. */ async function requestFundsFromFaucet(addressBech32: string) { - const requestObj = JSON.stringify({ address: addressBech32 }); - let errorMessage, data; - try { - const response = await fetch(FAUCET_ENDPOINT, { - method: "POST", - headers: { - Accept: "application/json", - "Content-Type": "application/json", - }, - body: requestObj, - }); - if (response.status === 202) { - errorMessage = "OK"; - } else if (response.status === 429) { - errorMessage = "too many requests, please try again later."; - } else { - data = await response.json(); - // @ts-ignore - errorMessage = data.error.message; + const requestObj = JSON.stringify({ address: addressBech32 }); + let errorMessage, data; + try { + const response = await fetch(FAUCET_ENDPOINT, { + method: "POST", + headers: { + Accept: "application/json", + "Content-Type": "application/json", + }, + body: requestObj, + }); + if (response.status === 202) { + errorMessage = "OK"; + } else if (response.status === 429) { + errorMessage = "too many requests, please try again later."; + } else { + data = await response.json(); + // @ts-ignore + errorMessage = data.error.message; + } + } catch (error) { + errorMessage = error; } - } catch (error) { - errorMessage = error; - } - if (errorMessage != "OK") { - throw new Error(`failed to get funds from faucet: ${errorMessage}`); - } + if (errorMessage != "OK") { + throw new Error(`failed to get funds from faucet: ${errorMessage}`); + } } diff --git a/bindings/wasm/lib/index.ts b/bindings/wasm/lib/index.ts index 5d81fd9b2d..de4fc96b24 100644 --- a/bindings/wasm/lib/index.ts +++ b/bindings/wasm/lib/index.ts @@ -1,6 +1,6 @@ // Copyright 2021-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -export * from './iota_identity_client'; +export * from "./iota_identity_client"; -export * from '~identity_wasm'; +export * from "~identity_wasm"; diff --git a/bindings/wasm/lib/iota_identity_client.ts b/bindings/wasm/lib/iota_identity_client.ts index 346f6e2efd..c1c93923a3 100644 --- a/bindings/wasm/lib/iota_identity_client.ts +++ b/bindings/wasm/lib/iota_identity_client.ts @@ -1,9 +1,8 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { IIotaIdentityClient, IotaDID, IotaDocument, IotaIdentityClientExt } from '~identity_wasm'; +import { IIotaIdentityClient, IotaDID, IotaDocument, IotaIdentityClientExt } from "~identity_wasm"; -import type { Client, INodeInfoWrapper, SecretManager } from '~iota-client-wasm'; import { ADDRESS_UNLOCK_CONDITION_TYPE, AddressTypes, @@ -12,8 +11,9 @@ import { IOutputResponse, IRent, IUTXOInput, - TransactionHelper -} from '@iota/iota.js'; + TransactionHelper, +} from "@iota/iota.js"; +import type { Client, INodeInfoWrapper, SecretManager } from "~iota-client-wasm"; /** Provides operations for IOTA DID Documents with Alias Outputs. */ export class IotaIdentityClient implements IIotaIdentityClient { @@ -133,7 +133,10 @@ export class IotaIdentityClient implements IIotaIdentityClient { async deleteDidOutput(secretManager: SecretManager, address: AddressTypes, did: IotaDID) { const networkHrp = await this.getNetworkHrp(); if (networkHrp !== did.networkStr()) { - throw new Error("deleteDidOutput: DID network mismatch, client expected `" + networkHrp + "`, DID network is `" + did.networkStr() + "`"); + throw new Error( + "deleteDidOutput: DID network mismatch, client expected `" + networkHrp + "`, DID network is `" + + did.networkStr() + "`", + ); } const aliasId: string = did.tag(); @@ -147,15 +150,15 @@ export class IotaIdentityClient implements IIotaIdentityClient { unlockConditions: [ { type: ADDRESS_UNLOCK_CONDITION_TYPE, - address: address - } + address: address, + }, ], - }) + }); // Publish block. const [blockId, _block] = await this.client.buildAndPostBlock(secretManager, { inputs: [aliasInput], - outputs: [basicOutput] + outputs: [basicOutput], }); await this.client.retryUntilIncluded(blockId); } diff --git a/bindings/wasm/tests/core.ts b/bindings/wasm/tests/core.ts index 1937ba2021..dada813c54 100644 --- a/bindings/wasm/tests/core.ts +++ b/bindings/wasm/tests/core.ts @@ -1,6 +1,6 @@ export {}; -const assert = require('assert'); +const assert = require("assert"); const { CoreDID, CoreDocument, @@ -14,11 +14,12 @@ const { const VALID_DID_KEY = "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"; const VALID_DID_EXAMPLE = "did:example:123"; -const KEY_BYTES = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32]); +const KEY_BYTES = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32]); -describe('CoreDID', function () { - describe('#parse', function () { - it('iota', () => { +describe("CoreDID", function() { + describe("#parse", function() { + it("iota", () => { let tag = "0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"; const didStr = "did:iota:smr:" + tag; const did = CoreDID.parse(didStr); @@ -28,7 +29,7 @@ describe('CoreDID', function () { assert.deepStrictEqual(did.methodId(), "smr:" + tag); assert.deepStrictEqual(did.scheme(), "did"); }); - it('key', () => { + it("key", () => { const tag = "z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"; const didStr = "did:key:" + tag; const did = CoreDID.parse(didStr); @@ -38,7 +39,7 @@ describe('CoreDID', function () { assert.deepStrictEqual(did.methodId(), tag); assert.deepStrictEqual(did.scheme(), "did"); }); - it('example', () => { + it("example", () => { const tag = "123"; const didStr = VALID_DID_EXAMPLE; const did = CoreDID.parse(didStr); @@ -49,8 +50,8 @@ describe('CoreDID', function () { assert.deepStrictEqual(did.scheme(), "did"); }); }); - describe('#setMethodId', function () { - it('should work', () => { + describe("#setMethodId", function() { + it("should work", () => { let didStr = "did:example:network:123"; const did = CoreDID.parse(didStr); did.setMethodId("abc"); @@ -60,8 +61,8 @@ describe('CoreDID', function () { assert.deepStrictEqual(did.methodId(), "abc"); }); }); - describe('#validMethodId', function () { - it('should work', () => { + describe("#validMethodId", function() { + it("should work", () => { // Valid assert.deepStrictEqual(CoreDID.validMethodId("abc"), true); assert.deepStrictEqual(CoreDID.validMethodId("network:123"), true); @@ -71,8 +72,8 @@ describe('CoreDID', function () { assert.deepStrictEqual(CoreDID.validMethodId("abc[brackets]"), false); }); }); - describe('#setMethodName', function () { - it('should work', () => { + describe("#setMethodName", function() { + it("should work", () => { let didStr = "did:example:network:123"; const did = CoreDID.parse(didStr); did.setMethodName("other"); @@ -82,8 +83,8 @@ describe('CoreDID', function () { assert.deepStrictEqual(did.methodId(), "network:123"); }); }); - describe('#validMethodName', function () { - it('should work', () => { + describe("#validMethodName", function() { + it("should work", () => { // Valid assert.deepStrictEqual(CoreDID.validMethodId("abc"), true); assert.deepStrictEqual(CoreDID.validMethodId("example"), true); @@ -95,9 +96,9 @@ describe('CoreDID', function () { }); }); -describe('CoreDocument', function () { - describe('#new', function () { - it('minimal should work', () => { +describe("CoreDocument", function() { + describe("#new", function() { + it("minimal should work", () => { const doc = new CoreDocument({ id: VALID_DID_EXAMPLE, }); @@ -114,13 +115,18 @@ describe('CoreDocument', function () { assert.deepStrictEqual(doc.service(), []); assert.deepStrictEqual(doc.properties(), new Map()); }); - it('full should work', () => { + it("full should work", () => { const did = CoreDID.parse(VALID_DID_EXAMPLE); const method0 = new CoreVerificationMethod(did, KeyType.Ed25519, KEY_BYTES, "key-0"); const method1 = new CoreVerificationMethod(did, KeyType.Ed25519, KEY_BYTES, "key-1"); - const method2 = new CoreVerificationMethod(CoreDID.parse(VALID_DID_EXAMPLE), KeyType.Ed25519, KEY_BYTES, "key-2"); + const method2 = new CoreVerificationMethod( + CoreDID.parse(VALID_DID_EXAMPLE), + KeyType.Ed25519, + KEY_BYTES, + "key-2", + ); const service = new CoreService({ - id: did.join('#service-1'), + id: did.join("#service-1"), type: "LinkedDomains", serviceEndpoint: "https://example.com/", }); @@ -140,7 +146,10 @@ describe('CoreDocument', function () { custom2: 1234, }); assert.deepStrictEqual(doc.id().toString(), did.toString()); - assert.deepStrictEqual(doc.controller().map((item: any) => item.toString()), [VALID_DID_KEY, VALID_DID_EXAMPLE]); + assert.deepStrictEqual(doc.controller().map((item: any) => item.toString()), [ + VALID_DID_KEY, + VALID_DID_EXAMPLE, + ]); assert.deepStrictEqual(doc.alsoKnownAs(), [VALID_DID_KEY]); assert.deepStrictEqual(doc.verificatonMethod().length, 2); assert.deepStrictEqual(doc.verificatonMethod()[0].toJSON(), method0.toJSON()); @@ -164,14 +173,14 @@ describe('CoreDocument', function () { assert.deepStrictEqual(doc.methods()[2].toJSON(), method2.toJSON()); assert.deepStrictEqual(doc.service().length, 1); assert.deepStrictEqual(doc.service()[0].toJSON(), service.toJSON()); - const properties = new Map() + const properties = new Map(); properties.set("custom1", "asdf"); properties.set("custom2", 1234); assert.deepStrictEqual(doc.properties(), properties); }); }); - describe('#insert/resolve/removeMethod', function () { - it('should work', async () => { + describe("#insert/resolve/removeMethod", function() { + it("should work", async () => { const doc = new CoreDocument({ id: VALID_DID_EXAMPLE, }); @@ -201,19 +210,25 @@ describe('CoreDocument', function () { assert.deepStrictEqual(doc.methods().length, 0); }); }); - describe('#attach/detachMethodRelationship', function () { - it('should work', async () => { + describe("#attach/detachMethodRelationship", function() { + it("should work", async () => { const doc = new CoreDocument({ id: VALID_DID_EXAMPLE, }); const fragment = "new-method-1"; const method = new CoreVerificationMethod(doc.id(), KeyType.Ed25519, KEY_BYTES, fragment); doc.insertMethod(method, MethodScope.VerificationMethod()); - assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.VerificationMethod()).toJSON(), method.toJSON()); + assert.deepStrictEqual( + doc.resolveMethod(fragment, MethodScope.VerificationMethod()).toJSON(), + method.toJSON(), + ); // Attach. doc.attachMethodRelationship(method.id(), MethodRelationship.Authentication); - assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.VerificationMethod()).toJSON(), method.toJSON()); + assert.deepStrictEqual( + doc.resolveMethod(fragment, MethodScope.VerificationMethod()).toJSON(), + method.toJSON(), + ); assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.Authentication()).toJSON(), method.toJSON()); assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.AssertionMethod()), undefined); assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.CapabilityInvocation()), undefined); @@ -222,7 +237,10 @@ describe('CoreDocument', function () { // Detach. doc.detachMethodRelationship(method.id(), MethodRelationship.Authentication); - assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.VerificationMethod()).toJSON(), method.toJSON()); + assert.deepStrictEqual( + doc.resolveMethod(fragment, MethodScope.VerificationMethod()).toJSON(), + method.toJSON(), + ); assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.Authentication()), undefined); assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.AssertionMethod()), undefined); assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.CapabilityInvocation()), undefined); @@ -230,16 +248,16 @@ describe('CoreDocument', function () { assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.KeyAgreement()), undefined); }); }); - describe('#insert/resolve/removeService', function () { - it('should work', async () => { + describe("#insert/resolve/removeService", function() { + it("should work", async () => { const doc = new CoreDocument({ id: VALID_DID_EXAMPLE, }); - + // Add. const fragment1 = "new-service-1"; const service = new CoreService({ - id: doc.id().toUrl().join('#' + fragment1), + id: doc.id().toUrl().join("#" + fragment1), type: ["LinkedDomains", "ExampleType"], serviceEndpoint: ["https://example.com/", "https://iota.org/"], }); @@ -261,14 +279,14 @@ describe('CoreDocument', function () { assert.deepStrictEqual(doc.service().length, 0); }); }); - describe('#properties', function () { - it('should work', () => { + describe("#properties", function() { + it("should work", () => { const doc = new CoreDocument({ id: VALID_DID_EXAMPLE, }); assert.deepStrictEqual(doc.properties(), new Map()); - const properties = new Map() + const properties = new Map(); properties.set("custom1", "asdf"); properties.set("custom2", 1234); doc.setPropertyUnchecked("custom1", "asdf"); diff --git a/bindings/wasm/tests/credentials.ts b/bindings/wasm/tests/credentials.ts index 0f12dc3ad5..3e14b50abb 100644 --- a/bindings/wasm/tests/credentials.ts +++ b/bindings/wasm/tests/credentials.ts @@ -1,6 +1,6 @@ export {}; -const assert = require('assert'); +const assert = require("assert"); const { Credential, CredentialValidator, @@ -31,23 +31,23 @@ const credentialFields = { id: "did:example:ebfeb1f712ebc6f1c276e12ec21", degree: { type: "BachelorDegree", - name: "Bachelor of Science and Arts" - } + name: "Bachelor of Science and Arts", + }, }, issuer: "https://example.edu/issuers/565049", issuanceDate: "2010-01-01T00:00:00Z", expirationDate: "2020-01-01T19:23:24Z", credentialStatus: { id: "https://example.edu/status/24", - type: "CredentialStatusList2017" + type: "CredentialStatusList2017", }, credentialSchema: { id: "https://example.org/examples/degree.json", - type: "JsonSchemaValidator2018" + type: "JsonSchemaValidator2018", }, refreshService: { id: "https://example.edu/refresh/3732", - type: "ManualRefreshService2018" + type: "ManualRefreshService2018", }, termsOfUse: { type: "IssuerPolicy", @@ -57,8 +57,8 @@ const credentialFields = { assigner: "https://example.edu/issuers/14", assignee: "AllVerifiers", target: "https://example.edu/credentials/3732", - action: ["Archival"] - }] + action: ["Archival"], + }], }, evidence: { id: "https://example.edu/evidence/f2aeec97-fc0d-42bf-8ca7-0548192d4231", @@ -67,16 +67,16 @@ const credentialFields = { evidenceDocument: "DriversLicense", subjectPresence: "Physical", documentPresence: "Physical", - licenseNumber: "123AB4567" + licenseNumber: "123AB4567", }, nonTransferable: true, custom1: "asdf", - custom2: 1234 + custom2: 1234, }; -describe('Credential', function () { - describe('#new and field getters', function () { - it('should work', async () => { +describe("Credential", function() { + describe("#new and field getters", function() { + it("should work", async () => { const credential = new Credential(credentialFields); assert.deepStrictEqual(credential.context(), [Credential.BaseContext(), credentialFields.context]); assert.deepStrictEqual(credential.id(), credentialFields.id); @@ -91,7 +91,7 @@ describe('Credential', function () { assert.deepStrictEqual(credential.termsOfUse(), [credentialFields.termsOfUse]); assert.deepStrictEqual(credential.evidence(), [credentialFields.evidence]); assert.deepStrictEqual(credential.nonTransferable(), credentialFields.nonTransferable); - const properties = new Map() + const properties = new Map(); properties.set("custom1", "asdf"); properties.set("custom2", 1234); assert.deepStrictEqual(credential.properties(), properties); @@ -105,23 +105,23 @@ const presentationFields = { id: "urn:uuid:3978344f-8596-4c3a-a978-8fcaba3903c5", type: "CredentialManagerPresentation", verifiableCredential: Credential.fromJSON({ - '@context': ["https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1"], + "@context": ["https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1"], id: "https://example.edu/credentials/3732", type: ["VerifiableCredential", "UniversityDegreeCredential"], credentialSubject: { id: "did:example:ebfeb1f712ebc6f1c276e12ec21", degree: { type: "BachelorDegree", - name: "Bachelor of Science and Arts" - } + name: "Bachelor of Science and Arts", + }, }, issuer: "https://example.edu/issuers/565049", - issuanceDate: "2010-01-01T00:00:00Z" + issuanceDate: "2010-01-01T00:00:00Z", }), holder: "did:example:1234", refreshService: { id: "https://example.edu/refresh/3732", - type: "ManualRefreshService2018" + type: "ManualRefreshService2018", }, termsOfUse: { type: "IssuerPolicy", @@ -131,25 +131,28 @@ const presentationFields = { assigner: "https://example.edu/issuers/14", assignee: "AllVerifiers", target: "https://example.edu/credentials/3732", - action: ["Archival"] - }] + action: ["Archival"], + }], }, custom1: "asdf", - custom2: 1234 + custom2: 1234, }; -describe('Presentation', function () { - describe('#new and field getters', function () { - it('should work', async () => { +describe("Presentation", function() { + describe("#new and field getters", function() { + it("should work", async () => { const presentation = new Presentation(presentationFields); assert.deepStrictEqual(presentation.context(), [Presentation.BaseContext(), presentationFields.context]); assert.deepStrictEqual(presentation.id(), presentationFields.id); assert.deepStrictEqual(presentation.type(), [Presentation.BaseType(), presentationFields.type]); - assert.deepStrictEqual(presentation.verifiableCredential()[0].toJSON(), presentationFields.verifiableCredential.toJSON()); + assert.deepStrictEqual( + presentation.verifiableCredential()[0].toJSON(), + presentationFields.verifiableCredential.toJSON(), + ); assert.deepStrictEqual(presentation.holder(), presentationFields.holder); assert.deepStrictEqual(presentation.refreshService(), [presentationFields.refreshService]); assert.deepStrictEqual(presentation.termsOfUse(), [presentationFields.termsOfUse]); - const properties = new Map() + const properties = new Map(); properties.set("custom1", "asdf"); properties.set("custom2", 1234); assert.deepStrictEqual(presentation.properties(), properties); @@ -159,25 +162,33 @@ describe('Presentation', function () { }); // Test the duck-typed interfaces for PresentationValidator and CredentialValidator. -describe('CredentialValidator, PresentationValidator', function () { - describe('#validate()', function () { - it('should work', async () => { +describe("CredentialValidator, PresentationValidator", function() { + describe("#validate()", function() { + it("should work", async () => { // Set up issuer & subject DID documents. const issuerDoc = new IotaDocument("iota"); const issuerKeys = new KeyPair(KeyType.Ed25519); - issuerDoc.insertMethod(new IotaVerificationMethod(issuerDoc.id(), KeyType.Ed25519, issuerKeys.public(), "#iss-0"), MethodScope.VerificationMethod()); + issuerDoc.insertMethod( + new IotaVerificationMethod(issuerDoc.id(), KeyType.Ed25519, issuerKeys.public(), "#iss-0"), + MethodScope.VerificationMethod(), + ); // Add RevocationBitmap service. const revocationBitmap = new RevocationBitmap(); - issuerDoc.insertService(new IotaService({ - id: issuerDoc.id().join("#my-revocation-service"), - type: RevocationBitmap.type(), - serviceEndpoint: revocationBitmap.toEndpoint() - })) + issuerDoc.insertService( + new IotaService({ + id: issuerDoc.id().join("#my-revocation-service"), + type: RevocationBitmap.type(), + serviceEndpoint: revocationBitmap.toEndpoint(), + }), + ); const subjectDoc = new IotaDocument("iota"); const subjectKeys = new KeyPair(KeyType.Ed25519); - subjectDoc.insertMethod(new IotaVerificationMethod(subjectDoc.id(), KeyType.Ed25519, subjectKeys.public(), "#sub-0"), MethodScope.VerificationMethod()); + subjectDoc.insertMethod( + new IotaVerificationMethod(subjectDoc.id(), KeyType.Ed25519, subjectKeys.public(), "#sub-0"), + MethodScope.VerificationMethod(), + ); const subjectDID = subjectDoc.id(); const issuerDID = issuerDoc.id(); @@ -186,7 +197,7 @@ describe('CredentialValidator, PresentationValidator', function () { name: "Alice", degreeName: "Bachelor of Science and Arts", degreeType: "BachelorDegree", - GPA: "4.0" + GPA: "4.0", }; const credential = new Credential({ id: "https://example.edu/credentials/3732", @@ -195,37 +206,88 @@ describe('CredentialValidator, PresentationValidator', function () { credentialStatus: { id: issuerDoc.id() + "#my-revocation-service", type: RevocationBitmap.type(), - revocationBitmapIndex: "5" + revocationBitmapIndex: "5", }, - credentialSubject: subject + credentialSubject: subject, }); // Sign the credential with the issuer's DID Document. - const signedCredential = issuerDoc.signCredential(credential, issuerKeys.private(), "#iss-0", ProofOptions.default()); + const signedCredential = issuerDoc.signCredential( + credential, + issuerKeys.private(), + "#iss-0", + ProofOptions.default(), + ); // Validate the credential. assert.doesNotThrow(() => CredentialValidator.checkStructure(signedCredential)); assert.doesNotThrow(() => CredentialValidator.checkExpiresOnOrAfter(signedCredential, Timestamp.nowUTC())); assert.doesNotThrow(() => CredentialValidator.checkIssuedOnOrBefore(signedCredential, Timestamp.nowUTC())); - assert.doesNotThrow(() => CredentialValidator.checkSubjectHolderRelationship(signedCredential, subjectDID.toString(), SubjectHolderRelationship.AlwaysSubject)); - assert.doesNotThrow(() => CredentialValidator.checkStatus(signedCredential, [issuerDoc], StatusCheck.Strict)); - assert.doesNotThrow(() => CredentialValidator.verifySignature(signedCredential, [issuerDoc, subjectDoc], VerifierOptions.default())); - assert.doesNotThrow(() => CredentialValidator.validate(signedCredential, issuerDoc, CredentialValidationOptions.default(), FailFast.FirstError)); - assert.deepStrictEqual(CredentialValidator.extractIssuer(signedCredential).toString(), issuerDID.toString()); + assert.doesNotThrow(() => + CredentialValidator.checkSubjectHolderRelationship( + signedCredential, + subjectDID.toString(), + SubjectHolderRelationship.AlwaysSubject, + ) + ); + assert.doesNotThrow(() => + CredentialValidator.checkStatus(signedCredential, [issuerDoc], StatusCheck.Strict) + ); + assert.doesNotThrow(() => + CredentialValidator.verifySignature( + signedCredential, + [issuerDoc, subjectDoc], + VerifierOptions.default(), + ) + ); + assert.doesNotThrow(() => + CredentialValidator.validate( + signedCredential, + issuerDoc, + CredentialValidationOptions.default(), + FailFast.FirstError, + ) + ); + assert.deepStrictEqual( + CredentialValidator.extractIssuer(signedCredential).toString(), + issuerDID.toString(), + ); // Construct a presentation. const presentation = new Presentation({ id: "https://example.org/credentials/3732", holder: subjectDID.toString(), - verifiableCredential: signedCredential + verifiableCredential: signedCredential, }); - const signedPresentation = subjectDoc.signPresentation(presentation, subjectKeys.private(), "#sub-0", ProofOptions.default()); + const signedPresentation = subjectDoc.signPresentation( + presentation, + subjectKeys.private(), + "#sub-0", + ProofOptions.default(), + ); // Validate the presentation. assert.doesNotThrow(() => PresentationValidator.checkStructure(signedPresentation)); - assert.doesNotThrow(() => PresentationValidator.verifyPresentationSignature(signedPresentation, subjectDoc, VerifierOptions.default())); - assert.doesNotThrow(() => PresentationValidator.validate(signedPresentation, subjectDoc, [issuerDoc], PresentationValidationOptions.default(), FailFast.FirstError)); - assert.deepStrictEqual(PresentationValidator.extractHolder(signedPresentation).toString(), subjectDID.toString()); + assert.doesNotThrow(() => + PresentationValidator.verifyPresentationSignature( + signedPresentation, + subjectDoc, + VerifierOptions.default(), + ) + ); + assert.doesNotThrow(() => + PresentationValidator.validate( + signedPresentation, + subjectDoc, + [issuerDoc], + PresentationValidationOptions.default(), + FailFast.FirstError, + ) + ); + assert.deepStrictEqual( + PresentationValidator.extractHolder(signedPresentation).toString(), + subjectDID.toString(), + ); }); }); }); diff --git a/bindings/wasm/tests/iota.ts b/bindings/wasm/tests/iota.ts index 2d9e71ec0b..726b16cc11 100644 --- a/bindings/wasm/tests/iota.ts +++ b/bindings/wasm/tests/iota.ts @@ -1,6 +1,6 @@ -export { }; +export {}; -const assert = require('assert'); +const assert = require("assert"); const { Duration, KeyType, @@ -14,13 +14,14 @@ const { Timestamp, } = require("../node"); -const aliasIdBytes = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32]); +const aliasIdBytes = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32]); const aliasIdHex = "0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"; const networkName = "smr"; -describe('IotaDID', function () { - describe('#constructor', function () { - it('should work', () => { +describe("IotaDID", function() { + describe("#constructor", function() { + it("should work", () => { const did = new IotaDID(aliasIdBytes, networkName); assert.deepStrictEqual(did.toString(), "did:" + IotaDID.METHOD + ":" + networkName + ":" + aliasIdHex); assert.deepStrictEqual(did.tag(), aliasIdHex); @@ -31,8 +32,8 @@ describe('IotaDID', function () { assert.deepStrictEqual(did.scheme(), "did"); }); }); - describe('#placeholder()', function () { - it('should be zeroes', () => { + describe("#placeholder()", function() { + it("should be zeroes", () => { const expectedTag = "0x0000000000000000000000000000000000000000000000000000000000000000"; const did = IotaDID.placeholder(networkName); assert.deepStrictEqual(did.toString(), "did:" + IotaDID.METHOD + ":" + networkName + ":" + expectedTag); @@ -46,20 +47,20 @@ describe('IotaDID', function () { }); }); -describe('IotaDocument', function () { - describe('#constructors', function () { - it('new should generate a placeholder', () => { +describe("IotaDocument", function() { + describe("#constructors", function() { + it("new should generate a placeholder", () => { const doc = new IotaDocument(networkName); assert.deepStrictEqual(doc.id().toString(), IotaDID.placeholder(networkName).toString()); }); - it('newWithId should work', () => { + it("newWithId should work", () => { const did = new IotaDID(aliasIdBytes, networkName); const doc = IotaDocument.newWithId(did); assert.deepStrictEqual(doc.id().toString(), did.toString()); }); }); - describe('#insert/resolve/removeMethod', function () { - it('should work', async () => { + describe("#insert/resolve/removeMethod", function() { + it("should work", async () => { const doc = new IotaDocument(networkName); const fragment = "new-method-1"; const scope = MethodScope.AssertionMethod(); @@ -87,17 +88,23 @@ describe('IotaDocument', function () { assert.deepStrictEqual(doc.methods().length, 0); }); }); - describe('#attach/detachMethodRelationship', function () { - it('should work', async () => { + describe("#attach/detachMethodRelationship", function() { + it("should work", async () => { const doc = new IotaDocument(networkName); const fragment = "new-method-1"; const method = new IotaVerificationMethod(doc.id(), KeyType.Ed25519, aliasIdBytes, fragment); doc.insertMethod(method, MethodScope.VerificationMethod()); - assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.VerificationMethod()).toJSON(), method.toJSON()); + assert.deepStrictEqual( + doc.resolveMethod(fragment, MethodScope.VerificationMethod()).toJSON(), + method.toJSON(), + ); // Attach. doc.attachMethodRelationship(method.id(), MethodRelationship.Authentication); - assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.VerificationMethod()).toJSON(), method.toJSON()); + assert.deepStrictEqual( + doc.resolveMethod(fragment, MethodScope.VerificationMethod()).toJSON(), + method.toJSON(), + ); assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.Authentication()).toJSON(), method.toJSON()); assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.AssertionMethod()), undefined); assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.CapabilityInvocation()), undefined); @@ -106,7 +113,10 @@ describe('IotaDocument', function () { // Detach. doc.detachMethodRelationship(method.id(), MethodRelationship.Authentication); - assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.VerificationMethod()).toJSON(), method.toJSON()); + assert.deepStrictEqual( + doc.resolveMethod(fragment, MethodScope.VerificationMethod()).toJSON(), + method.toJSON(), + ); assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.Authentication()), undefined); assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.AssertionMethod()), undefined); assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.CapabilityInvocation()), undefined); @@ -114,14 +124,14 @@ describe('IotaDocument', function () { assert.deepStrictEqual(doc.resolveMethod(fragment, MethodScope.KeyAgreement()), undefined); }); }); - describe('#insert/resolve/removeService', function () { - it('should work', async () => { + describe("#insert/resolve/removeService", function() { + it("should work", async () => { const doc = new IotaDocument(networkName); // Add. const fragment1 = "new-service-1"; const service = new IotaService({ - id: doc.id().toUrl().join('#' + fragment1), + id: doc.id().toUrl().join("#" + fragment1), type: ["LinkedDomains", "ExampleType"], serviceEndpoint: ["https://example.com/", "https://iota.org/"], }); @@ -143,8 +153,8 @@ describe('IotaDocument', function () { assert.deepStrictEqual(doc.service().length, 0); }); }); - describe('#metadata', function () { - it('should work', () => { + describe("#metadata", function() { + it("should work", () => { const doc = new IotaDocument(networkName); const previousCreated = doc.metadataCreated(); const previousUpdated = doc.metadataUpdated(); @@ -173,7 +183,7 @@ describe('IotaDocument', function () { assert.deepStrictEqual(doc.metadataDeactivated(), undefined); // Properties. assert.deepStrictEqual(doc.metadata().properties(), new Map()); - const properties = new Map() + const properties = new Map(); properties.set("custom1", "asdf"); properties.set("custom2", 1234); doc.setMetadataPropertyUnchecked("custom1", "asdf"); @@ -181,12 +191,12 @@ describe('IotaDocument', function () { assert.deepStrictEqual(doc.metadata().properties(), properties); }); }); - describe('#properties', function () { - it('should work', () => { + describe("#properties", function() { + it("should work", () => { const doc = new IotaDocument(networkName); assert.deepStrictEqual(doc.properties(), new Map()); - const properties = new Map() + const properties = new Map(); properties.set("custom1", "asdf"); properties.set("custom2", 1234); doc.setPropertyUnchecked("custom1", "asdf"); diff --git a/bindings/wasm/tests/legacy/account.ts b/bindings/wasm/tests/legacy/account.ts index 7a22be6f36..a3ba9c5ca4 100644 --- a/bindings/wasm/tests/legacy/account.ts +++ b/bindings/wasm/tests/legacy/account.ts @@ -1,8 +1,8 @@ // TODO: Remove or reuse depending on what we do with the account. // Note that this test is not executed as long as it sits in the legacy directory. -export { }; +export {}; -const assert = require('assert'); +const assert = require("assert"); const { AccountBuilder, AutoSave, @@ -19,7 +19,7 @@ function setupAccountBuilder() { autopublish: false, clientConfig: { nodeSyncDisabled: true, - } + }, }); } @@ -27,12 +27,14 @@ async function setupAccount() { return await setupAccountBuilder().createIdentity(); } -const privateKeyBytes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32]; -const ed25519PublicKeyBytes = [121, 181, 86, 46, 143, 230, 84, 249, 64, 120, 177, 18, 232, 169, 139, 167, 144, 31, 133, 58, 230, 149, 190, 215, 224, 227, 145, 11, 173, 4, 150, 100]; +const privateKeyBytes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32]; +const ed25519PublicKeyBytes = [121, 181, 86, 46, 143, 230, 84, 249, 64, 120, 177, 18, 232, 169, 139, 167, 144, 31, 133, + 58, 230, 149, 190, 215, 224, 227, 145, 11, 173, 4, 150, 100]; -describe('AccountBuilder', function () { - describe('#createIdentity()', function () { - it('should deserialize privateKey Uint8Array correctly', async () => { +describe("AccountBuilder", function() { + describe("#createIdentity()", function() { + it("should deserialize privateKey Uint8Array correctly", async () => { const builder = setupAccountBuilder(); const privateKey = new Uint8Array(privateKeyBytes); const account = await builder.createIdentity({ @@ -43,9 +45,9 @@ describe('AccountBuilder', function () { }); }); -describe('Account', function () { - describe('#createMethod()', function () { - it('should deserialize MethodContent privateKey Uint8Array correctly', async () => { +describe("Account", function() { + describe("#createMethod()", function() { + it("should deserialize MethodContent privateKey Uint8Array correctly", async () => { const account = await setupAccount(); // Test hard-coded private key. @@ -72,7 +74,7 @@ describe('Account', function () { assert.equal(method2.type().toString(), MethodType.X25519KeyAgreementKey2019().toString()); assert.equal(method2.data().tryDecode().toString(), keypair.public().toString()); }); - it('should deserialize MethodContent publicKey Uint8Array correctly', async () => { + it("should deserialize MethodContent publicKey Uint8Array correctly", async () => { const account = await setupAccount(); // Test hard-coded public key. @@ -100,8 +102,8 @@ describe('Account', function () { assert.equal(method2.data().tryDecode().toString(), keypair.public().toString()); }); }); - describe('#createService()', function () { - it('should take one type and endpoint', async () => { + describe("#createService()", function() { + it("should take one type and endpoint", async () => { const account = await setupAccount(); // Test single type & endpoint. @@ -109,14 +111,14 @@ describe('Account', function () { await account.createService({ fragment: fragment1, type: "LinkedDomains", - endpoint: "https://example.com/" + endpoint: "https://example.com/", }); const service = account.document().resolveService(fragment1); assert.deepStrictEqual(service.id().fragment(), fragment1); assert.deepStrictEqual(service.type(), ["LinkedDomains"]); assert.deepStrictEqual(service.serviceEndpoint(), "https://example.com/"); }); - it('should take multiple types and endpoints', async () => { + it("should take multiple types and endpoints", async () => { const account = await setupAccount(); // Test multiple types & endpoints. @@ -124,7 +126,7 @@ describe('Account', function () { await account.createService({ fragment: fragment1, type: ["LinkedDomains", "ExampleType"], - endpoint: ["https://example.com/", "https://iota.org/"] + endpoint: ["https://example.com/", "https://iota.org/"], }); const service = account.document().resolveService(fragment1); assert.deepStrictEqual(service.id().fragment(), fragment1); diff --git a/bindings/wasm/tests/resolver.ts b/bindings/wasm/tests/resolver.ts index 772ebe90a0..59ad8f1e34 100644 --- a/bindings/wasm/tests/resolver.ts +++ b/bindings/wasm/tests/resolver.ts @@ -1,167 +1,171 @@ export {}; import { - CoreDocument, - IotaDID, - Resolver, - Presentation, - IotaDocument, - CoreDID, - FailFast, - PresentationValidationOptions + CoreDID, + CoreDocument, + FailFast, + IotaDID, + IotaDocument, + Presentation, + PresentationValidationOptions, + Resolver, } from "../node"; import assert = require("assert"); const presentationJSON = require("../../../identity_credential/tests/fixtures/signed_presentation/presentation.json"); -const issuerIotaDocJSON = require("../../../identity_credential/tests/fixtures/signed_presentation/issuer_iota_doc.json"); +const issuerIotaDocJSON = require( + "../../../identity_credential/tests/fixtures/signed_presentation/issuer_iota_doc.json", +); const issuerBarDocJSON = require("../../../identity_credential/tests/fixtures/signed_presentation/issuer_bar_doc.json"); -const holderFooDocJSON = require("../../../identity_credential/tests/fixtures/signed_presentation/subject_foo_doc.json"); +const holderFooDocJSON = require( + "../../../identity_credential/tests/fixtures/signed_presentation/subject_foo_doc.json", +); const presentation = Presentation.fromJSON(presentationJSON); const holderFooDoc = CoreDocument.fromJSON(holderFooDocJSON); const issuerIotaDoc: IotaDocument = IotaDocument.fromJSON(issuerIotaDocJSON); const issuerBarDoc: CoreDocument = CoreDocument.fromJSON(issuerBarDocJSON); -describe("Resolver", function () { - describe("#verifyPresentation", function () { - it("should accept a correct presentation when configured correctly", async () => { - // mock method handlers - const resolveDidIota = async function (did_input: string) { - const parsedDid: IotaDID = IotaDID.parse(did_input); - if (issuerIotaDoc.id().toString() == parsedDid.toString()) { - return issuerIotaDoc; - } else { - throw new Error(`could not resolve did ${did_input}`); - } - }; - - const resolveDidFoo = async function (did_input: string) { - const parsedDid: CoreDID = CoreDID.parse(did_input); - if (holderFooDoc.id().toString() == parsedDid.toString()) { - return holderFooDoc; - } else { - throw new Error(`could not resolve did ${did_input}`); - } - }; - - const resolveDidBar = async function (did_input: string) { - const parsedDid: CoreDID = CoreDID.parse(did_input); - if (issuerBarDoc.id().toString() == parsedDid.toString()) { - return issuerBarDoc; - } else { - throw new Error(`could not resolve did ${did_input}`); - } - }; - - let handlerMap: Map Promise> = new Map(); - handlerMap.set("iota", resolveDidIota); - handlerMap.set("foo", resolveDidFoo); - handlerMap.set("bar", resolveDidBar); - - const resolver = new Resolver({ - handlers: handlerMap - }); - - const resolvedHolderDoc = await resolver.resolvePresentationHolder(presentation); - assert(resolvedHolderDoc instanceof CoreDocument); - - const resolvedIssuerDocuments = await resolver.resolvePresentationIssuers(presentation); - - assert(resolvedIssuerDocuments instanceof Array); - - let verificationResultPassingHolderDoc = await resolver.verifyPresentation( - presentation, - PresentationValidationOptions.default(), - FailFast.FirstError, - resolvedHolderDoc, - undefined - ); - assert.equal(verificationResultPassingHolderDoc, undefined); - - let verificationResultPassingHolderAndIssuerDocuments = await resolver.verifyPresentation( - presentation, - PresentationValidationOptions.default(), - FailFast.FirstError, - resolvedHolderDoc, - resolvedIssuerDocuments - ); - assert.equal(verificationResultPassingHolderAndIssuerDocuments, undefined); - - let verificationResultPassingIssuerDocuments = await resolver.verifyPresentation( - presentation, - PresentationValidationOptions.default(), - FailFast.FirstError, - undefined, - resolvedIssuerDocuments - ); - assert.equal(verificationResultPassingIssuerDocuments, undefined); - - let verificationResultPassingNoDocuments = await resolver.verifyPresentation( - presentation, - PresentationValidationOptions.default(), - FailFast.FirstError, - undefined, - undefined - ); - assert.equal(verificationResultPassingNoDocuments, undefined); - - // passing the wrong document should throw an error - assert.notEqual(resolvedHolderDoc, resolvedIssuerDocuments[0]); - - try { - let result = await resolver.verifyPresentation( - presentation, - PresentationValidationOptions.default(), - FailFast.FirstError, - resolvedIssuerDocuments[0], - undefined - ); - } catch (e) { - return; - } - throw new Error("no error thrown when passing incorrect holder"); +describe("Resolver", function() { + describe("#verifyPresentation", function() { + it("should accept a correct presentation when configured correctly", async () => { + // mock method handlers + const resolveDidIota = async function(did_input: string) { + const parsedDid: IotaDID = IotaDID.parse(did_input); + if (issuerIotaDoc.id().toString() == parsedDid.toString()) { + return issuerIotaDoc; + } else { + throw new Error(`could not resolve did ${did_input}`); + } + }; + + const resolveDidFoo = async function(did_input: string) { + const parsedDid: CoreDID = CoreDID.parse(did_input); + if (holderFooDoc.id().toString() == parsedDid.toString()) { + return holderFooDoc; + } else { + throw new Error(`could not resolve did ${did_input}`); + } + }; + + const resolveDidBar = async function(did_input: string) { + const parsedDid: CoreDID = CoreDID.parse(did_input); + if (issuerBarDoc.id().toString() == parsedDid.toString()) { + return issuerBarDoc; + } else { + throw new Error(`could not resolve did ${did_input}`); + } + }; + + let handlerMap: Map Promise> = new Map(); + handlerMap.set("iota", resolveDidIota); + handlerMap.set("foo", resolveDidFoo); + handlerMap.set("bar", resolveDidBar); + + const resolver = new Resolver({ + handlers: handlerMap, + }); + + const resolvedHolderDoc = await resolver.resolvePresentationHolder(presentation); + assert(resolvedHolderDoc instanceof CoreDocument); + + const resolvedIssuerDocuments = await resolver.resolvePresentationIssuers(presentation); + + assert(resolvedIssuerDocuments instanceof Array); + + let verificationResultPassingHolderDoc = await resolver.verifyPresentation( + presentation, + PresentationValidationOptions.default(), + FailFast.FirstError, + resolvedHolderDoc, + undefined, + ); + assert.equal(verificationResultPassingHolderDoc, undefined); + + let verificationResultPassingHolderAndIssuerDocuments = await resolver.verifyPresentation( + presentation, + PresentationValidationOptions.default(), + FailFast.FirstError, + resolvedHolderDoc, + resolvedIssuerDocuments, + ); + assert.equal(verificationResultPassingHolderAndIssuerDocuments, undefined); + + let verificationResultPassingIssuerDocuments = await resolver.verifyPresentation( + presentation, + PresentationValidationOptions.default(), + FailFast.FirstError, + undefined, + resolvedIssuerDocuments, + ); + assert.equal(verificationResultPassingIssuerDocuments, undefined); + + let verificationResultPassingNoDocuments = await resolver.verifyPresentation( + presentation, + PresentationValidationOptions.default(), + FailFast.FirstError, + undefined, + undefined, + ); + assert.equal(verificationResultPassingNoDocuments, undefined); + + // passing the wrong document should throw an error + assert.notEqual(resolvedHolderDoc, resolvedIssuerDocuments[0]); + + try { + let result = await resolver.verifyPresentation( + presentation, + PresentationValidationOptions.default(), + FailFast.FirstError, + resolvedIssuerDocuments[0], + undefined, + ); + } catch (e) { + return; + } + throw new Error("no error thrown when passing incorrect holder"); + }); + + it("should fail presentation validation when configured incorrectly", async () => { + // setup mock handlers returning DID documents from other methods + const resolveDidIotaMisconfigured = async function(_did_input: string) { + return holderFooDoc; + }; + + const resolveDidFooMisconfigured = async function(_did_input: string) { + return issuerBarDoc; + }; + + const resolveDidBarMisconfigured = async function(did_input: string) { + return issuerIotaDoc; + }; + + let handlerMap: Map Promise> = new Map(); + handlerMap.set("iota", resolveDidIotaMisconfigured); + handlerMap.set("foo", resolveDidFooMisconfigured); + handlerMap.set("bar", resolveDidBarMisconfigured); + + const resolver = new Resolver({ + handlers: handlerMap, + }); + + try { + await resolver.verifyPresentation( + presentation, + PresentationValidationOptions.default(), + FailFast.FirstError, + undefined, + undefined, + ); + } catch (e) { + if (e instanceof Error) { + assert.equal("ResolverError::PresentationValidationError", e.name); + return; + } + } + + throw new Error( + "the incorrectly configured resolver did not throw the expected error when validating the presentation", + ); + }); }); - - it("should fail presentation validation when configured incorrectly", async () => { - // setup mock handlers returning DID documents from other methods - const resolveDidIotaMisconfigured = async function (_did_input: string) { - return holderFooDoc; - }; - - const resolveDidFooMisconfigured = async function (_did_input: string) { - return issuerBarDoc; - }; - - const resolveDidBarMisconfigured = async function (did_input: string) { - return issuerIotaDoc; - }; - - let handlerMap: Map Promise> = new Map(); - handlerMap.set("iota", resolveDidIotaMisconfigured); - handlerMap.set("foo", resolveDidFooMisconfigured); - handlerMap.set("bar", resolveDidBarMisconfigured); - - const resolver = new Resolver({ - handlers: handlerMap - }); - - try { - await resolver.verifyPresentation( - presentation, - PresentationValidationOptions.default(), - FailFast.FirstError, - undefined, - undefined - ); - } catch (e) { - if (e instanceof Error) { - assert.equal("ResolverError::PresentationValidationError", e.name); - return; - } - } - - throw new Error( - "the incorrectly configured resolver did not throw the expected error when validating the presentation" - ); - }); - }); }); diff --git a/bindings/wasm/tests/txm_readme.js b/bindings/wasm/tests/txm_readme.js index a784b1828c..0f1ed5d0c0 100644 --- a/bindings/wasm/tests/txm_readme.js +++ b/bindings/wasm/tests/txm_readme.js @@ -1,7 +1,7 @@ -const {execSync} = require('child_process') +const { execSync } = require("child_process"); -describe("Test TXM", function () { +describe("Test TXM", function() { it("README examples pass", async () => { - execSync("txm README.md") + execSync("txm README.md"); }); -}) +}); diff --git a/dprint.json b/dprint.json index 829af810cc..4c575cb466 100644 --- a/dprint.json +++ b/dprint.json @@ -1,11 +1,23 @@ { "$schema": "https://dprint.dev/schemas/v0.json", "incremental": true, - "toml": { + "typescript": { + "indentWidth": 4, + "arrayExpression.preferHanging": true }, - "includes": ["**/*.toml"], - "excludes": [], + "toml": {}, + "includes": [ + "**/*.{toml,js,ts}" + ], + "excludes": [ + "documentation", + "bindings/wasm/cypress/**/*.{js,ts}", + "**/{node_modules,target}", + "bindings/wasm/{cypress,node,web}/**/*.{js,ts}", + "bindings/stronghold-nodejs/{napi-dist,dist}/**/*.{js,ts}" + ], "plugins": [ - "https://plugins.dprint.dev/toml-0.5.1.wasm" + "https://plugins.dprint.dev/toml-0.5.1.wasm", + "https://plugins.dprint.dev/typescript-0.73.1.wasm" ] -} +} \ No newline at end of file From f0b69a3ebea2ba894df3936b404ed4a102062780 Mon Sep 17 00:00:00 2001 From: Abdulrahim Al Methiab <31316147+abdulmth@users.noreply.github.com> Date: Tue, 20 Sep 2022 23:17:27 +0200 Subject: [PATCH 79/89] Expose iteration over verification relationship fields (#1024) --- bindings/wasm/src/did/wasm_core_document.rs | 16 ++-- bindings/wasm/src/iota/iota_document.rs | 16 ++-- identity_account/src/tests/account.rs | 4 +- identity_account/src/tests/updates.rs | 8 +- .../src/validator/credential_validator.rs | 16 ++-- .../src/validator/presentation_validator.rs | 18 ++--- identity_did/src/document/core_document.rs | 80 ++++++++++++++++++- .../src/tangle/resolver.rs | 6 +- .../src/document/iota_document.rs | 14 ++-- .../src/document/iota_document.rs | 10 +-- 10 files changed, 138 insertions(+), 50 deletions(-) diff --git a/bindings/wasm/src/did/wasm_core_document.rs b/bindings/wasm/src/did/wasm_core_document.rs index eb30fb13b6..9a8b6a1b8a 100644 --- a/bindings/wasm/src/did/wasm_core_document.rs +++ b/bindings/wasm/src/did/wasm_core_document.rs @@ -293,17 +293,23 @@ impl WasmCoreDocument { // Verification Methods // =========================================================================== - /// Returns a list of all {@link CoreVerificationMethod} in the DID Document. + /// Returns a list of all {@link CoreVerificationMethod} in the DID Document, + /// whose verification relationship matches `scope`. + /// + /// If `scope` is not set, a list over the **embedded** methods is returned. #[wasm_bindgen] - pub fn methods(&self) -> ArrayCoreVerificationMethod { - self + pub fn methods(&self, scope: Option) -> Result { + let scope: Option = scope.map(|js| js.into_serde().wasm_result()).transpose()?; + let methods = self .0 - .methods() + .methods(scope) + .into_iter() .cloned() .map(WasmCoreVerificationMethod::from) .map(JsValue::from) .collect::() - .unchecked_into::() + .unchecked_into::(); + Ok(methods) } /// Returns an array of all verification relationships. diff --git a/bindings/wasm/src/iota/iota_document.rs b/bindings/wasm/src/iota/iota_document.rs index 422f9c529b..4dd9598f79 100644 --- a/bindings/wasm/src/iota/iota_document.rs +++ b/bindings/wasm/src/iota/iota_document.rs @@ -199,17 +199,23 @@ impl WasmIotaDocument { // Verification Methods // =========================================================================== - /// Returns a list of all {@link IotaVerificationMethod} in the DID Document. + /// Returns a list of all {@link IotaVerificationMethod} in the DID Document, + /// whose verification relationship matches `scope`. + /// + /// If `scope` is not set, a list over the **embedded** methods is returned. #[wasm_bindgen] - pub fn methods(&self) -> ArrayIotaVerificationMethods { - self + pub fn methods(&self, scope: Option) -> Result { + let scope: Option = scope.map(|js| js.into_serde().wasm_result()).transpose()?; + let methods = self .0 - .methods() + .methods(scope) + .into_iter() .cloned() .map(WasmIotaVerificationMethod::from) .map(JsValue::from) .collect::() - .unchecked_into::() + .unchecked_into::(); + Ok(methods) } /// Adds a new `method` to the document in the given `scope`. diff --git a/identity_account/src/tests/account.rs b/identity_account/src/tests/account.rs index 7ccf1e9121..daaa2d6b33 100644 --- a/identity_account/src/tests/account.rs +++ b/identity_account/src/tests/account.rs @@ -159,7 +159,7 @@ async fn test_account_autopublish() { let doc = account.document(); - assert_eq!(doc.methods().count(), 1); + assert_eq!(doc.methods().len(), 1); assert_eq!(doc.service().len(), 2); for service in ["my-service", "my-other-service"] { @@ -213,7 +213,7 @@ async fn test_account_autopublish() { let doc = account.document(); assert_eq!(doc.service().len(), 0); - assert_eq!(doc.methods().count(), 2); + assert_eq!(doc.methods().len(), 2); for method in ["sign-0", "new-method"] { assert!(doc.resolve_method(method, None).is_some()); diff --git a/identity_account/src/tests/updates.rs b/identity_account/src/tests/updates.rs index 63b1309fa5..cedadecd62 100644 --- a/identity_account/src/tests/updates.rs +++ b/identity_account/src/tests/updates.rs @@ -55,7 +55,7 @@ async fn test_create_identity() -> Result<()> { let method: &IotaVerificationMethod = document.resolve_method(expected_fragment, None).unwrap(); assert_eq!(document.core_document().verification_relationships().count(), 1); - assert_eq!(document.core_document().methods().count(), 1); + assert_eq!(document.core_document().methods(None).len(), 1); let location: KeyLocation = KeyLocation::from_verification_method(method).unwrap(); @@ -204,7 +204,7 @@ async fn test_create_method_content_generate() -> Result<()> { // Still only the default relationship. assert_eq!(document.core_document().verification_relationships().count(), 1); - assert_eq!(document.core_document().methods().count(), 2); + assert_eq!(document.core_document().methods(None).len(), 2); let location: KeyLocation = KeyLocation::from_verification_method(method).unwrap(); @@ -291,7 +291,7 @@ async fn test_create_scoped_method() -> Result<()> { assert_eq!(document.core_document().verification_relationships().count(), 2); - assert_eq!(document.core_document().methods().count(), 2); + assert_eq!(document.core_document().methods(None).len(), 2); let core_doc = document.core_document(); @@ -601,7 +601,7 @@ async fn test_delete_method() -> Result<()> { // Still only the default relationship. assert_eq!(document.core_document().verification_relationships().count(), 1); - assert_eq!(document.core_document().methods().count(), 1); + assert_eq!(document.core_document().methods(None).len(), 1); // Ensure the key still exists in storage. assert!(account diff --git a/identity_credential/src/validator/credential_validator.rs b/identity_credential/src/validator/credential_validator.rs index 7d62adc216..3596fb27f1 100644 --- a/identity_credential/src/validator/credential_validator.rs +++ b/identity_credential/src/validator/credential_validator.rs @@ -408,7 +408,7 @@ mod tests { issuer_doc .signer(issuer_key.private()) .options(ProofOptions::default()) - .method(issuer_doc.methods().next().unwrap().id()) + .method(issuer_doc.methods(None).get(0).unwrap().id()) .sign(&mut credential) .unwrap(); @@ -478,7 +478,7 @@ mod tests { issuer_doc .signer(issuer_key.private()) .options(ProofOptions::default()) - .method(issuer_doc.methods().next().unwrap().id()) + .method(issuer_doc.methods(None).get(0).unwrap().id()) .sign(&mut credential) .unwrap(); @@ -516,7 +516,7 @@ mod tests { issuer_doc .signer(issuer_key.private()) .options(ProofOptions::default()) - .method(issuer_doc.methods().next().unwrap().id()) + .method(issuer_doc.methods(None).get(0).unwrap().id()) .sign(&mut credential) .unwrap(); @@ -542,7 +542,7 @@ mod tests { issuer_doc .signer(issuer_key.private()) .options(ProofOptions::default()) - .method(issuer_doc.methods().next().unwrap().id()) + .method(issuer_doc.methods(None).get(0).unwrap().id()) .sign(&mut credential) .unwrap(); @@ -589,7 +589,7 @@ mod tests { issuer_doc .signer(other_keys.private()) .options(ProofOptions::default()) - .method(issuer_doc.methods().next().unwrap().id()) + .method(issuer_doc.methods(None).get(0).unwrap().id()) .sign(&mut credential) .unwrap(); @@ -815,7 +815,7 @@ mod tests { issuer_doc .signer(issuer_key.private()) .options(ProofOptions::default()) - .method(issuer_doc.methods().next().unwrap().id()) + .method(issuer_doc.methods(None).get(0).unwrap().id()) .sign(&mut credential) .unwrap(); // the credential now has no credential subjects which is not semantically correct @@ -855,7 +855,7 @@ mod tests { issuer_doc .signer(issuer_key.private()) .options(ProofOptions::default()) - .method(issuer_doc.methods().next().unwrap().id()) + .method(issuer_doc.methods(None).get(0).unwrap().id()) .sign(&mut credential) .unwrap(); // the credential now has no credential subjects which is not semantically correct @@ -894,7 +894,7 @@ mod tests { issuer_doc .signer(issuer_key.private()) .options(ProofOptions::default()) - .method(issuer_doc.methods().next().unwrap().id()) + .method(issuer_doc.methods(None).get(0).unwrap().id()) .sign(&mut credential) .unwrap(); // the credential now has no credential subjects which is not semantically correct diff --git a/identity_credential/src/validator/presentation_validator.rs b/identity_credential/src/validator/presentation_validator.rs index ddae49f703..f84921560d 100644 --- a/identity_credential/src/validator/presentation_validator.rs +++ b/identity_credential/src/validator/presentation_validator.rs @@ -318,14 +318,14 @@ mod tests { issuer_foo_doc .signer(issuer_foo_key.private()) .options(ProofOptions::default()) - .method(issuer_foo_doc.methods().next().unwrap().id()) + .method(issuer_foo_doc.methods(None).get(0).unwrap().id()) .sign(credential_foo) .unwrap(); issuer_bar_doc .signer(issuer_bar_key.private()) .options(ProofOptions::default()) - .method(issuer_bar_doc.methods().next().unwrap().id()) + .method(issuer_bar_doc.methods(None).get(0).unwrap().id()) .sign(credential_bar) .unwrap(); setup @@ -350,7 +350,7 @@ mod tests { subject_foo_doc .signer(subject_foo_key.private()) .options(ProofOptions::new().challenge("475a7984-1bb5-4c4c-a56f-822bccd46440".to_owned())) - .method(subject_foo_doc.methods().next().unwrap().id()) + .method(subject_foo_doc.methods(None).get(0).unwrap().id()) .sign(&mut presentation) .unwrap(); @@ -396,7 +396,7 @@ mod tests { subject_foo_doc .signer(subject_foo_key.private()) .options(ProofOptions::new().challenge("some challenge".to_owned())) - .method(subject_foo_doc.methods().next().unwrap().id()) + .method(subject_foo_doc.methods(None).get(0).unwrap().id()) .sign(&mut presentation) .unwrap(); @@ -461,7 +461,7 @@ mod tests { subject_foo_doc .signer(subject_foo_key.private()) .options(ProofOptions::new().challenge("some challenge".to_owned())) - .method(subject_foo_doc.methods().next().unwrap().id()) + .method(subject_foo_doc.methods(None).get(0).unwrap().id()) .sign(&mut presentation) .unwrap(); @@ -513,7 +513,7 @@ mod tests { issuer_bar_doc .signer(issuer_bar_key.private()) .options(ProofOptions::default()) - .method(issuer_bar_doc.methods().next().unwrap().id()) + .method(issuer_bar_doc.methods(None).get(0).unwrap().id()) .sign(&mut credential_bar) .unwrap(); @@ -525,7 +525,7 @@ mod tests { subject_foo_doc .signer(subject_foo_key.private()) .options(ProofOptions::new().challenge("some challenge".to_owned())) - .method(subject_foo_doc.methods().next().unwrap().id()) + .method(subject_foo_doc.methods(None).get(0).unwrap().id()) .sign(&mut presentation) .unwrap(); @@ -619,7 +619,7 @@ mod tests { subject_foo_doc .signer(subject_foo_key.private()) .options(ProofOptions::new().challenge("some challenge".to_owned())) - .method(subject_foo_doc.methods().next().unwrap().id()) + .method(subject_foo_doc.methods(None).get(0).unwrap().id()) .sign(&mut presentation) .unwrap(); @@ -692,7 +692,7 @@ mod tests { subject_foo_doc .signer(subject_foo_key.private()) .options(ProofOptions::new().challenge("some challenge".to_owned())) - .method(subject_foo_doc.methods().next().unwrap().id()) + .method(subject_foo_doc.methods(None).get(0).unwrap().id()) .sign(&mut presentation) .unwrap(); diff --git a/identity_did/src/document/core_document.rs b/identity_did/src/document/core_document.rs index 56271a7b8d..8189fb3771 100644 --- a/identity_did/src/document/core_document.rs +++ b/identity_did/src/document/core_document.rs @@ -483,10 +483,51 @@ where } } + /// Returns a `Vec` of verification method references whose verification relationship matches `scope`. + /// + /// If `scope` is `None`, an iterator over all **embedded** methods is returned. + pub fn methods(&self, scope: Option) -> Vec<&VerificationMethod> + where + D: DID, + { + if let Some(scope) = scope { + match scope { + MethodScope::VerificationMethod => self.verification_method().iter().collect(), + MethodScope::VerificationRelationship(MethodRelationship::AssertionMethod) => self + .assertion_method() + .iter() + .filter_map(|method_ref| self.resolve_method_ref(method_ref)) + .collect(), + MethodScope::VerificationRelationship(MethodRelationship::Authentication) => self + .authentication() + .iter() + .filter_map(|method_ref| self.resolve_method_ref(method_ref)) + .collect(), + MethodScope::VerificationRelationship(MethodRelationship::CapabilityDelegation) => self + .capability_delegation() + .iter() + .filter_map(|method_ref| self.resolve_method_ref(method_ref)) + .collect(), + MethodScope::VerificationRelationship(MethodRelationship::CapabilityInvocation) => self + .capability_invocation() + .iter() + .filter_map(|method_ref| self.resolve_method_ref(method_ref)) + .collect(), + MethodScope::VerificationRelationship(MethodRelationship::KeyAgreement) => self + .key_agreement() + .iter() + .filter_map(|method_ref| self.resolve_method_ref(method_ref)) + .collect(), + } + } else { + self.all_methods().collect() + } + } + /// Returns an iterator over all embedded verification methods in the DID Document. /// /// This excludes verification methods that are referenced by the DID Document. - pub fn methods(&self) -> impl Iterator> { + fn all_methods(&self) -> impl Iterator> { fn __filter_ref(method: &MethodRef) -> Option<&VerificationMethod> where D: DID, @@ -1053,8 +1094,41 @@ mod tests { let document: CoreDocument = document(); // Access methods by index. - assert_eq!(document.methods().next().unwrap().id().to_string(), "did:example:1234#key-1"); - assert_eq!(document.methods().nth(2).unwrap().id().to_string(), "did:example:1234#key-3"); + assert_eq!(document.methods(None).get(0).unwrap().id().to_string(), "did:example:1234#key-1"); + assert_eq!(document.methods(None).get(2).unwrap().id().to_string(), "did:example:1234#key-3"); + } + + #[test] + fn test_methods_scope() { + let document: CoreDocument = document(); + + // VerificationMethod + let verification_methods: Vec<&VerificationMethod> = document.methods(Some(MethodScope::VerificationMethod)); + assert_eq!( + verification_methods.get(0).unwrap().id().to_string(), + "did:example:1234#key-1" + ); + assert_eq!( + verification_methods.get(1).unwrap().id().to_string(), + "did:example:1234#key-2" + ); + assert_eq!( + verification_methods.get(2).unwrap().id().to_string(), + "did:example:1234#key-3" + ); + assert_eq!(verification_methods.len(), 3); + + // Authentication + let authentication: Vec<&VerificationMethod> = document.methods(Some(MethodScope::authentication())); + assert_eq!( + authentication.get(0).unwrap().id().to_string(), + "did:example:1234#auth-key" + ); + assert_eq!( + authentication.get(1).unwrap().id().to_string(), + "did:example:1234#key-3" + ); + assert_eq!(authentication.len(), 2); } #[test] diff --git a/identity_iota_client_legacy/src/tangle/resolver.rs b/identity_iota_client_legacy/src/tangle/resolver.rs index 22331b8951..eb2312db89 100644 --- a/identity_iota_client_legacy/src/tangle/resolver.rs +++ b/identity_iota_client_legacy/src/tangle/resolver.rs @@ -410,7 +410,7 @@ mod tests { issuer_core_doc .signer(issuer_core_key.private()) .options(ProofOptions::default()) - .method(issuer_core_doc.methods().next().unwrap().id()) + .method(issuer_core_doc.methods(None).get(0).unwrap().id()) .sign(credential_core) .unwrap(); setup @@ -436,7 +436,7 @@ mod tests { subject_doc .signer(subject_key.private()) .options(ProofOptions::new().challenge(challenge.clone())) - .method(subject_doc.methods().next().unwrap().id()) + .method(subject_doc.methods(None).get(0).unwrap().id()) .sign(&mut presentation) .unwrap(); @@ -479,7 +479,7 @@ mod tests { subject_doc .signer(subject_key.private()) .options(ProofOptions::new().challenge(challenge.clone())) - .method(subject_doc.methods().next().unwrap().id()) + .method(subject_doc.methods(None).get(0).unwrap().id()) .sign(&mut presentation) .unwrap(); diff --git a/identity_iota_core/src/document/iota_document.rs b/identity_iota_core/src/document/iota_document.rs index c4b65082e2..e60bc4bd01 100644 --- a/identity_iota_core/src/document/iota_document.rs +++ b/identity_iota_core/src/document/iota_document.rs @@ -161,9 +161,11 @@ impl IotaDocument { // Verification Methods // =========================================================================== - /// Returns an iterator over all [`IotaVerificationMethod`] in the DID Document. - pub fn methods(&self) -> impl Iterator { - self.document.methods() + /// Returns a `Vec` of verification method references whose verification relationship matches `scope`. + /// + /// If `scope` is `None`, an iterator over all **embedded** methods is returned. + pub fn methods(&self, scope: Option) -> Vec<&IotaVerificationMethod> { + self.document.methods(scope) } /// Adds a new [`IotaVerificationMethod`] to the document in the given [`MethodScope`]. @@ -506,14 +508,14 @@ mod tests { assert_eq!(doc1.id().network_str(), network.as_ref()); assert_eq!(doc1.id().tag(), placeholder.tag()); assert_eq!(doc1.id(), &placeholder); - assert_eq!(doc1.methods().count(), 0); + assert_eq!(doc1.methods(None).len(), 0); assert!(doc1.service().is_empty()); // VALID new_with_id(). let did: IotaDID = valid_did(); let doc2: IotaDocument = IotaDocument::new_with_id(did.clone()); assert_eq!(doc2.id(), &did); - assert_eq!(doc2.methods().count(), 0); + assert_eq!(doc2.methods(None).len(), 0); assert!(doc2.service().is_empty()); } @@ -528,7 +530,7 @@ mod tests { generate_method(&controller, "#auth-key"), ]; - let mut methods = document.methods(); + let mut methods = document.methods(None).into_iter(); assert_eq!(methods.next(), Some(&expected[0])); assert_eq!(methods.next(), Some(&expected[1])); assert_eq!(methods.next(), Some(&expected[2])); diff --git a/identity_iota_core_legacy/src/document/iota_document.rs b/identity_iota_core_legacy/src/document/iota_document.rs index eeb750ac32..cfaed019c8 100644 --- a/identity_iota_core_legacy/src/document/iota_document.rs +++ b/identity_iota_core_legacy/src/document/iota_document.rs @@ -273,8 +273,8 @@ impl IotaDocument { // =========================================================================== /// Returns an iterator over all [`IotaVerificationMethods`][IotaVerificationMethod] in the DID Document. - pub fn methods(&self) -> impl Iterator { - self.document.methods() + pub fn methods(&self) -> Vec<&IotaVerificationMethod> { + self.document.methods(None) } /// Adds a new [`IotaVerificationMethod`] to the document in the given [`MethodScope`]. @@ -950,7 +950,7 @@ mod tests { .build() .unwrap(); - let mut methods = document.methods(); + let mut methods = document.methods().into_iter(); assert_eq!(methods.next(), Some(expected).as_ref()); assert_eq!(methods.next(), None); @@ -967,7 +967,7 @@ mod tests { valid_verification_method(&controller, "#auth-key"), ]; - let mut methods = document.methods(); + let mut methods = document.methods().into_iter(); assert_eq!(methods.next(), Some(&expected[0])); assert_eq!(methods.next(), Some(&expected[1])); assert_eq!(methods.next(), Some(&expected[2])); @@ -1640,7 +1640,7 @@ mod tests { } // `methods` returns all embedded verification methods, so only one is expected. - assert_eq!(document.methods().count(), 1); + assert_eq!(document.methods().len(), 1); } #[test] From 20dc654b14ef22ddf7a5bf3f033578f1f135061b Mon Sep 17 00:00:00 2001 From: cycraig Date: Wed, 21 Sep 2022 14:08:48 +0200 Subject: [PATCH 80/89] Remove legacy example tests from CI (#1040) --- .github/workflows/build-and-test.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 5429d1759a..300faa2a00 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -110,14 +110,6 @@ jobs: command: test args: --workspace --all-features --release - - name: Run legacy examples - # run examples only on ubuntu for now - if: matrix.os == 'ubuntu-latest' - run: | - cargo metadata --format-version 1 --manifest-path ./examples_legacy/Cargo.toml | \ - jq -r '.packages[] | select(.name == "examples_legacy") | .targets[].name' | \ - parallel -k -j 4 --retries 3 ./target/release/examples/{} - - name: Run Rust examples # run examples only on ubuntu for now if: matrix.os == 'ubuntu-latest' From 1a00b1389ce5801acc598c6542759b958a17036f Mon Sep 17 00:00:00 2001 From: Henrique Nogara Date: Wed, 21 Sep 2022 14:37:38 -0300 Subject: [PATCH 81/89] Set Controller and Governor Addresses (#1023) * Set controller and governor addresses * Add unpack_from_output * Unset governor and state controller addresses when packing * Remove unpack; Set addresses when packing * Add wasm unpack_from_output; Always overwrite controller * Fix unpack_from_output * Remove dependecy and; Fix tests --- bindings/wasm/src/iota/iota_document.rs | 14 ++- .../src/client/identity_client.rs | 4 +- .../src/document/iota_document.rs | 57 +++++++++-- .../src/document/iota_document_metadata.rs | 8 ++ .../src/state_metadata/document.rs | 97 +++++++++++++++++-- 5 files changed, 159 insertions(+), 21 deletions(-) diff --git a/bindings/wasm/src/iota/iota_document.rs b/bindings/wasm/src/iota/iota_document.rs index 4dd9598f79..1e752e0197 100644 --- a/bindings/wasm/src/iota/iota_document.rs +++ b/bindings/wasm/src/iota/iota_document.rs @@ -12,6 +12,8 @@ use identity_iota::crypto::ProofOptions; use identity_iota::did::verifiable::VerifiableProperties; use identity_iota::did::Document; use identity_iota::did::MethodScope; +use identity_iota::iota::block::output::dto::AliasOutputDto; +use identity_iota::iota::block::output::AliasOutput; use identity_iota::iota::IotaDID; use identity_iota::iota::IotaDocument; use identity_iota::iota::IotaVerificationMethod; @@ -395,9 +397,15 @@ impl WasmIotaDocument { /// cannot be inferred from the state metadata. It also indicates the network, which is not /// encoded in the `AliasId` alone. #[allow(non_snake_case)] - #[wasm_bindgen] - pub fn unpack(did: &WasmIotaDID, stateMetadata: &[u8], allowEmpty: bool) -> Result { - IotaDocument::unpack(&did.0, stateMetadata, allowEmpty) + #[wasm_bindgen(js_name = unpackFromOutput)] + pub fn unpack_from_output(did: &WasmIotaDID, aliasDto: JsValue, allowEmpty: bool) -> Result { + let alias_dto: AliasOutputDto = aliasDto.into_serde().wasm_result()?; + let alias_output: AliasOutput = AliasOutput::try_from(&alias_dto) + .map_err(|err| { + identity_iota::iota::Error::JsError(format!("get_alias_output failed to convert AliasOutputDto: {}", err)) + }) + .wasm_result()?; + IotaDocument::unpack_from_output(&did.0, &alias_output, allowEmpty) .map(WasmIotaDocument) .wasm_result() } diff --git a/identity_iota_core/src/client/identity_client.rs b/identity_iota_core/src/client/identity_client.rs index b53a2d363f..7bf6168c45 100644 --- a/identity_iota_core/src/client/identity_client.rs +++ b/identity_iota_core/src/client/identity_client.rs @@ -147,9 +147,7 @@ pub trait IotaIdentityClientExt: IotaIdentityClient { let id: AliasId = AliasId::from(did); let (_, alias_output) = self.get_alias_output(id).await?; - - let document: &[u8] = alias_output.state_metadata(); - IotaDocument::unpack(did, document, true) + IotaDocument::unpack_from_output(did, &alias_output, true) } /// Fetches the [`AliasOutput`] associated with the given DID. diff --git a/identity_iota_core/src/document/iota_document.rs b/identity_iota_core/src/document/iota_document.rs index e60bc4bd01..deee2af2e6 100644 --- a/identity_iota_core/src/document/iota_document.rs +++ b/identity_iota_core/src/document/iota_document.rs @@ -28,6 +28,8 @@ use identity_did::verification::VerificationMethod; use serde::Deserialize; use serde::Serialize; +use crate::block::address::Address; +use crate::block::output::AliasOutput; use crate::error::Result; use crate::IotaDID; use crate::IotaDIDUrl; @@ -277,7 +279,7 @@ impl IotaDocument { StateMetadataDocument::from(self).pack(encoding) } - /// Deserializes the document from the state metadata bytes of an Alias Output. + /// Deserializes the document from an Alias Output. /// /// If `allow_empty` is true, this will return an empty DID document marked as `deactivated` /// if `state_metadata` is empty. @@ -285,15 +287,35 @@ impl IotaDocument { /// NOTE: `did` is required since it is omitted from the serialized DID Document and /// cannot be inferred from the state metadata. It also indicates the network, which is not /// encoded in the `AliasId` alone. - pub fn unpack(did: &IotaDID, state_metadata: &[u8], allow_empty: bool) -> Result { - if state_metadata.is_empty() && allow_empty { + pub fn unpack_from_output(did: &IotaDID, alias_output: &AliasOutput, allow_empty: bool) -> Result { + if alias_output.state_metadata().is_empty() && allow_empty { let mut empty_document = IotaDocument::new_with_id(did.clone()); empty_document.metadata.created = None; empty_document.metadata.updated = None; empty_document.metadata.deactivated = Some(true); return Ok(empty_document); } - StateMetadataDocument::unpack(state_metadata).and_then(|doc| doc.into_iota_document(did)) + let mut iota_document: IotaDocument = + StateMetadataDocument::unpack(alias_output.state_metadata()).and_then(|doc| doc.into_iota_document(did))?; + iota_document.set_controller_and_governor_addresses(alias_output, &did.network_str().to_owned().try_into()?); + Ok(iota_document) + } + + fn set_controller_and_governor_addresses(&mut self, alias_output: &AliasOutput, network_name: &NetworkName) { + self.metadata.governor_address = Some(*alias_output.governor_address()); + self.metadata.state_controller_address = Some(*alias_output.state_controller_address()); + + let controller_did: Option = { + match alias_output.unlock_conditions().state_controller_address() { + Some(unlock_condition) => match unlock_condition.address() { + Address::Alias(alias_address) => Some(IotaDID::new(alias_address.alias_id(), network_name)), + Address::Nft(_) | Address::Ed25519(_) => None, + }, + None => None, + } + }; + // Overwrite the DID Document controller. + *self.core_document_mut().controller_mut() = controller_did.map(OneOrSet::new_one); } } @@ -341,7 +363,7 @@ mod client_document { }; let did: IotaDID = IotaDID::new(alias_id.deref(), network); - documents.push(IotaDocument::unpack(&did, alias_output.state_metadata(), true)?); + documents.push(IotaDocument::unpack_from_output(&did, alias_output, true)?); } } } @@ -463,6 +485,12 @@ mod tests { use identity_did::verification::MethodType; use super::*; + use crate::block::address::AliasAddress; + use crate::block::output::unlock_condition::GovernorAddressUnlockCondition; + use crate::block::output::unlock_condition::StateControllerAddressUnlockCondition; + use crate::block::output::AliasId; + use crate::block::output::AliasOutputBuilder; + use crate::block::output::UnlockCondition; fn valid_did() -> IotaDID { "did:iota:0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" @@ -721,9 +749,20 @@ mod tests { #[test] fn test_unpack_empty() { // VALID: unpack empty, deactivated document. - let did: IotaDID = valid_did(); - let empty: &[u8] = &[]; - let document: IotaDocument = IotaDocument::unpack(&did, empty, true).unwrap(); + let did: IotaDID = "did:iota:0xBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" + .parse() + .unwrap(); + let alias_output: AliasOutput = AliasOutputBuilder::new_with_amount(1, AliasId::from(&did)) + .unwrap() + .add_unlock_condition(UnlockCondition::StateControllerAddress( + StateControllerAddressUnlockCondition::new(Address::Alias(AliasAddress::new(AliasId::from(&valid_did())))), + )) + .add_unlock_condition(UnlockCondition::GovernorAddress(GovernorAddressUnlockCondition::new( + Address::Alias(AliasAddress::new(AliasId::from(&valid_did()))), + ))) + .finish() + .unwrap(); + let document: IotaDocument = IotaDocument::unpack_from_output(&did, &alias_output, true).unwrap(); assert_eq!(document.id(), &did); assert_eq!(document.metadata.deactivated, Some(true)); @@ -733,7 +772,7 @@ mod tests { assert_eq!(document.to_json().unwrap(), json); // INVALID: reject empty document. - assert!(IotaDocument::unpack(&did, empty, false).is_err()); + assert!(IotaDocument::unpack_from_output(&did, &alias_output, false).is_err()); } #[test] diff --git a/identity_iota_core/src/document/iota_document_metadata.rs b/identity_iota_core/src/document/iota_document_metadata.rs index 79f3f94902..6a6bc448c0 100644 --- a/identity_iota_core/src/document/iota_document_metadata.rs +++ b/identity_iota_core/src/document/iota_document_metadata.rs @@ -11,6 +11,8 @@ use identity_core::convert::FmtJson; use serde::Deserialize; use serde::Serialize; +use crate::block::address::Address; + /// Additional attributes related to a [`IotaDocument`][crate::IotaDocument]. #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct IotaDocumentMetadata { @@ -21,6 +23,10 @@ pub struct IotaDocumentMetadata { pub updated: Option, #[serde(skip_serializing_if = "Option::is_none")] pub deactivated: Option, + #[serde(rename = "governorAddress", skip_serializing_if = "Option::is_none")] + pub governor_address: Option
, + #[serde(rename = "stateControllerAddress", skip_serializing_if = "Option::is_none")] + pub state_controller_address: Option
, #[serde(flatten)] pub properties: Object, } @@ -34,6 +40,8 @@ impl IotaDocumentMetadata { created: Some(now), updated: Some(now), deactivated: None, + governor_address: None, + state_controller_address: None, properties: Object::default(), } } diff --git a/identity_iota_core/src/state_metadata/document.rs b/identity_iota_core/src/state_metadata/document.rs index 011456bde6..b0e2c66f37 100644 --- a/identity_iota_core/src/state_metadata/document.rs +++ b/identity_iota_core/src/state_metadata/document.rs @@ -31,9 +31,9 @@ const DID_MARKER: &[u8] = b"DID"; #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] pub(crate) struct StateMetadataDocument { #[serde(rename = "doc")] - document: CoreDocument, + pub(crate) document: CoreDocument, #[serde(rename = "meta")] - metadata: IotaDocumentMetadata, + pub(crate) metadata: IotaDocumentMetadata, } impl StateMetadataDocument { @@ -58,7 +58,12 @@ impl StateMetadataDocument { /// Pack a [`StateMetadataDocument`] into bytes, suitable for inclusion in /// an Alias Output's state metadata, according to the given `encoding`. - pub fn pack(self, encoding: StateMetadataEncoding) -> Result> { + pub fn pack(mut self, encoding: StateMetadataEncoding) -> Result> { + // Unset Governor and State Controller Addresses to avoid bloating the payload + self.metadata.governor_address = None; + self.metadata.state_controller_address = None; + *self.document.controller_mut() = None; + let encoded_message_data: Vec = match encoding { StateMetadataEncoding::Json => self .to_json_vec() @@ -329,15 +334,56 @@ mod tests { let packed_bytes: Vec = state_metadata_doc.clone().pack(StateMetadataEncoding::Json).unwrap(); let unpacked_doc = StateMetadataDocument::unpack(&packed_bytes).unwrap(); - assert_eq!(state_metadata_doc, unpacked_doc); + // Controller and State Controller are set to None when packing + assert_eq!(state_metadata_doc.metadata.created, unpacked_doc.metadata.created); + assert_eq!(state_metadata_doc.metadata.updated, unpacked_doc.metadata.updated); + assert_eq!( + state_metadata_doc.metadata.deactivated, + unpacked_doc.metadata.deactivated + ); + + assert_eq!(state_metadata_doc.document.id(), unpacked_doc.document.id()); + assert_eq!( + state_metadata_doc.document.also_known_as(), + unpacked_doc.document.also_known_as() + ); + assert_eq!( + state_metadata_doc.document.verification_method(), + unpacked_doc.document.verification_method() + ); + assert_eq!( + state_metadata_doc.document.authentication(), + unpacked_doc.document.authentication() + ); + assert_eq!( + state_metadata_doc.document.assertion_method(), + unpacked_doc.document.assertion_method() + ); + assert_eq!( + state_metadata_doc.document.key_agreement(), + unpacked_doc.document.key_agreement() + ); + assert_eq!( + state_metadata_doc.document.capability_delegation(), + unpacked_doc.document.capability_delegation() + ); + assert_eq!(state_metadata_doc.document.service(), unpacked_doc.document.service()); + assert_eq!( + state_metadata_doc.document.properties(), + unpacked_doc.document.properties() + ); } #[test] fn test_pack_format() { // Changing the serialization is a breaking change! let TestSetup { document, .. } = test_document(); - let state_metadata_doc: StateMetadataDocument = StateMetadataDocument::from(document); + let mut state_metadata_doc: StateMetadataDocument = StateMetadataDocument::from(document); let packed: Vec = state_metadata_doc.clone().pack(StateMetadataEncoding::Json).unwrap(); + // Controller and State Controller are set to None when packing + *state_metadata_doc.document.controller_mut() = None; + state_metadata_doc.metadata.governor_address = None; + state_metadata_doc.metadata.state_controller_address = None; let expected_payload: String = format!( "{{\"doc\":{},\"meta\":{}}}", state_metadata_doc.document, state_metadata_doc.metadata @@ -394,6 +440,45 @@ mod tests { let original = original_length.to_le_bytes(); packed[5] = original[0]; packed[6] = original[1]; - assert_eq!(StateMetadataDocument::unpack(&packed).unwrap(), state_metadata_doc); + + let unpacked_doc = StateMetadataDocument::unpack(&packed).unwrap(); + // Controller and State Controller are set to None when packing + assert_eq!(state_metadata_doc.metadata.created, unpacked_doc.metadata.created); + assert_eq!(state_metadata_doc.metadata.updated, unpacked_doc.metadata.updated); + assert_eq!( + state_metadata_doc.metadata.deactivated, + unpacked_doc.metadata.deactivated + ); + + assert_eq!(state_metadata_doc.document.id(), unpacked_doc.document.id()); + assert_eq!( + state_metadata_doc.document.also_known_as(), + unpacked_doc.document.also_known_as() + ); + assert_eq!( + state_metadata_doc.document.verification_method(), + unpacked_doc.document.verification_method() + ); + assert_eq!( + state_metadata_doc.document.authentication(), + unpacked_doc.document.authentication() + ); + assert_eq!( + state_metadata_doc.document.assertion_method(), + unpacked_doc.document.assertion_method() + ); + assert_eq!( + state_metadata_doc.document.key_agreement(), + unpacked_doc.document.key_agreement() + ); + assert_eq!( + state_metadata_doc.document.capability_delegation(), + unpacked_doc.document.capability_delegation() + ); + assert_eq!(state_metadata_doc.document.service(), unpacked_doc.document.service()); + assert_eq!( + state_metadata_doc.document.properties(), + unpacked_doc.document.properties() + ); } } From 1f720368936b291d7c87232202a3267850200f92 Mon Sep 17 00:00:00 2001 From: "Oliver E. Anderson" Date: Thu, 22 Sep 2022 12:07:04 +0200 Subject: [PATCH 82/89] Example: Custom `Resolver` configuration. (#1021) --- bindings/wasm/docs/api-reference.md | 54 +- bindings/wasm/examples/README.md | 1 + .../src/1_advanced/5_custom_resolution.ts | 73 ++ bindings/wasm/examples/src/main.ts | 3 + .../examples/src/tests/5_custom_resolution.ts | 8 + bindings/wasm/package-lock.json | 843 +++++++++++++++++- bindings/wasm/package.json | 1 + examples/1_advanced/6_custom_resolution.rs | 104 +++ examples/Cargo.toml | 5 + examples/README.md | 1 + identity_resolver/src/resolution/resolver.rs | 7 + 11 files changed, 1039 insertions(+), 61 deletions(-) create mode 100644 bindings/wasm/examples/src/1_advanced/5_custom_resolution.ts create mode 100644 bindings/wasm/examples/src/tests/5_custom_resolution.ts create mode 100644 examples/1_advanced/6_custom_resolution.rs diff --git a/bindings/wasm/docs/api-reference.md b/bindings/wasm/docs/api-reference.md index 563b665331..9853e25a45 100644 --- a/bindings/wasm/docs/api-reference.md +++ b/bindings/wasm/docs/api-reference.md @@ -102,10 +102,6 @@ See IVerifierOptions.

## Members
-
MethodRelationship
-
-
StateMetadataEncoding
-
StatusCheck

Controls validation behaviour when checking whether or not a credential has been revoked by its credentialStatus.

@@ -150,6 +146,10 @@ This variant is the default used if no other variant is specified when construct
KeyType
+
MethodRelationship
+
+
StateMetadataEncoding
+
## Functions @@ -513,7 +513,7 @@ A method-agnostic DID Document. * [.insertService(service)](#CoreDocument+insertService) ⇒ boolean * [.removeService(didUrl)](#CoreDocument+removeService) ⇒ boolean * [.resolveService(query)](#CoreDocument+resolveService) ⇒ [CoreService](#CoreService) \| undefined - * [.methods()](#CoreDocument+methods) ⇒ [Array.<CoreVerificationMethod>](#CoreVerificationMethod) + * [.methods(scope)](#CoreDocument+methods) ⇒ [Array.<CoreVerificationMethod>](#CoreVerificationMethod) * [.verificationRelationships()](#CoreDocument+verificationRelationships) ⇒ Array.<(CoreDIDUrl\|CoreVerificationMethod)> * [.insertMethod(method, scope)](#CoreDocument+insertMethod) * [.removeMethod(did)](#CoreDocument+removeMethod) @@ -697,10 +697,18 @@ if present. -### coreDocument.methods() ⇒ [Array.<CoreVerificationMethod>](#CoreVerificationMethod) -Returns a list of all [CoreVerificationMethod](#CoreVerificationMethod) in the DID Document. +### coreDocument.methods(scope) ⇒ [Array.<CoreVerificationMethod>](#CoreVerificationMethod) +Returns a list of all [CoreVerificationMethod](#CoreVerificationMethod) in the DID Document, +whose verification relationship matches `scope`. + +If `scope` is not set, a list over the **embedded** methods is returned. **Kind**: instance method of [CoreDocument](#CoreDocument) + +| Param | Type | +| --- | --- | +| scope | [MethodScope](#MethodScope) \| undefined | + ### coreDocument.verificationRelationships() ⇒ Array.<(CoreDIDUrl\|CoreVerificationMethod)> @@ -1722,7 +1730,7 @@ Clones the `DID` into a `DIDUrl`. ### did.toAliasId() ⇒ string -Creates an AliasId from the DID tag. +Returns the hex-encoded AliasId with a '0x' prefix, from the DID tag. **Kind**: instance method of [IotaDID](#IotaDID) @@ -1961,7 +1969,7 @@ Deserializes an instance from a JSON object. * [.insertService(service)](#IotaDocument+insertService) ⇒ boolean * [.removeService(did)](#IotaDocument+removeService) ⇒ boolean * [.resolveService(query)](#IotaDocument+resolveService) ⇒ [IotaService](#IotaService) \| undefined - * [.methods()](#IotaDocument+methods) ⇒ [Array.<IotaVerificationMethod>](#IotaVerificationMethod) + * [.methods(scope)](#IotaDocument+methods) ⇒ [Array.<IotaVerificationMethod>](#IotaVerificationMethod) * [.insertMethod(method, scope)](#IotaDocument+insertMethod) * [.removeMethod(did)](#IotaDocument+removeMethod) * [.resolveMethod(query, scope)](#IotaDocument+resolveMethod) ⇒ [IotaVerificationMethod](#IotaVerificationMethod) \| undefined @@ -2102,10 +2110,18 @@ if present. -### iotaDocument.methods() ⇒ [Array.<IotaVerificationMethod>](#IotaVerificationMethod) -Returns a list of all [IotaVerificationMethod](#IotaVerificationMethod) in the DID Document. +### iotaDocument.methods(scope) ⇒ [Array.<IotaVerificationMethod>](#IotaVerificationMethod) +Returns a list of all [IotaVerificationMethod](#IotaVerificationMethod) in the DID Document, +whose verification relationship matches `scope`. + +If `scope` is not set, a list over the **embedded** methods is returned. **Kind**: instance method of [IotaDocument](#IotaDocument) + +| Param | Type | +| --- | --- | +| scope | [MethodScope](#MethodScope) \| undefined | + ### iotaDocument.insertMethod(method, scope) @@ -3991,14 +4007,6 @@ This is possible because Ed25519 is birationally equivalent to Curve25519 used b | --- | --- | | publicKey | Uint8Array | - - -## MethodRelationship -**Kind**: global variable - - -## StateMetadataEncoding -**Kind**: global variable ## StatusCheck @@ -4081,6 +4089,14 @@ Return after the first error occurs. ## KeyType **Kind**: global variable + + +## MethodRelationship +**Kind**: global variable + + +## StateMetadataEncoding +**Kind**: global variable ## start() diff --git a/bindings/wasm/examples/README.md b/bindings/wasm/examples/README.md index 040eeee67b..5452076427 100644 --- a/bindings/wasm/examples/README.md +++ b/bindings/wasm/examples/README.md @@ -55,6 +55,7 @@ The following advanced examples are available: | [2_nft_owns_did](src/1_advanced/2_nft_owns_did.ts) | Demonstrates how an identity can be owned by NFTs, and how observers can verify that relationship. | | [3_did_issues_tokens](src/1_advanced/3_did_issues_tokens.ts) | Demonstrates how an identity can issue and control a Token Foundry and its tokens. | | [4_key_exchange](src/1_advanced/4_key_exchange.ts) | Demonstrates Elliptic-curve Diffie-Hellman (ECDH) cryptographic key exchange with DID Documents. | +| [5_custom_resolution](src/1_advanced/5_custom_resolution.ts) | Demonstrates how to set up a resolver using custom handlers. | ## Browser diff --git a/bindings/wasm/examples/src/1_advanced/5_custom_resolution.ts b/bindings/wasm/examples/src/1_advanced/5_custom_resolution.ts new file mode 100644 index 0000000000..6af7339b82 --- /dev/null +++ b/bindings/wasm/examples/src/1_advanced/5_custom_resolution.ts @@ -0,0 +1,73 @@ +import { Bip39 } from "@iota/crypto.js"; +import { Client, MnemonicSecretManager } from "@iota/iota-client-wasm/node"; +import { CoreDocument, IotaDocument, IotaIdentityClient, Resolver } from "../../../node"; +import { API_ENDPOINT, createDid } from "../util"; + +// Use this external package to avoid implementing the entire did:key method in this example. +import * as ed25519 from "@transmute/did-key-ed25519"; + +/** Demonstrates how to set up a resolver using custom handlers. + */ +export async function customResolution() { + // Set up a handler for resolving Ed25519 did:key + const keyHandler = async function(didKey: string): Promise { + let document = await ed25519.resolve( + didKey, + { accept: "application/did+ld+json" }, + ); + return CoreDocument.fromJSON(document.didDocument); + }; + + // Create a new client to interact with the IOTA ledger. + const client = new Client({ + primaryNode: API_ENDPOINT, + localPow: true, + }); + const didClient = new IotaIdentityClient(client); + + // Construct a Resolver capable of resolving the did:key and iota methods. + let handlerMap: Map Promise> = new Map(); + handlerMap.set("key", keyHandler); + + const resolver = new Resolver( + { + client: didClient, + handlers: handlerMap, + }, + ); + + // A valid Ed25519 did:key value taken from https://w3c-ccg.github.io/did-method-key/#example-1-a-simple-ed25519-did-key-value. + const didKey = "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"; + + // Generate a random mnemonic for our wallet. + const secretManager: MnemonicSecretManager = { + Mnemonic: Bip39.randomMnemonic(), + }; + + // Creates a new wallet and identity for us to resolve (see "0_create_did" example). + const { did } = await createDid(client, secretManager); + + // Resolve didKey into a DID document. + const didKeyDoc = await resolver.resolve(didKey); + + // Resolve the DID we created on the IOTA ledger. + const didIotaDoc = await resolver.resolve(did.toString()); + + // Check that the types of the resolved documents match our expectations: + + if (didKeyDoc instanceof CoreDocument) { + console.log("Resolved DID Key document:", JSON.stringify(didKeyDoc, null, 2)); + } else { + throw new Error( + "the resolved document type should match the output type of keyHandler", + ); + } + + if (didIotaDoc instanceof IotaDocument) { + console.log("Resolved IOTA DID document:", JSON.stringify(didIotaDoc, null, 2)); + } else { + throw new Error( + "the resolved document type should match IotaDocument", + ); + } +} diff --git a/bindings/wasm/examples/src/main.ts b/bindings/wasm/examples/src/main.ts index e00e60fe69..98e014427a 100644 --- a/bindings/wasm/examples/src/main.ts +++ b/bindings/wasm/examples/src/main.ts @@ -11,6 +11,7 @@ import { didIssuesNft } from "./1_advanced/1_did_issues_nft"; import { nftOwnsDid } from "./1_advanced/2_nft_owns_did"; import { didIssuesTokens } from "./1_advanced/3_did_issues_tokens"; import { keyExchange } from "./1_advanced/4_key_exchange"; +import { customResolution } from "./1_advanced/5_custom_resolution"; async function main() { // Extract example name. @@ -40,6 +41,8 @@ async function main() { return await didIssuesTokens(); case "4_key_exchange": return await keyExchange(); + case "5_custom_resolution": + return await customResolution(); default: throw "Unknown example name: '" + argument + "'"; } diff --git a/bindings/wasm/examples/src/tests/5_custom_resolution.ts b/bindings/wasm/examples/src/tests/5_custom_resolution.ts new file mode 100644 index 0000000000..76d8bc601a --- /dev/null +++ b/bindings/wasm/examples/src/tests/5_custom_resolution.ts @@ -0,0 +1,8 @@ +import { customResolution } from "../1_advanced/5_custom_resolution"; + +// Only verifies that no uncaught exceptions are thrown, including syntax errors etc. +describe("Test node examples", function() { + it("Custom Resolution", async () => { + await customResolution(); + }); +}); diff --git a/bindings/wasm/package-lock.json b/bindings/wasm/package-lock.json index 4ff18276d4..3610ab1f41 100644 --- a/bindings/wasm/package-lock.json +++ b/bindings/wasm/package-lock.json @@ -16,6 +16,7 @@ "devDependencies": { "@iota/crypto.js": "^1.9.0-stardust.6", "@iota/util.js": "^1.9.0-stardust.5", + "@transmute/did-key-ed25519": "0.3.0-unstable.9", "@types/mocha": "^9.1.0", "big-integer": "^1.6.51", "concurrently": "^7.0.0", @@ -138,12 +139,51 @@ "ms": "^2.1.1" } }, + "node_modules/@did-core/data-model": { + "version": "0.1.1-unstable.15", + "resolved": "https://registry.npmjs.org/@did-core/data-model/-/data-model-0.1.1-unstable.15.tgz", + "integrity": "sha512-l7gxLxegcXW7389G+j6o+S24lS8uasmJx5txWpW3QadNvOawKwvWn8bV59SdHSK806xNzIZaCLKmXKxebs8yAQ==", + "dev": true, + "dependencies": { + "factory.ts": "^0.5.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@did-core/did-ld-json": { + "version": "0.1.1-unstable.15", + "resolved": "https://registry.npmjs.org/@did-core/did-ld-json/-/did-ld-json-0.1.1-unstable.15.tgz", + "integrity": "sha512-p2jKRxSU+eJJqd+ewCklYp/XZ6ysISk8VU2/kANCoB/WwUy/kVgw2rUNScRDXw2utr9Qj36P8EZTYi4aj7vRCQ==", + "dev": true, + "dependencies": { + "@transmute/did-context": "^0.6.1-unstable.25", + "jsonld-checker": "^0.1.6" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@digitalbazaar/http-client": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@digitalbazaar/http-client/-/http-client-1.2.0.tgz", + "integrity": "sha512-W9KQQ5pUJcaR0I4c2HPJC0a7kRbZApIorZgPnEDwMBgj16iQzutGLrCXYaZOmxqVLVNqqlQ4aUJh+HBQZy4W6Q==", + "dev": true, + "dependencies": { + "esm": "^3.2.22", + "ky": "^0.25.1", + "ky-universal": "^0.8.2" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/@iota/crypto.js": { - "version": "1.9.0-stardust.7", - "resolved": "https://registry.npmjs.org/@iota/crypto.js/-/crypto.js-1.9.0-stardust.7.tgz", - "integrity": "sha512-GiUByLYpLuf9i+Cpm5lFPQ9CnO0t54FXWKxf0iDPkqaI5smpBMGKHpqPuk4tAiSvUsIcZ4QIVIgBLgYuOssSCQ==", + "version": "1.9.0-stardust.8", + "resolved": "https://registry.npmjs.org/@iota/crypto.js/-/crypto.js-1.9.0-stardust.8.tgz", + "integrity": "sha512-86bAc7eXoM/2713ZBbf0xV9D03Z9ikhEG5f1ZauZtuujHQITLg3svRQzrKnboZE5tiNzghC7qt2DfD0VySBabA==", "dependencies": { - "@iota/util.js": "^1.9.0-stardust.5", + "@iota/util.js": "next", "big-integer": "^1.6.51" }, "engines": { @@ -165,13 +205,13 @@ } }, "node_modules/@iota/iota.js": { - "version": "1.9.0-stardust.25", - "resolved": "https://registry.npmjs.org/@iota/iota.js/-/iota.js-1.9.0-stardust.25.tgz", - "integrity": "sha512-mx/ZpkrUEgPjd1FxVxDKGpmDF/aTuUcRXjzZWjBL8v2w9dn3/Bs2fZ3AJw7wIwMG6v48AMXWq2yPwndpwDx/qw==", + "version": "1.9.0-stardust.27", + "resolved": "https://registry.npmjs.org/@iota/iota.js/-/iota.js-1.9.0-stardust.27.tgz", + "integrity": "sha512-PgJV74krVXm67bLAl77OCjWKeSJ9Zg3uqJMSJ/pmq+pVwpN7Nb4KqY0abWCGmIuM5yvn0mXD4MOWywphUE2xeg==", "peer": true, "dependencies": { - "@iota/crypto.js": "^1.9.0-stardust.6", - "@iota/util.js": "^1.9.0-stardust.5", + "@iota/crypto.js": "next", + "@iota/util.js": "next", "big-integer": "^1.6.51", "node-fetch": "2.6.7" }, @@ -180,9 +220,9 @@ } }, "node_modules/@iota/types": { - "version": "1.0.0-beta.12", - "resolved": "https://registry.npmjs.org/@iota/types/-/types-1.0.0-beta.12.tgz", - "integrity": "sha512-GNIvPBauq8jAuOG7Xyb0W08KV9J0jeZ08igcl6sj3vSSiDb5360tON1NShdCCLfql4vr7n6U5f9Gmv0uSRuTYA==" + "version": "1.0.0-beta.14", + "resolved": "https://registry.npmjs.org/@iota/types/-/types-1.0.0-beta.14.tgz", + "integrity": "sha512-fFO7iklliawrZIAFYRc6P1d+aGhseKMYHBUyg5M+kDQCrXlghwdlbTVT7QqEFsai1U0x0CsVSibVbu6FuQLN/w==" }, "node_modules/@iota/util.js": { "version": "1.9.0-stardust.5", @@ -291,6 +331,165 @@ "node": ">= 8" } }, + "node_modules/@stablelib/binary": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/binary/-/binary-1.0.1.tgz", + "integrity": "sha512-ClJWvmL6UBM/wjkvv/7m5VP3GMr9t0osr4yVgLZsLCOz4hGN9gIAFEqnJ0TsSMAN+n840nf2cHZnA5/KFqHC7Q==", + "dev": true, + "dependencies": { + "@stablelib/int": "^1.0.1" + } + }, + "node_modules/@stablelib/bytes": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/bytes/-/bytes-1.0.1.tgz", + "integrity": "sha512-Kre4Y4kdwuqL8BR2E9hV/R5sOrUj6NanZaZis0V6lX5yzqC3hBuVSDXUIBqQv/sCpmuWRiHLwqiT1pqqjuBXoQ==", + "dev": true + }, + "node_modules/@stablelib/ed25519": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@stablelib/ed25519/-/ed25519-1.0.3.tgz", + "integrity": "sha512-puIMWaX9QlRsbhxfDc5i+mNPMY+0TmQEskunY1rZEBPi1acBCVQAhnsk/1Hk50DGPtVsZtAWQg4NHGlVaO9Hqg==", + "dev": true, + "dependencies": { + "@stablelib/random": "^1.0.2", + "@stablelib/sha512": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@stablelib/hash": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/hash/-/hash-1.0.1.tgz", + "integrity": "sha512-eTPJc/stDkdtOcrNMZ6mcMK1e6yBbqRBaNW55XA1jU8w/7QdnCF0CmMmOD1m7VSkBR44PWrMHU2l6r8YEQHMgg==", + "dev": true + }, + "node_modules/@stablelib/int": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/int/-/int-1.0.1.tgz", + "integrity": "sha512-byr69X/sDtDiIjIV6m4roLVWnNNlRGzsvxw+agj8CIEazqWGOQp2dTYgQhtyVXV9wpO6WyXRQUzLV/JRNumT2w==", + "dev": true + }, + "node_modules/@stablelib/keyagreement": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/keyagreement/-/keyagreement-1.0.1.tgz", + "integrity": "sha512-VKL6xBwgJnI6l1jKrBAfn265cspaWBPAPEc62VBQrWHLqVgNRE09gQ/AnOEyKUWrrqfD+xSQ3u42gJjLDdMDQg==", + "dev": true, + "dependencies": { + "@stablelib/bytes": "^1.0.1" + } + }, + "node_modules/@stablelib/random": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@stablelib/random/-/random-1.0.2.tgz", + "integrity": "sha512-rIsE83Xpb7clHPVRlBj8qNe5L8ISQOzjghYQm/dZ7VaM2KHYwMW5adjQjrzTZCchFnNCNhkwtnOBa9HTMJCI8w==", + "dev": true, + "dependencies": { + "@stablelib/binary": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@stablelib/sha512": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/sha512/-/sha512-1.0.1.tgz", + "integrity": "sha512-13gl/iawHV9zvDKciLo1fQ8Bgn2Pvf7OV6amaRVKiq3pjQ3UmEpXxWiAfV8tYjUpeZroBxtyrwtdooQT/i3hzw==", + "dev": true, + "dependencies": { + "@stablelib/binary": "^1.0.1", + "@stablelib/hash": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@stablelib/wipe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/wipe/-/wipe-1.0.1.tgz", + "integrity": "sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg==", + "dev": true + }, + "node_modules/@stablelib/x25519": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@stablelib/x25519/-/x25519-1.0.3.tgz", + "integrity": "sha512-KnTbKmUhPhHavzobclVJQG5kuivH+qDLpe84iRqX3CLrKp881cF160JvXJ+hjn1aMyCwYOKeIZefIH/P5cJoRw==", + "dev": true, + "dependencies": { + "@stablelib/keyagreement": "^1.0.1", + "@stablelib/random": "^1.0.2", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@transmute/did-context": { + "version": "0.6.1-unstable.37", + "resolved": "https://registry.npmjs.org/@transmute/did-context/-/did-context-0.6.1-unstable.37.tgz", + "integrity": "sha512-p/QnG3QKS4218hjIDgdvJOFATCXsAnZKgy4egqRrJLlo3Y6OaDBg7cA73dixOwUPoEKob0K6rLIGcsCI/L1acw==", + "dev": true + }, + "node_modules/@transmute/did-key-common": { + "version": "0.3.0-unstable.9", + "resolved": "https://registry.npmjs.org/@transmute/did-key-common/-/did-key-common-0.3.0-unstable.9.tgz", + "integrity": "sha512-qGzFJA615Gu/UnAvuSNrRvtCCKRVvxCmuDawenudyGR8X8WkkJ19uD8kI0GaCzZazpX1SWiairM87nKJ9aH7Tw==", + "dev": true, + "dependencies": { + "@did-core/data-model": "^0.1.1-unstable.13", + "@did-core/did-ld-json": "^0.1.1-unstable.13", + "@transmute/did-context": "^0.6.1-unstable.36", + "@transmute/ld-key-pair": "^0.6.1-unstable.36", + "@transmute/security-context": "^0.6.1-unstable.36" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@transmute/did-key-ed25519": { + "version": "0.3.0-unstable.9", + "resolved": "https://registry.npmjs.org/@transmute/did-key-ed25519/-/did-key-ed25519-0.3.0-unstable.9.tgz", + "integrity": "sha512-mFTTL1IHp26JweHN/SCj2Re5iBr5sWbyctd5LRoHRU1DQB0XmBBFX5ZzCCtnEiBGvDF55Eyx1vpkwUHIcD3QGg==", + "dev": true, + "dependencies": { + "@transmute/did-key-common": "^0.3.0-unstable.9", + "@transmute/ed25519-key-pair": "^0.6.1-unstable.37" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@transmute/ed25519-key-pair": { + "version": "0.6.1-unstable.37", + "resolved": "https://registry.npmjs.org/@transmute/ed25519-key-pair/-/ed25519-key-pair-0.6.1-unstable.37.tgz", + "integrity": "sha512-l34yzE/QnQwmdk5xY9g2kD55e4XPp/jTZQzPu7I6J4Ar+bMaL/0RLL/pgvwyI7qUpsddxRf4WPZCCcZveqPcdA==", + "dev": true, + "dependencies": { + "@stablelib/ed25519": "^1.0.1", + "@transmute/ld-key-pair": "^0.6.1-unstable.37", + "@transmute/x25519-key-pair": "^0.6.1-unstable.37" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@transmute/ld-key-pair": { + "version": "0.6.1-unstable.37", + "resolved": "https://registry.npmjs.org/@transmute/ld-key-pair/-/ld-key-pair-0.6.1-unstable.37.tgz", + "integrity": "sha512-DcTpEruAQBfOd2laZkg3uCQ+67Y7dw2hsvo42NAQ5tItCIx5AClP7zccri7T2JUcfDUFaE32z/BLTMEKYt3XZQ==", + "dev": true + }, + "node_modules/@transmute/security-context": { + "version": "0.6.1-unstable.37", + "resolved": "https://registry.npmjs.org/@transmute/security-context/-/security-context-0.6.1-unstable.37.tgz", + "integrity": "sha512-GtLmG65qlORrz/2S4I74DT+vA4+qXsFxrMr0cNOXjUqZBd/AW1PTrFnryLF9907BfoiD58HC9qb1WVGWjSlBYw==", + "dev": true + }, + "node_modules/@transmute/x25519-key-pair": { + "version": "0.6.1-unstable.37", + "resolved": "https://registry.npmjs.org/@transmute/x25519-key-pair/-/x25519-key-pair-0.6.1-unstable.37.tgz", + "integrity": "sha512-j6zR9IoJmgVhUCVH8YVGpsgQf99SxPKZ00LGnUheBAQzgj2lULGBQ44G+GqBCdzfT0qweptTfp1RjqqHEpizeA==", + "dev": true, + "dependencies": { + "@stablelib/x25519": "^1.0.0", + "@transmute/ld-key-pair": "^0.6.1-unstable.37" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@tsconfig/node10": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", @@ -632,6 +831,18 @@ "dev": true, "peer": true }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dev": true, + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/acorn": { "version": "8.8.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", @@ -1169,9 +1380,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001402", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001402.tgz", - "integrity": "sha512-Mx4MlhXO5NwuvXGgVb+hg65HZ+bhUYsz8QtDGDo2QmaJS2GBX47Xfi2koL86lc8K+l+htXeTEB/Aeqvezoo6Ew==", + "version": "1.0.30001409", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001409.tgz", + "integrity": "sha512-V0mnJ5dwarmhYv8/MzhJ//aW68UpvnQBXv8lJ2QUsvn2pHcmAuNtu8hQEDz37XnA1iE+lRR9CIfGWWpgJ5QedQ==", "dev": true, "funding": [ { @@ -1185,6 +1396,12 @@ ], "peer": true }, + "node_modules/canonicalize": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/canonicalize/-/canonicalize-1.0.8.tgz", + "integrity": "sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A==", + "dev": true + }, "node_modules/caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -1365,6 +1582,20 @@ "wrap-ansi": "^7.0.0" } }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/collect-all": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/collect-all/-/collect-all-1.0.4.tgz", @@ -2128,6 +2359,15 @@ "node": ">=0.10" } }, + "node_modules/data-uri-to-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz", + "integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/date-fns": { "version": "2.29.3", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.3.tgz", @@ -2276,9 +2516,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.253", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.253.tgz", - "integrity": "sha512-1pezJ2E1UyBTGbA7fUlHdPSXQw1k+82VhTFLG5G0AUqLGvsZqFzleOblceqegZzxYX4kC7hGEEdzIQI9RZ1Cuw==", + "version": "1.4.256", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.256.tgz", + "integrity": "sha512-x+JnqyluoJv8I0U9gVe+Sk2st8vF0CzMt78SXxuoWCooLLY2k5VerIBdpvG7ql6GKI4dzNnPjmqgDJ76EdaAKw==", "dev": true, "peer": true }, @@ -2380,6 +2620,15 @@ "node": ">=8.0.0" } }, + "node_modules/esm": { + "version": "3.2.25", + "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", + "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", @@ -2413,6 +2662,15 @@ "node": ">=4.0" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/eventemitter2": { "version": "6.4.7", "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.7.tgz", @@ -2505,6 +2763,19 @@ "node >=0.6.0" ] }, + "node_modules/factory.ts": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/factory.ts/-/factory.ts-0.5.2.tgz", + "integrity": "sha512-I4YDKuyMW+s2PocnWh/Ekv9wSStt/MNN1ZRb1qhy0Kv056ndlzbLHDsW9KEmTAqMpLI3BtjSqEdZ7ZfdnaXn9w==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "source-map-support": "^0.5.19" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -2551,6 +2822,20 @@ "pend": "~1.2.0" } }, + "node_modules/fetch-blob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-2.1.2.tgz", + "integrity": "sha512-YKqtUDwqLyfyMnmbw8XD6Q8j9i/HggKtPEI+pZ1+8bvheBu78biSmNaXWusx1TauGqtUUGx/cBb1mKdq2rLYow==", + "dev": true, + "engines": { + "node": "^10.17.0 || >=12.3.0" + }, + "peerDependenciesMeta": { + "domexception": { + "optional": true + } + } + }, "node_modules/figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -3208,6 +3493,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -3244,6 +3541,15 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -3437,6 +3743,34 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jsonld": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/jsonld/-/jsonld-5.2.0.tgz", + "integrity": "sha512-JymgT6Xzk5CHEmHuEyvoTNviEPxv6ihLWSPu1gFdtjSAyM6cFqNrv02yS/SIur3BBIkCf0HjizRc24d8/FfQKw==", + "dev": true, + "dependencies": { + "@digitalbazaar/http-client": "^1.1.0", + "canonicalize": "^1.0.1", + "lru-cache": "^6.0.0", + "rdf-canonize": "^3.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/jsonld-checker": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/jsonld-checker/-/jsonld-checker-0.1.8.tgz", + "integrity": "sha512-jclmnPRrm5SEpaIV6IiSTJxplRAqIWHduQLsUfrYpZM41Ng48m1RN2/aUyHze/ynfO0D2UhlJBt8SdObsH5GBw==", + "dev": true, + "dependencies": { + "jsonld": "^5.2.0", + "node-fetch": "^2.6.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/jsprim": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", @@ -3452,6 +3786,15 @@ "verror": "1.10.0" } }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/klaw": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", @@ -3470,6 +3813,60 @@ "node": ">=6" } }, + "node_modules/ky": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/ky/-/ky-0.25.1.tgz", + "integrity": "sha512-PjpCEWlIU7VpiMVrTwssahkYXX1by6NCT0fhTUX34F3DTinARlgMpriuroolugFPcMgpPWrOW4mTb984Qm1RXA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/ky?sponsor=1" + } + }, + "node_modules/ky-universal": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/ky-universal/-/ky-universal-0.8.2.tgz", + "integrity": "sha512-xe0JaOH9QeYxdyGLnzUOVGK4Z6FGvDVzcXFTdrYA1f33MZdEa45sUDaMBy98xQMcsd2XIBrTXRrRYnegcSdgVQ==", + "dev": true, + "dependencies": { + "abort-controller": "^3.0.0", + "node-fetch": "3.0.0-beta.9" + }, + "engines": { + "node": ">=10.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/ky-universal?sponsor=1" + }, + "peerDependencies": { + "ky": ">=0.17.0", + "web-streams-polyfill": ">=2.0.0" + }, + "peerDependenciesMeta": { + "web-streams-polyfill": { + "optional": true + } + } + }, + "node_modules/ky-universal/node_modules/node-fetch": { + "version": "3.0.0-beta.9", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.0.0-beta.9.tgz", + "integrity": "sha512-RdbZCEynH2tH46+tj0ua9caUHVWrd/RHnRfvly2EVdqGmI3ndS1Vn/xjm5KuGejDt2RNDQsVRLPNd2QPwcewVg==", + "dev": true, + "dependencies": { + "data-uri-to-buffer": "^3.0.1", + "fetch-blob": "^2.1.1" + }, + "engines": { + "node": "^10.17 || >=12.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, "node_modules/lazy-ass": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", @@ -4904,6 +5301,18 @@ "safe-buffer": "^5.1.0" } }, + "node_modules/rdf-canonize": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/rdf-canonize/-/rdf-canonize-3.3.0.tgz", + "integrity": "sha512-gfSNkMua/VWC1eYbSkVaL/9LQhFeOh0QULwv7Or0f+po8pMgQ1blYQFe1r9Mv2GJZXw88Cz/drnAnB9UlNnHfQ==", + "dev": true, + "dependencies": { + "setimmediate": "^1.0.5" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -5230,6 +5639,24 @@ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -5997,9 +6424,9 @@ "dev": true }, "node_modules/uglify-js": { - "version": "3.17.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.0.tgz", - "integrity": "sha512-aTeNPVmgIMPpm1cxXr2Q/nEbvkmV8yq66F3om7X3P/cvOXQ0TMQ64Wk63iyT1gPlmdmGzjGpyLh1f3y8MZWXGg==", + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.1.tgz", + "integrity": "sha512-+juFBsLLw7AqMaqJ0GFvlsGZwdQfI2ooKQB39PSBgMnMakcFosi9O8jCwE+2/2nMNcc0z63r9mwjoDG8zr+q0Q==", "dev": true, "optional": true, "bin": { @@ -6656,12 +7083,42 @@ } } }, + "@did-core/data-model": { + "version": "0.1.1-unstable.15", + "resolved": "https://registry.npmjs.org/@did-core/data-model/-/data-model-0.1.1-unstable.15.tgz", + "integrity": "sha512-l7gxLxegcXW7389G+j6o+S24lS8uasmJx5txWpW3QadNvOawKwvWn8bV59SdHSK806xNzIZaCLKmXKxebs8yAQ==", + "dev": true, + "requires": { + "factory.ts": "^0.5.1" + } + }, + "@did-core/did-ld-json": { + "version": "0.1.1-unstable.15", + "resolved": "https://registry.npmjs.org/@did-core/did-ld-json/-/did-ld-json-0.1.1-unstable.15.tgz", + "integrity": "sha512-p2jKRxSU+eJJqd+ewCklYp/XZ6ysISk8VU2/kANCoB/WwUy/kVgw2rUNScRDXw2utr9Qj36P8EZTYi4aj7vRCQ==", + "dev": true, + "requires": { + "@transmute/did-context": "^0.6.1-unstable.25", + "jsonld-checker": "^0.1.6" + } + }, + "@digitalbazaar/http-client": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@digitalbazaar/http-client/-/http-client-1.2.0.tgz", + "integrity": "sha512-W9KQQ5pUJcaR0I4c2HPJC0a7kRbZApIorZgPnEDwMBgj16iQzutGLrCXYaZOmxqVLVNqqlQ4aUJh+HBQZy4W6Q==", + "dev": true, + "requires": { + "esm": "^3.2.22", + "ky": "^0.25.1", + "ky-universal": "^0.8.2" + } + }, "@iota/crypto.js": { - "version": "1.9.0-stardust.7", - "resolved": "https://registry.npmjs.org/@iota/crypto.js/-/crypto.js-1.9.0-stardust.7.tgz", - "integrity": "sha512-GiUByLYpLuf9i+Cpm5lFPQ9CnO0t54FXWKxf0iDPkqaI5smpBMGKHpqPuk4tAiSvUsIcZ4QIVIgBLgYuOssSCQ==", + "version": "1.9.0-stardust.8", + "resolved": "https://registry.npmjs.org/@iota/crypto.js/-/crypto.js-1.9.0-stardust.8.tgz", + "integrity": "sha512-86bAc7eXoM/2713ZBbf0xV9D03Z9ikhEG5f1ZauZtuujHQITLg3svRQzrKnboZE5tiNzghC7qt2DfD0VySBabA==", "requires": { - "@iota/util.js": "^1.9.0-stardust.5", + "@iota/util.js": "next", "big-integer": "^1.6.51" } }, @@ -6677,21 +7134,21 @@ } }, "@iota/iota.js": { - "version": "1.9.0-stardust.25", - "resolved": "https://registry.npmjs.org/@iota/iota.js/-/iota.js-1.9.0-stardust.25.tgz", - "integrity": "sha512-mx/ZpkrUEgPjd1FxVxDKGpmDF/aTuUcRXjzZWjBL8v2w9dn3/Bs2fZ3AJw7wIwMG6v48AMXWq2yPwndpwDx/qw==", + "version": "1.9.0-stardust.27", + "resolved": "https://registry.npmjs.org/@iota/iota.js/-/iota.js-1.9.0-stardust.27.tgz", + "integrity": "sha512-PgJV74krVXm67bLAl77OCjWKeSJ9Zg3uqJMSJ/pmq+pVwpN7Nb4KqY0abWCGmIuM5yvn0mXD4MOWywphUE2xeg==", "peer": true, "requires": { - "@iota/crypto.js": "^1.9.0-stardust.6", - "@iota/util.js": "^1.9.0-stardust.5", + "@iota/crypto.js": "next", + "@iota/util.js": "next", "big-integer": "^1.6.51", "node-fetch": "2.6.7" } }, "@iota/types": { - "version": "1.0.0-beta.12", - "resolved": "https://registry.npmjs.org/@iota/types/-/types-1.0.0-beta.12.tgz", - "integrity": "sha512-GNIvPBauq8jAuOG7Xyb0W08KV9J0jeZ08igcl6sj3vSSiDb5360tON1NShdCCLfql4vr7n6U5f9Gmv0uSRuTYA==" + "version": "1.0.0-beta.14", + "resolved": "https://registry.npmjs.org/@iota/types/-/types-1.0.0-beta.14.tgz", + "integrity": "sha512-fFO7iklliawrZIAFYRc6P1d+aGhseKMYHBUyg5M+kDQCrXlghwdlbTVT7QqEFsai1U0x0CsVSibVbu6FuQLN/w==" }, "@iota/util.js": { "version": "1.9.0-stardust.5", @@ -6779,6 +7236,153 @@ "fastq": "^1.6.0" } }, + "@stablelib/binary": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/binary/-/binary-1.0.1.tgz", + "integrity": "sha512-ClJWvmL6UBM/wjkvv/7m5VP3GMr9t0osr4yVgLZsLCOz4hGN9gIAFEqnJ0TsSMAN+n840nf2cHZnA5/KFqHC7Q==", + "dev": true, + "requires": { + "@stablelib/int": "^1.0.1" + } + }, + "@stablelib/bytes": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/bytes/-/bytes-1.0.1.tgz", + "integrity": "sha512-Kre4Y4kdwuqL8BR2E9hV/R5sOrUj6NanZaZis0V6lX5yzqC3hBuVSDXUIBqQv/sCpmuWRiHLwqiT1pqqjuBXoQ==", + "dev": true + }, + "@stablelib/ed25519": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@stablelib/ed25519/-/ed25519-1.0.3.tgz", + "integrity": "sha512-puIMWaX9QlRsbhxfDc5i+mNPMY+0TmQEskunY1rZEBPi1acBCVQAhnsk/1Hk50DGPtVsZtAWQg4NHGlVaO9Hqg==", + "dev": true, + "requires": { + "@stablelib/random": "^1.0.2", + "@stablelib/sha512": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "@stablelib/hash": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/hash/-/hash-1.0.1.tgz", + "integrity": "sha512-eTPJc/stDkdtOcrNMZ6mcMK1e6yBbqRBaNW55XA1jU8w/7QdnCF0CmMmOD1m7VSkBR44PWrMHU2l6r8YEQHMgg==", + "dev": true + }, + "@stablelib/int": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/int/-/int-1.0.1.tgz", + "integrity": "sha512-byr69X/sDtDiIjIV6m4roLVWnNNlRGzsvxw+agj8CIEazqWGOQp2dTYgQhtyVXV9wpO6WyXRQUzLV/JRNumT2w==", + "dev": true + }, + "@stablelib/keyagreement": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/keyagreement/-/keyagreement-1.0.1.tgz", + "integrity": "sha512-VKL6xBwgJnI6l1jKrBAfn265cspaWBPAPEc62VBQrWHLqVgNRE09gQ/AnOEyKUWrrqfD+xSQ3u42gJjLDdMDQg==", + "dev": true, + "requires": { + "@stablelib/bytes": "^1.0.1" + } + }, + "@stablelib/random": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@stablelib/random/-/random-1.0.2.tgz", + "integrity": "sha512-rIsE83Xpb7clHPVRlBj8qNe5L8ISQOzjghYQm/dZ7VaM2KHYwMW5adjQjrzTZCchFnNCNhkwtnOBa9HTMJCI8w==", + "dev": true, + "requires": { + "@stablelib/binary": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "@stablelib/sha512": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/sha512/-/sha512-1.0.1.tgz", + "integrity": "sha512-13gl/iawHV9zvDKciLo1fQ8Bgn2Pvf7OV6amaRVKiq3pjQ3UmEpXxWiAfV8tYjUpeZroBxtyrwtdooQT/i3hzw==", + "dev": true, + "requires": { + "@stablelib/binary": "^1.0.1", + "@stablelib/hash": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "@stablelib/wipe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/wipe/-/wipe-1.0.1.tgz", + "integrity": "sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg==", + "dev": true + }, + "@stablelib/x25519": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@stablelib/x25519/-/x25519-1.0.3.tgz", + "integrity": "sha512-KnTbKmUhPhHavzobclVJQG5kuivH+qDLpe84iRqX3CLrKp881cF160JvXJ+hjn1aMyCwYOKeIZefIH/P5cJoRw==", + "dev": true, + "requires": { + "@stablelib/keyagreement": "^1.0.1", + "@stablelib/random": "^1.0.2", + "@stablelib/wipe": "^1.0.1" + } + }, + "@transmute/did-context": { + "version": "0.6.1-unstable.37", + "resolved": "https://registry.npmjs.org/@transmute/did-context/-/did-context-0.6.1-unstable.37.tgz", + "integrity": "sha512-p/QnG3QKS4218hjIDgdvJOFATCXsAnZKgy4egqRrJLlo3Y6OaDBg7cA73dixOwUPoEKob0K6rLIGcsCI/L1acw==", + "dev": true + }, + "@transmute/did-key-common": { + "version": "0.3.0-unstable.9", + "resolved": "https://registry.npmjs.org/@transmute/did-key-common/-/did-key-common-0.3.0-unstable.9.tgz", + "integrity": "sha512-qGzFJA615Gu/UnAvuSNrRvtCCKRVvxCmuDawenudyGR8X8WkkJ19uD8kI0GaCzZazpX1SWiairM87nKJ9aH7Tw==", + "dev": true, + "requires": { + "@did-core/data-model": "^0.1.1-unstable.13", + "@did-core/did-ld-json": "^0.1.1-unstable.13", + "@transmute/did-context": "^0.6.1-unstable.36", + "@transmute/ld-key-pair": "^0.6.1-unstable.36", + "@transmute/security-context": "^0.6.1-unstable.36" + } + }, + "@transmute/did-key-ed25519": { + "version": "0.3.0-unstable.9", + "resolved": "https://registry.npmjs.org/@transmute/did-key-ed25519/-/did-key-ed25519-0.3.0-unstable.9.tgz", + "integrity": "sha512-mFTTL1IHp26JweHN/SCj2Re5iBr5sWbyctd5LRoHRU1DQB0XmBBFX5ZzCCtnEiBGvDF55Eyx1vpkwUHIcD3QGg==", + "dev": true, + "requires": { + "@transmute/did-key-common": "^0.3.0-unstable.9", + "@transmute/ed25519-key-pair": "^0.6.1-unstable.37" + } + }, + "@transmute/ed25519-key-pair": { + "version": "0.6.1-unstable.37", + "resolved": "https://registry.npmjs.org/@transmute/ed25519-key-pair/-/ed25519-key-pair-0.6.1-unstable.37.tgz", + "integrity": "sha512-l34yzE/QnQwmdk5xY9g2kD55e4XPp/jTZQzPu7I6J4Ar+bMaL/0RLL/pgvwyI7qUpsddxRf4WPZCCcZveqPcdA==", + "dev": true, + "requires": { + "@stablelib/ed25519": "^1.0.1", + "@transmute/ld-key-pair": "^0.6.1-unstable.37", + "@transmute/x25519-key-pair": "^0.6.1-unstable.37" + } + }, + "@transmute/ld-key-pair": { + "version": "0.6.1-unstable.37", + "resolved": "https://registry.npmjs.org/@transmute/ld-key-pair/-/ld-key-pair-0.6.1-unstable.37.tgz", + "integrity": "sha512-DcTpEruAQBfOd2laZkg3uCQ+67Y7dw2hsvo42NAQ5tItCIx5AClP7zccri7T2JUcfDUFaE32z/BLTMEKYt3XZQ==", + "dev": true + }, + "@transmute/security-context": { + "version": "0.6.1-unstable.37", + "resolved": "https://registry.npmjs.org/@transmute/security-context/-/security-context-0.6.1-unstable.37.tgz", + "integrity": "sha512-GtLmG65qlORrz/2S4I74DT+vA4+qXsFxrMr0cNOXjUqZBd/AW1PTrFnryLF9907BfoiD58HC9qb1WVGWjSlBYw==", + "dev": true + }, + "@transmute/x25519-key-pair": { + "version": "0.6.1-unstable.37", + "resolved": "https://registry.npmjs.org/@transmute/x25519-key-pair/-/x25519-key-pair-0.6.1-unstable.37.tgz", + "integrity": "sha512-j6zR9IoJmgVhUCVH8YVGpsgQf99SxPKZ00LGnUheBAQzgj2lULGBQ44G+GqBCdzfT0qweptTfp1RjqqHEpizeA==", + "dev": true, + "requires": { + "@stablelib/x25519": "^1.0.0", + "@transmute/ld-key-pair": "^0.6.1-unstable.37" + } + }, "@tsconfig/node10": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", @@ -7120,6 +7724,15 @@ "dev": true, "peer": true }, + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dev": true, + "requires": { + "event-target-shim": "^5.0.0" + } + }, "acorn": { "version": "8.8.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", @@ -7497,12 +8110,18 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001402", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001402.tgz", - "integrity": "sha512-Mx4MlhXO5NwuvXGgVb+hg65HZ+bhUYsz8QtDGDo2QmaJS2GBX47Xfi2koL86lc8K+l+htXeTEB/Aeqvezoo6Ew==", + "version": "1.0.30001409", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001409.tgz", + "integrity": "sha512-V0mnJ5dwarmhYv8/MzhJ//aW68UpvnQBXv8lJ2QUsvn2pHcmAuNtu8hQEDz37XnA1iE+lRR9CIfGWWpgJ5QedQ==", "dev": true, "peer": true }, + "canonicalize": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/canonicalize/-/canonicalize-1.0.8.tgz", + "integrity": "sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A==", + "dev": true + }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -7632,6 +8251,17 @@ "wrap-ansi": "^7.0.0" } }, + "clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + } + }, "collect-all": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/collect-all/-/collect-all-1.0.4.tgz", @@ -8225,6 +8855,12 @@ "assert-plus": "^1.0.0" } }, + "data-uri-to-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz", + "integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==", + "dev": true + }, "date-fns": { "version": "2.29.3", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.3.tgz", @@ -8330,9 +8966,9 @@ } }, "electron-to-chromium": { - "version": "1.4.253", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.253.tgz", - "integrity": "sha512-1pezJ2E1UyBTGbA7fUlHdPSXQw1k+82VhTFLG5G0AUqLGvsZqFzleOblceqegZzxYX4kC7hGEEdzIQI9RZ1Cuw==", + "version": "1.4.256", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.256.tgz", + "integrity": "sha512-x+JnqyluoJv8I0U9gVe+Sk2st8vF0CzMt78SXxuoWCooLLY2k5VerIBdpvG7ql6GKI4dzNnPjmqgDJ76EdaAKw==", "dev": true, "peer": true }, @@ -8413,6 +9049,12 @@ "estraverse": "^4.1.1" } }, + "esm": { + "version": "3.2.25", + "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", + "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==", + "dev": true + }, "esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", @@ -8439,6 +9081,12 @@ "dev": true, "peer": true }, + "event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "dev": true + }, "eventemitter2": { "version": "6.4.7", "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.7.tgz", @@ -8508,6 +9156,16 @@ "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", "dev": true }, + "factory.ts": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/factory.ts/-/factory.ts-0.5.2.tgz", + "integrity": "sha512-I4YDKuyMW+s2PocnWh/Ekv9wSStt/MNN1ZRb1qhy0Kv056ndlzbLHDsW9KEmTAqMpLI3BtjSqEdZ7ZfdnaXn9w==", + "dev": true, + "requires": { + "clone-deep": "^4.0.1", + "source-map-support": "^0.5.19" + } + }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -8551,6 +9209,12 @@ "pend": "~1.2.0" } }, + "fetch-blob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-2.1.2.tgz", + "integrity": "sha512-YKqtUDwqLyfyMnmbw8XD6Q8j9i/HggKtPEI+pZ1+8bvheBu78biSmNaXWusx1TauGqtUUGx/cBb1mKdq2rLYow==", + "dev": true + }, "figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -8996,6 +9660,15 @@ "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", "dev": true }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, "is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -9020,6 +9693,12 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true + }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -9180,6 +9859,28 @@ "universalify": "^2.0.0" } }, + "jsonld": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/jsonld/-/jsonld-5.2.0.tgz", + "integrity": "sha512-JymgT6Xzk5CHEmHuEyvoTNviEPxv6ihLWSPu1gFdtjSAyM6cFqNrv02yS/SIur3BBIkCf0HjizRc24d8/FfQKw==", + "dev": true, + "requires": { + "@digitalbazaar/http-client": "^1.1.0", + "canonicalize": "^1.0.1", + "lru-cache": "^6.0.0", + "rdf-canonize": "^3.0.0" + } + }, + "jsonld-checker": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/jsonld-checker/-/jsonld-checker-0.1.8.tgz", + "integrity": "sha512-jclmnPRrm5SEpaIV6IiSTJxplRAqIWHduQLsUfrYpZM41Ng48m1RN2/aUyHze/ynfO0D2UhlJBt8SdObsH5GBw==", + "dev": true, + "requires": { + "jsonld": "^5.2.0", + "node-fetch": "^2.6.1" + } + }, "jsprim": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", @@ -9192,6 +9893,12 @@ "verror": "1.10.0" } }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, "klaw": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", @@ -9207,6 +9914,34 @@ "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", "dev": true }, + "ky": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/ky/-/ky-0.25.1.tgz", + "integrity": "sha512-PjpCEWlIU7VpiMVrTwssahkYXX1by6NCT0fhTUX34F3DTinARlgMpriuroolugFPcMgpPWrOW4mTb984Qm1RXA==", + "dev": true + }, + "ky-universal": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/ky-universal/-/ky-universal-0.8.2.tgz", + "integrity": "sha512-xe0JaOH9QeYxdyGLnzUOVGK4Z6FGvDVzcXFTdrYA1f33MZdEa45sUDaMBy98xQMcsd2XIBrTXRrRYnegcSdgVQ==", + "dev": true, + "requires": { + "abort-controller": "^3.0.0", + "node-fetch": "3.0.0-beta.9" + }, + "dependencies": { + "node-fetch": { + "version": "3.0.0-beta.9", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.0.0-beta.9.tgz", + "integrity": "sha512-RdbZCEynH2tH46+tj0ua9caUHVWrd/RHnRfvly2EVdqGmI3ndS1Vn/xjm5KuGejDt2RNDQsVRLPNd2QPwcewVg==", + "dev": true, + "requires": { + "data-uri-to-buffer": "^3.0.1", + "fetch-blob": "^2.1.1" + } + } + } + }, "lazy-ass": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", @@ -10173,6 +10908,15 @@ "safe-buffer": "^5.1.0" } }, + "rdf-canonize": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/rdf-canonize/-/rdf-canonize-3.3.0.tgz", + "integrity": "sha512-gfSNkMua/VWC1eYbSkVaL/9LQhFeOh0QULwv7Or0f+po8pMgQ1blYQFe1r9Mv2GJZXw88Cz/drnAnB9UlNnHfQ==", + "dev": true, + "requires": { + "setimmediate": "^1.0.5" + } + }, "readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -10412,6 +11156,21 @@ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true + }, + "shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "requires": { + "kind-of": "^6.0.2" + } + }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -10986,9 +11745,9 @@ "dev": true }, "uglify-js": { - "version": "3.17.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.0.tgz", - "integrity": "sha512-aTeNPVmgIMPpm1cxXr2Q/nEbvkmV8yq66F3om7X3P/cvOXQ0TMQ64Wk63iyT1gPlmdmGzjGpyLh1f3y8MZWXGg==", + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.1.tgz", + "integrity": "sha512-+juFBsLLw7AqMaqJ0GFvlsGZwdQfI2ooKQB39PSBgMnMakcFosi9O8jCwE+2/2nMNcc0z63r9mwjoDG8zr+q0Q==", "dev": true, "optional": true }, diff --git a/bindings/wasm/package.json b/bindings/wasm/package.json index ac4598dd5e..06b1ff19db 100644 --- a/bindings/wasm/package.json +++ b/bindings/wasm/package.json @@ -57,6 +57,7 @@ "big-integer": "^1.6.51", "@iota/crypto.js": "^1.9.0-stardust.6", "@iota/util.js": "^1.9.0-stardust.5", + "@transmute/did-key-ed25519": "0.3.0-unstable.9", "@types/mocha": "^9.1.0", "concurrently": "^7.0.0", "copy-webpack-plugin": "^7.0.0", diff --git a/examples/1_advanced/6_custom_resolution.rs b/examples/1_advanced/6_custom_resolution.rs new file mode 100644 index 0000000000..4da72bb09f --- /dev/null +++ b/examples/1_advanced/6_custom_resolution.rs @@ -0,0 +1,104 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use anyhow::Context; +// Use the external did-key crate to avoid implementing the entire DID key method in this example. +use did_key::Config; +use did_key::DIDCore; +use did_key::DIDKey; +use did_key::KeyPair; +use examples::create_did; +use examples::random_stronghold_path; +use examples::API_ENDPOINT; +use identity_iota::core::FromJson; +use identity_iota::core::ToJson; +use identity_iota::credential::AbstractThreadSafeValidatorDocument; +use identity_iota::did::CoreDID; +use identity_iota::did::CoreDocument; +use identity_iota::did::DID; +use identity_iota::iota::IotaDID; +use identity_iota::iota::IotaDocument; +use identity_iota::resolver::Resolver; +use iota_client::block::address::Address; +use iota_client::secret::stronghold::StrongholdSecretManager; +use iota_client::secret::SecretManager; +use iota_client::Client; + +/// Demonstrates how to set up a resolver using custom handlers. +/// +/// NOTE: Since both `IotaDocument` and `CoreDocument` implement `Into` we could have used +/// Resolver in this example and just worked with `CoreDocument` representations throughout. +#[tokio::main] +async fn main() -> anyhow::Result<()> { + // Create a method agnostic resolver and attach handlers for the "key" and "iota" methods. + let mut resolver: Resolver = Resolver::new(); + + // Create a new client to interact with the IOTA ledger. + let client: Client = Client::builder().with_primary_node(API_ENDPOINT, None)?.finish()?; + + // This is a convenience method for attaching a handler for the "iota" method by providing just a client. + resolver.attach_iota_handler(client.clone()); + resolver.attach_handler("key".to_owned(), resolve_ed25519_did_key); + + // A valid Ed25519 did:key value taken from https://w3c-ccg.github.io/did-method-key/#example-1-a-simple-ed25519-did-key-value. + let did_key: CoreDID = "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK".parse()?; + + // Create a new secret manager backed by a Stronghold. + let mut secret_manager: SecretManager = SecretManager::Stronghold( + StrongholdSecretManager::builder() + .password("secure_password") + .build(random_stronghold_path())?, + ); + + // Create a new DID for us to resolve. + let (_, iota_did): (Address, IotaDID) = create_did(&client, &mut secret_manager).await?; + + // Resolve did_key to get an abstract document. + let did_key_doc: AbstractThreadSafeValidatorDocument = resolver.resolve(&did_key).await?; + + // Resolve iota_did to get an abstract document. + let iota_doc: AbstractThreadSafeValidatorDocument = resolver.resolve(&iota_did).await?; + + // These documents are mainly meant for validating credentials and presentations, but one can also attempt to cast + // them to concrete document types. + + let did_key_doc: CoreDocument = *did_key_doc + .into_any() + .downcast::() + .expect("downcasting to the return type of the did:key handler should be fine"); + + println!("Resolved DID Key document: {}", did_key_doc.to_json_pretty()?); + + let iota_doc: IotaDocument = *iota_doc + .into_any() + .downcast::() + .expect("downcasting to the return type of the iota handler should be fine"); + println!("Resolved IOTA DID document: {}", iota_doc.to_json_pretty()?); + + Ok(()) +} + +/// Resolves an Ed25519 did:key DID to a spec compliant DID Document represented as a [`CoreDocument`]. +/// +/// # Errors +/// Errors if the DID is not a valid Ed25519 did:key. +async fn resolve_ed25519_did_key(did: CoreDID) -> anyhow::Result { + DIDKey::try_from(did.as_str()) + .map_err(|err| anyhow::anyhow!("the provided DID does not satisfy the did:key format: {:?}", err)) + .and_then(|key_pair| { + if let KeyPair::Ed25519(..) = key_pair { + Ok(key_pair) + } else { + Err(anyhow::anyhow!("the provided DID is not an Ed25519 did:key")) + } + }) + .map(|key| { + key.get_did_document(Config { + use_jose_format: false, + serialize_secrets: false, + }) + })? + .to_json() + .and_then(|doc_json| CoreDocument::from_json(&doc_json)) + .context("failed to obtain a supported DID Document") +} diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 15da7be4b6..65b37c8af2 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -7,6 +7,7 @@ publish = false [dependencies] anyhow = "1.0.62" +did-key = { version = "0.1.1", default-features = false } identity_iota = { path = "../identity_iota" } iota-client = { version = "2.0.0-beta.3", default-features = false, features = ["tls", "stronghold"] } primitive-types = "0.11.1" @@ -59,3 +60,7 @@ name = "4_key_exchange" [[example]] path = "1_advanced/5_alias_output_history.rs" name = "5_alias_output_history" + +[[example]] +path = "1_advanced/6_custom_resolution.rs" +name = "6_custom_resolution" diff --git a/examples/README.md b/examples/README.md index 570bb40bbf..7fd6fb3d84 100644 --- a/examples/README.md +++ b/examples/README.md @@ -40,3 +40,4 @@ The following advanced examples are available: | [3_did_issues_tokens](./1_advanced/3_did_issues_tokens.rs) | Demonstrates how an identity can issue and control a Token Foundry and its tokens. | | [4_key_exchange](./1_advanced/4_key_exchange.rs) | Demonstrates Elliptic-curve Diffie-Hellman (ECDH) cryptographic key exchange with DID Documents. | | [5_alias_output_history](./1_advanced/5_alias_output_history.rs) | Demonstrates fetching the history of an Alias Output. | +| [6_custom_resolution](./1_advanced/6_custom_resolution.rs) | Demonstrates how to set up a resolver using custom handlers. | diff --git a/identity_resolver/src/resolution/resolver.rs b/identity_resolver/src/resolution/resolver.rs index 937e1c4b57..97c5d89dad 100644 --- a/identity_resolver/src/resolution/resolver.rs +++ b/identity_resolver/src/resolution/resolver.rs @@ -70,6 +70,13 @@ where /// let mut resolver: Resolver = Resolver::new(); /// // Now attach some handlers whose output type implements the `Document` trait. /// ``` + /// + /// # Tradeoffs + /// The default type agnostic [`Resolver`] is more convenient when working with document types whose implementations of the [`Document`](::identity_did::document::Document) + /// trait do not map well to a single representation (such as for instance [`CoreDocument`](::identity_did::document::CoreDocument)). + /// This is typically the case whenever custom cryptography is applied in implementations of the [`Document::verify_data`](::identity_did::document::Document::verify_data()) method. + /// The extra flexibility offered by the type agnostic resolver comes at the cost of less type information, hence specifying a concrete type in the constructor + /// is recommended whenever a single representation is a good fit. pub fn new() -> Self { Self { command_map: HashMap::new(), From f44a8b988f9331c898dfcb1521eae9034691bd58 Mon Sep 17 00:00:00 2001 From: cycraig Date: Thu, 22 Sep 2022 15:01:04 +0200 Subject: [PATCH 83/89] Use Bech32-encoded state controller and governor addresses (#1044) * Change IotaDID::parse to convert to lowercase automatically * Change address type to Bech32-encoded strings * Update Wasm bindings * Fix formatting * Update presentation test fixtures * Update Wasm api-reference.md * Fix JSON fixture causing signature verification to fail --- bindings/wasm/docs/api-reference.md | 72 ++++++++---- bindings/wasm/src/iota/iota_document.rs | 12 ++ .../wasm/src/iota/iota_document_metadata.rs | 12 ++ .../signed_presentation/issuer_iota_doc.json | 6 +- .../signed_presentation/presentation.json | 93 ++++++--------- identity_iota_core/src/did/iota_did.rs | 2 +- .../src/document/iota_document.rs | 111 ++++++++++-------- .../src/document/iota_document_metadata.rs | 8 +- 8 files changed, 178 insertions(+), 138 deletions(-) diff --git a/bindings/wasm/docs/api-reference.md b/bindings/wasm/docs/api-reference.md index 9853e25a45..ce937daa44 100644 --- a/bindings/wasm/docs/api-reference.md +++ b/bindings/wasm/docs/api-reference.md @@ -102,6 +102,12 @@ See IVerifierOptions.

## Members
+
KeyType
+
+
MethodRelationship
+
+
StateMetadataEncoding
+
StatusCheck

Controls validation behaviour when checking whether or not a credential has been revoked by its credentialStatus.

@@ -144,12 +150,6 @@ This variant is the default used if no other variant is specified when construct
FirstError

Return after the first error occurs.

-
KeyType
-
-
MethodRelationship
-
-
StateMetadataEncoding
-
## Functions @@ -1988,6 +1988,8 @@ Deserializes an instance from a JSON object. * [.setMetadataUpdated(timestamp)](#IotaDocument+setMetadataUpdated) * [.metadataDeactivated()](#IotaDocument+metadataDeactivated) ⇒ boolean \| undefined * [.setMetadataDeactivated(deactivated)](#IotaDocument+setMetadataDeactivated) + * [.metadataStateControllerAddress()](#IotaDocument+metadataStateControllerAddress) ⇒ string \| undefined + * [.metadataGovernorAddress()](#IotaDocument+metadataGovernorAddress) ⇒ string \| undefined * [.setMetadataPropertyUnchecked(key, value)](#IotaDocument+setMetadataPropertyUnchecked) * [.revokeCredentials(serviceQuery, indices)](#IotaDocument+revokeCredentials) * [.unrevokeCredentials(serviceQuery, indices)](#IotaDocument+unrevokeCredentials) @@ -1995,7 +1997,7 @@ Deserializes an instance from a JSON object. * [.clone()](#IotaDocument+clone) ⇒ [IotaDocument](#IotaDocument) * _static_ * [.newWithId(id)](#IotaDocument.newWithId) ⇒ [IotaDocument](#IotaDocument) - * [.unpack(did, stateMetadata, allowEmpty)](#IotaDocument.unpack) ⇒ [IotaDocument](#IotaDocument) + * [.unpackFromOutput(did, aliasDto, allowEmpty)](#IotaDocument.unpackFromOutput) ⇒ [IotaDocument](#IotaDocument) * [.unpackFromBlock(network, block)](#IotaDocument.unpackFromBlock) ⇒ [Array.<IotaDocument>](#IotaDocument) * [.fromJSON(json)](#IotaDocument.fromJSON) ⇒ [IotaDocument](#IotaDocument) @@ -2323,6 +2325,18 @@ Sets the deactivated status of the DID document. | --- | --- | | deactivated | boolean \| undefined | + + +### iotaDocument.metadataStateControllerAddress() ⇒ string \| undefined +Returns a copy of the Bech32-encoded state controller address, if present. + +**Kind**: instance method of [IotaDocument](#IotaDocument) + + +### iotaDocument.metadataGovernorAddress() ⇒ string \| undefined +Returns a copy of the Bech32-encoded governor address, if present. + +**Kind**: instance method of [IotaDocument](#IotaDocument) ### iotaDocument.setMetadataPropertyUnchecked(key, value) @@ -2385,9 +2399,9 @@ Constructs an empty DID Document with the given identifier. | --- | --- | | id | [IotaDID](#IotaDID) | - + -### IotaDocument.unpack(did, stateMetadata, allowEmpty) ⇒ [IotaDocument](#IotaDocument) +### IotaDocument.unpackFromOutput(did, aliasDto, allowEmpty) ⇒ [IotaDocument](#IotaDocument) Deserializes the document from the state metadata bytes of an Alias Output. If `allowEmpty` is true, this will return an empty DID document marked as `deactivated` @@ -2402,7 +2416,7 @@ encoded in the `AliasId` alone. | Param | Type | | --- | --- | | did | [IotaDID](#IotaDID) | -| stateMetadata | Uint8Array | +| aliasDto | any | | allowEmpty | boolean | @@ -2443,6 +2457,8 @@ Additional attributes related to an IOTA DID Document. * [.created()](#IotaDocumentMetadata+created) ⇒ [Timestamp](#Timestamp) \| undefined * [.updated()](#IotaDocumentMetadata+updated) ⇒ [Timestamp](#Timestamp) \| undefined * [.deactivated()](#IotaDocumentMetadata+deactivated) ⇒ boolean \| undefined + * [.stateControllerAddress()](#IotaDocumentMetadata+stateControllerAddress) ⇒ string \| undefined + * [.governorAddress()](#IotaDocumentMetadata+governorAddress) ⇒ string \| undefined * [.properties()](#IotaDocumentMetadata+properties) ⇒ Map.<string, any> * [.toJSON()](#IotaDocumentMetadata+toJSON) ⇒ any * [.clone()](#IotaDocumentMetadata+clone) ⇒ [IotaDocumentMetadata](#IotaDocumentMetadata) @@ -2466,6 +2482,18 @@ Returns a copy of the timestamp of the last DID document update. ### iotaDocumentMetadata.deactivated() ⇒ boolean \| undefined Returns a copy of the deactivated status of the DID document. +**Kind**: instance method of [IotaDocumentMetadata](#IotaDocumentMetadata) + + +### iotaDocumentMetadata.stateControllerAddress() ⇒ string \| undefined +Returns a copy of the Bech32-encoded state controller address, if present. + +**Kind**: instance method of [IotaDocumentMetadata](#IotaDocumentMetadata) + + +### iotaDocumentMetadata.governorAddress() ⇒ string \| undefined +Returns a copy of the Bech32-encoded governor address, if present. + **Kind**: instance method of [IotaDocumentMetadata](#IotaDocumentMetadata) @@ -4007,6 +4035,18 @@ This is possible because Ed25519 is birationally equivalent to Curve25519 used b | --- | --- | | publicKey | Uint8Array | + + +## KeyType +**Kind**: global variable + + +## MethodRelationship +**Kind**: global variable + + +## StateMetadataEncoding +**Kind**: global variable ## StatusCheck @@ -4084,18 +4124,6 @@ Return all errors that occur during validation. ## FirstError Return after the first error occurs. -**Kind**: global variable - - -## KeyType -**Kind**: global variable - - -## MethodRelationship -**Kind**: global variable - - -## StateMetadataEncoding **Kind**: global variable diff --git a/bindings/wasm/src/iota/iota_document.rs b/bindings/wasm/src/iota/iota_document.rs index 1e752e0197..89604a5155 100644 --- a/bindings/wasm/src/iota/iota_document.rs +++ b/bindings/wasm/src/iota/iota_document.rs @@ -493,6 +493,18 @@ impl WasmIotaDocument { self.0.metadata.deactivated = deactivated; } + /// Returns a copy of the Bech32-encoded state controller address, if present. + #[wasm_bindgen(js_name = metadataStateControllerAddress)] + pub fn metadata_state_controller_address(&self) -> Option { + self.0.metadata.state_controller_address.clone() + } + + /// Returns a copy of the Bech32-encoded governor address, if present. + #[wasm_bindgen(js_name = metadataGovernorAddress)] + pub fn metadata_governor_address(&self) -> Option { + self.0.metadata.governor_address.clone() + } + /// Sets a custom property in the document metadata. /// If the value is set to `null`, the custom property will be removed. #[wasm_bindgen(js_name = setMetadataPropertyUnchecked)] diff --git a/bindings/wasm/src/iota/iota_document_metadata.rs b/bindings/wasm/src/iota/iota_document_metadata.rs index 7d7f6a1578..2638bf7ac4 100644 --- a/bindings/wasm/src/iota/iota_document_metadata.rs +++ b/bindings/wasm/src/iota/iota_document_metadata.rs @@ -34,6 +34,18 @@ impl WasmIotaDocumentMetadata { self.0.deactivated } + /// Returns a copy of the Bech32-encoded state controller address, if present. + #[wasm_bindgen(js_name = stateControllerAddress)] + pub fn state_controller_address(&self) -> Option { + self.0.state_controller_address.clone() + } + + /// Returns a copy of the Bech32-encoded governor address, if present. + #[wasm_bindgen(js_name = governorAddress)] + pub fn governor_address(&self) -> Option { + self.0.governor_address.clone() + } + /// Returns a copy of the custom metadata properties. #[wasm_bindgen] pub fn properties(&self) -> Result { diff --git a/identity_credential/tests/fixtures/signed_presentation/issuer_iota_doc.json b/identity_credential/tests/fixtures/signed_presentation/issuer_iota_doc.json index 7ff91ad52d..8815ea283d 100644 --- a/identity_credential/tests/fixtures/signed_presentation/issuer_iota_doc.json +++ b/identity_credential/tests/fixtures/signed_presentation/issuer_iota_doc.json @@ -1,10 +1,10 @@ { "doc": { - "id": "did:iota:0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "id": "did:iota:0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "verificationMethod": [ { - "id": "did:iota:0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA#issuerKey", - "controller": "did:iota:0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "id": "did:iota:0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#issuerKey", + "controller": "did:iota:0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "type": "Ed25519VerificationKey2018", "publicKeyMultibase": "zFVen3X669xLzsi6N2V91DoiyzHzg1uAgqiT8jZ9nS96Z" } diff --git a/identity_credential/tests/fixtures/signed_presentation/presentation.json b/identity_credential/tests/fixtures/signed_presentation/presentation.json index a379ebcf2d..d35bd2dcba 100644 --- a/identity_credential/tests/fixtures/signed_presentation/presentation.json +++ b/identity_credential/tests/fixtures/signed_presentation/presentation.json @@ -1,64 +1,37 @@ { - "@context": "https://www.w3.org/2018/credentials/v1", - "id": "https://example.org/credentials/3732", - "type": "VerifiablePresentation", - "verifiableCredential": [ - { - "@context": "https://www.w3.org/2018/credentials/v1", - "id": "https://example.edu/credentials/3732", - "type": [ - "VerifiableCredential", - "UniversityDegreeCredential" - ], - "credentialSubject": { - "id": "did:foo:586Z7H2vpX9qNhN2T4e9Utugie3ogjbxzGaMtM3E6HR5", - "GPA": "4.0", - "degree": { - "name": "Bachelor of Science and Arts", - "type": "BachelorDegree" - }, - "name": "Alice" + "@context": "https://www.w3.org/2018/credentials/v1", + "id": "https://example.org/credentials/3732", + "type": "VerifiablePresentation", + "verifiableCredential": { + "@context": "https://www.w3.org/2018/credentials/v1", + "id": "https://example.edu/credentials/3732", + "type": [ + "VerifiableCredential", + "UniversityDegreeCredential" + ], + "credentialSubject": { + "id": "did:foo:586Z7H2vpX9qNhN2T4e9Utugie3ogjbxzGaMtM3E6HR5", + "GPA": "4.0", + "degree": { + "name": "Bachelor of Science and Arts", + "type": "BachelorDegree" }, - "issuer": "did:iota:0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", - "issuanceDate": "2022-08-31T08:35:44Z", - "expirationDate": "2050-09-01T08:35:44Z", - "proof": { - "type": "JcsEd25519Signature2020", - "verificationMethod": "did:iota:0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA#issuerKey", - "signatureValue": "3d2aAPqjzaSQ2XbFtqLsauv2Ukdn4Hcevz2grNuJn4q4JbBmDHZpAvekVG12A3ZKRRTeKaBPguxXqcDaqujckWWz" - } + "name": "Alice" }, - { - "@context": "https://www.w3.org/2018/credentials/v1", - "id": "https://example.edu/credentials/3732", - "type": [ - "VerifiableCredential", - "UniversityDegreeCredential" - ], - "credentialSubject": { - "id": "did:foo:586Z7H2vpX9qNhN2T4e9Utugie3ogjbxzGaMtM3E6HR5", - "GPA": "4.0", - "degree": { - "name": "Bachelor of Science and Arts", - "type": "BachelorDegree" - }, - "name": "Alice" - }, - "issuer": "did:bar:Hyx62wPQGyvXCoihZq1BrbUjBRh2LuNxWiiqMkfAuSZr", - "issuanceDate": "2022-08-31T08:35:44Z", - "expirationDate": "2050-09-01T08:35:44Z", - "proof": { - "type": "JcsEd25519Signature2020", - "verificationMethod": "did:bar:Hyx62wPQGyvXCoihZq1BrbUjBRh2LuNxWiiqMkfAuSZr#root", - "signatureValue": "2iAYujqHLXP5csZzabdkfurpHaKT3Q8dnJDA4TL7pSJ7gjXLCb2tN7CF4ztKkCKmvY6VYG3pTuN1PeLGEFiQvuQr" - } + "issuer": "did:iota:0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "issuanceDate": "2022-08-31T08:35:44Z", + "expirationDate": "2050-09-01T08:35:44Z", + "proof": { + "type": "JcsEd25519Signature2020", + "verificationMethod": "did:iota:0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#issuerKey", + "signatureValue": "2xoRHPUWRVavrBsymJCKhTUe9iYX8iEEZtPhaMjmFvVmjaybVUYXHJagw9kCFmDoPgeyvZjRVQatp2VYs1b8f2Ug" } - ], - "holder": "did:foo:586Z7H2vpX9qNhN2T4e9Utugie3ogjbxzGaMtM3E6HR5", - "proof": { - "type": "JcsEd25519Signature2020", - "verificationMethod": "did:foo:586Z7H2vpX9qNhN2T4e9Utugie3ogjbxzGaMtM3E6HR5#root", - "signatureValue": "3tVeoKjftrEAQvV3MgpKgiydRHU6i8mYVRnPc6C85upo1TDBEdN94gyW1RzbgPESaZCGeDa582BxAUHVE4rVjaAd", - "challenge": "475a7984-1bb5-4c4c-a56f-822bccd46441" - } - } \ No newline at end of file + }, + "holder": "did:foo:586Z7H2vpX9qNhN2T4e9Utugie3ogjbxzGaMtM3E6HR5", + "proof": { + "type": "JcsEd25519Signature2020", + "verificationMethod": "did:foo:586Z7H2vpX9qNhN2T4e9Utugie3ogjbxzGaMtM3E6HR5#root", + "signatureValue": "5xgWVFQ8J5rsYkkF2BLxzY6YDfdGoZXfT9R9rJPbq53MHc3QkLrpr49kY9aMtQQzEL53nBxJZMtihN2F5nZ44TeR", + "challenge": "475a7984-1bb5-4c4c-a56f-822bccd46441" + } +} diff --git a/identity_iota_core/src/did/iota_did.rs b/identity_iota_core/src/did/iota_did.rs index 8f9c610da4..c2e9fc8ebd 100644 --- a/identity_iota_core/src/did/iota_did.rs +++ b/identity_iota_core/src/did/iota_did.rs @@ -93,7 +93,7 @@ impl IotaDID { /// /// Returns `Err` if the input does not conform to the [`IotaDID`] specification. pub fn parse(input: impl AsRef) -> Result { - CoreDID::parse(input).and_then(Self::try_from_core) + CoreDID::parse(input.as_ref().to_lowercase()).and_then(Self::try_from_core) } /// Converts a [`CoreDID`] to a [`IotaDID`]. diff --git a/identity_iota_core/src/document/iota_document.rs b/identity_iota_core/src/document/iota_document.rs index deee2af2e6..fcb687838e 100644 --- a/identity_iota_core/src/document/iota_document.rs +++ b/identity_iota_core/src/document/iota_document.rs @@ -5,6 +5,9 @@ use core::fmt; use core::fmt::Debug; use core::fmt::Display; +use serde::Deserialize; +use serde::Serialize; + use identity_core::common::Object; use identity_core::common::OneOrSet; use identity_core::common::OrderedSet; @@ -25,11 +28,7 @@ use identity_did::verification::MethodScope; use identity_did::verification::MethodUriType; use identity_did::verification::TryMethod; use identity_did::verification::VerificationMethod; -use serde::Deserialize; -use serde::Serialize; -use crate::block::address::Address; -use crate::block::output::AliasOutput; use crate::error::Result; use crate::IotaDID; use crate::IotaDIDUrl; @@ -278,52 +277,15 @@ impl IotaDocument { pub fn pack_with_encoding(self, encoding: StateMetadataEncoding) -> Result> { StateMetadataDocument::from(self).pack(encoding) } - - /// Deserializes the document from an Alias Output. - /// - /// If `allow_empty` is true, this will return an empty DID document marked as `deactivated` - /// if `state_metadata` is empty. - /// - /// NOTE: `did` is required since it is omitted from the serialized DID Document and - /// cannot be inferred from the state metadata. It also indicates the network, which is not - /// encoded in the `AliasId` alone. - pub fn unpack_from_output(did: &IotaDID, alias_output: &AliasOutput, allow_empty: bool) -> Result { - if alias_output.state_metadata().is_empty() && allow_empty { - let mut empty_document = IotaDocument::new_with_id(did.clone()); - empty_document.metadata.created = None; - empty_document.metadata.updated = None; - empty_document.metadata.deactivated = Some(true); - return Ok(empty_document); - } - let mut iota_document: IotaDocument = - StateMetadataDocument::unpack(alias_output.state_metadata()).and_then(|doc| doc.into_iota_document(did))?; - iota_document.set_controller_and_governor_addresses(alias_output, &did.network_str().to_owned().try_into()?); - Ok(iota_document) - } - - fn set_controller_and_governor_addresses(&mut self, alias_output: &AliasOutput, network_name: &NetworkName) { - self.metadata.governor_address = Some(*alias_output.governor_address()); - self.metadata.state_controller_address = Some(*alias_output.state_controller_address()); - - let controller_did: Option = { - match alias_output.unlock_conditions().state_controller_address() { - Some(unlock_condition) => match unlock_condition.address() { - Address::Alias(alias_address) => Some(IotaDID::new(alias_address.alias_id(), network_name)), - Address::Nft(_) | Address::Ed25519(_) => None, - }, - None => None, - } - }; - // Overwrite the DID Document controller. - *self.core_document_mut().controller_mut() = controller_did.map(OneOrSet::new_one); - } } #[cfg(feature = "client")] mod client_document { use std::ops::Deref; + use crate::block::address::Address; use crate::block::output::AliasId; + use crate::block::output::AliasOutput; use crate::block::output::Output; use crate::block::output::OutputId; use crate::block::payload::transaction::TransactionEssence; @@ -336,6 +298,45 @@ mod client_document { use super::*; impl IotaDocument { + // =========================================================================== + // Unpacking + // =========================================================================== + + /// Deserializes the document from an Alias Output. + /// + /// If `allow_empty` is true, this will return an empty DID document marked as `deactivated` + /// if `state_metadata` is empty. + /// + /// NOTE: `did` is required since it is omitted from the serialized DID Document and + /// cannot be inferred from the state metadata. It also indicates the network, which is not + /// encoded in the `AliasId` alone. + pub fn unpack_from_output(did: &IotaDID, alias_output: &AliasOutput, allow_empty: bool) -> Result { + let mut document: IotaDocument = if alias_output.state_metadata().is_empty() && allow_empty { + let mut empty_document = IotaDocument::new_with_id(did.clone()); + empty_document.metadata.created = None; + empty_document.metadata.updated = None; + empty_document.metadata.deactivated = Some(true); + empty_document + } else { + StateMetadataDocument::unpack(alias_output.state_metadata()).and_then(|doc| doc.into_iota_document(did))? + }; + + document.set_controller_and_governor_addresses(alias_output, &did.network_str().to_owned().try_into()?); + Ok(document) + } + + fn set_controller_and_governor_addresses(&mut self, alias_output: &AliasOutput, network_name: &NetworkName) { + self.metadata.governor_address = Some(alias_output.governor_address().to_bech32(network_name)); + self.metadata.state_controller_address = Some(alias_output.state_controller_address().to_bech32(network_name)); + + // Overwrite the DID Document controller. + let controller_did: Option = match alias_output.state_controller_address() { + Address::Alias(alias_address) => Some(IotaDID::new(alias_address.alias_id(), network_name)), + _ => None, + }; + *self.core_document_mut().controller_mut() = controller_did.map(OneOrSet::new_one); + } + /// Returns all DID documents of the Alias Outputs contained in the block's transaction payload /// outputs, if any. /// @@ -484,14 +485,17 @@ mod tests { use identity_did::verification::MethodData; use identity_did::verification::MethodType; - use super::*; + use crate::block::address::Address; use crate::block::address::AliasAddress; use crate::block::output::unlock_condition::GovernorAddressUnlockCondition; use crate::block::output::unlock_condition::StateControllerAddressUnlockCondition; use crate::block::output::AliasId; + use crate::block::output::AliasOutput; use crate::block::output::AliasOutputBuilder; use crate::block::output::UnlockCondition; + use super::*; + fn valid_did() -> IotaDID { "did:iota:0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" .parse() @@ -748,6 +752,8 @@ mod tests { #[test] fn test_unpack_empty() { + let controller_did: IotaDID = valid_did(); + // VALID: unpack empty, deactivated document. let did: IotaDID = "did:iota:0xBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" .parse() @@ -755,10 +761,10 @@ mod tests { let alias_output: AliasOutput = AliasOutputBuilder::new_with_amount(1, AliasId::from(&did)) .unwrap() .add_unlock_condition(UnlockCondition::StateControllerAddress( - StateControllerAddressUnlockCondition::new(Address::Alias(AliasAddress::new(AliasId::from(&valid_did())))), + StateControllerAddressUnlockCondition::new(Address::Alias(AliasAddress::new(AliasId::from(&controller_did)))), )) .add_unlock_condition(UnlockCondition::GovernorAddress(GovernorAddressUnlockCondition::new( - Address::Alias(AliasAddress::new(AliasId::from(&valid_did()))), + Address::Alias(AliasAddress::new(AliasId::from(&controller_did))), ))) .finish() .unwrap(); @@ -767,12 +773,21 @@ mod tests { assert_eq!(document.metadata.deactivated, Some(true)); // Ensure no other fields are injected. - // TODO: update this when controller/governor fields are added? - let json: String = format!("{{\"doc\":{{\"id\":\"{did}\"}},\"meta\":{{\"deactivated\":true}}}}"); + let json: String = format!( + r#"{{"doc":{{"id":"{did}","controller":"{controller_did}"}},"meta":{{"deactivated":true,"governorAddress":"iota1pz424242424242424242424242424242424242424242424242425ryaqzy","stateControllerAddress":"iota1pz424242424242424242424242424242424242424242424242425ryaqzy"}}}}"# + ); assert_eq!(document.to_json().unwrap(), json); // INVALID: reject empty document. assert!(IotaDocument::unpack_from_output(&did, &alias_output, false).is_err()); + + // Ensure re-packing removes the controller, state controller address, and governor address. + let packed: Vec = document.pack_with_encoding(StateMetadataEncoding::Json).unwrap(); + let state_metadata_document: StateMetadataDocument = StateMetadataDocument::unpack(&packed).unwrap(); + let unpacked_document: IotaDocument = state_metadata_document.into_iota_document(&did).unwrap(); + assert!(unpacked_document.document.controller().is_none()); + assert!(unpacked_document.metadata.state_controller_address.is_none()); + assert!(unpacked_document.metadata.governor_address.is_none()); } #[test] diff --git a/identity_iota_core/src/document/iota_document_metadata.rs b/identity_iota_core/src/document/iota_document_metadata.rs index 6a6bc448c0..29d70e5a99 100644 --- a/identity_iota_core/src/document/iota_document_metadata.rs +++ b/identity_iota_core/src/document/iota_document_metadata.rs @@ -11,8 +11,6 @@ use identity_core::convert::FmtJson; use serde::Deserialize; use serde::Serialize; -use crate::block::address::Address; - /// Additional attributes related to a [`IotaDocument`][crate::IotaDocument]. #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct IotaDocumentMetadata { @@ -23,10 +21,12 @@ pub struct IotaDocumentMetadata { pub updated: Option, #[serde(skip_serializing_if = "Option::is_none")] pub deactivated: Option, + /// Bech32-encoded address of the governor unlock condition. #[serde(rename = "governorAddress", skip_serializing_if = "Option::is_none")] - pub governor_address: Option
, + pub governor_address: Option, + /// Bech32-encoded address of the state controller unlock condition. #[serde(rename = "stateControllerAddress", skip_serializing_if = "Option::is_none")] - pub state_controller_address: Option
, + pub state_controller_address: Option, #[serde(flatten)] pub properties: Object, } From c4916a6d2a2687137df29ff162024c6feae73821 Mon Sep 17 00:00:00 2001 From: cycraig Date: Thu, 22 Sep 2022 15:25:10 +0200 Subject: [PATCH 84/89] Check default and no-default-features in CI (#1042) * Check default and no-default-features in CI * Fix arguments passing in CI --- .github/workflows/build-and-test.yml | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 300faa2a00..08b0b20324 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -97,7 +97,29 @@ jobs: with: os: ${{matrix.os}} - - name: Build + - name: Check --no-default-features + if: matrix.os == 'ubuntu-latest' + run: | + cargo metadata --format-version 1 | \ + jq -r '.workspace_members[] | select(contains("examples") | not)' | \ + awk '{print $1}' | \ + xargs -I {} cargo check -p {} --no-default-features + + - name: Check default features + if: matrix.os == 'ubuntu-latest' + run: | + cargo metadata --format-version 1 | \ + jq -r '.workspace_members[] | select(contains("examples") | not)' | \ + awk '{print $1}' | \ + xargs -I {} cargo check -p {} + + # Clean debug target to avoid bloating the GitHub Actions cache. + # The previous builds cannot be re-used at all for the full --all-features --release build anyway. + - name: Clean target + if: matrix.os == 'ubuntu-latest' + run: cargo clean + + - name: Build all features uses: actions-rs/cargo@v1 with: # Build the library, tests, and examples without running them to avoid recompilation in the run tests step From 8c7c95d1595d5d5ab4930f982dc312dd9194d625 Mon Sep 17 00:00:00 2001 From: cycraig Date: Fri, 23 Sep 2022 08:31:38 +0200 Subject: [PATCH 85/89] Fix the aliasOutput parameter type for unpackFromOutput (#1041) --- bindings/wasm/docs/api-reference.md | 8 ++++---- bindings/wasm/src/iota/identity_client_ext.rs | 3 +++ bindings/wasm/src/iota/iota_document.rs | 11 ++++++++--- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/bindings/wasm/docs/api-reference.md b/bindings/wasm/docs/api-reference.md index ce937daa44..8baa35f7c1 100644 --- a/bindings/wasm/docs/api-reference.md +++ b/bindings/wasm/docs/api-reference.md @@ -1997,7 +1997,7 @@ Deserializes an instance from a JSON object. * [.clone()](#IotaDocument+clone) ⇒ [IotaDocument](#IotaDocument) * _static_ * [.newWithId(id)](#IotaDocument.newWithId) ⇒ [IotaDocument](#IotaDocument) - * [.unpackFromOutput(did, aliasDto, allowEmpty)](#IotaDocument.unpackFromOutput) ⇒ [IotaDocument](#IotaDocument) + * [.unpackFromOutput(did, aliasOutput, allowEmpty)](#IotaDocument.unpackFromOutput) ⇒ [IotaDocument](#IotaDocument) * [.unpackFromBlock(network, block)](#IotaDocument.unpackFromBlock) ⇒ [Array.<IotaDocument>](#IotaDocument) * [.fromJSON(json)](#IotaDocument.fromJSON) ⇒ [IotaDocument](#IotaDocument) @@ -2401,8 +2401,8 @@ Constructs an empty DID Document with the given identifier. -### IotaDocument.unpackFromOutput(did, aliasDto, allowEmpty) ⇒ [IotaDocument](#IotaDocument) -Deserializes the document from the state metadata bytes of an Alias Output. +### IotaDocument.unpackFromOutput(did, aliasOutput, allowEmpty) ⇒ [IotaDocument](#IotaDocument) +Deserializes the document from an Alias Output. If `allowEmpty` is true, this will return an empty DID document marked as `deactivated` if `stateMetadata` is empty. @@ -2416,7 +2416,7 @@ encoded in the `AliasId` alone. | Param | Type | | --- | --- | | did | [IotaDID](#IotaDID) | -| aliasDto | any | +| aliasOutput | IAliasOutput | | allowEmpty | boolean | diff --git a/bindings/wasm/src/iota/identity_client_ext.rs b/bindings/wasm/src/iota/identity_client_ext.rs index 4bdb1300cd..492354a757 100644 --- a/bindings/wasm/src/iota/identity_client_ext.rs +++ b/bindings/wasm/src/iota/identity_client_ext.rs @@ -36,6 +36,9 @@ extern "C" { #[wasm_bindgen(typescript_type = "AddressTypes")] pub type AddressTypes; + #[wasm_bindgen(typescript_type = "IAliasOutput")] + pub type IAliasOutput; + #[wasm_bindgen(typescript_type = "IRent")] pub type IRent; } diff --git a/bindings/wasm/src/iota/iota_document.rs b/bindings/wasm/src/iota/iota_document.rs index 89604a5155..a9659061dc 100644 --- a/bindings/wasm/src/iota/iota_document.rs +++ b/bindings/wasm/src/iota/iota_document.rs @@ -37,6 +37,7 @@ use crate::did::WasmMethodScope; use crate::did::WasmVerifierOptions; use crate::error::Result; use crate::error::WasmResult; +use crate::iota::identity_client_ext::IAliasOutput; use crate::iota::WasmIotaDID; use crate::iota::WasmIotaDIDUrl; use crate::iota::WasmIotaDocumentMetadata; @@ -388,7 +389,7 @@ impl WasmIotaDocument { .wasm_result() } - /// Deserializes the document from the state metadata bytes of an Alias Output. + /// Deserializes the document from an Alias Output. /// /// If `allowEmpty` is true, this will return an empty DID document marked as `deactivated` /// if `stateMetadata` is empty. @@ -398,8 +399,12 @@ impl WasmIotaDocument { /// encoded in the `AliasId` alone. #[allow(non_snake_case)] #[wasm_bindgen(js_name = unpackFromOutput)] - pub fn unpack_from_output(did: &WasmIotaDID, aliasDto: JsValue, allowEmpty: bool) -> Result { - let alias_dto: AliasOutputDto = aliasDto.into_serde().wasm_result()?; + pub fn unpack_from_output( + did: &WasmIotaDID, + aliasOutput: IAliasOutput, + allowEmpty: bool, + ) -> Result { + let alias_dto: AliasOutputDto = aliasOutput.into_serde().wasm_result()?; let alias_output: AliasOutput = AliasOutput::try_from(&alias_dto) .map_err(|err| { identity_iota::iota::Error::JsError(format!("get_alias_output failed to convert AliasOutputDto: {}", err)) From 0524b77318810c3057307f74a9f57eb0cb8ce102 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Ha=C3=9F?= Date: Fri, 23 Sep 2022 14:51:50 +0200 Subject: [PATCH 86/89] Use a private tangle in CI (#943) * use a private tangle in CI * add tear down * move private tangle to stardust workflow, fix tear down * use private tangle * use faucet * use faucet API * increase tiemout * use improved run script, always clen up private tangle * increase timeout * Implement automatic faucet fund request * add wait for healthcheck * increase timeout * fix health check endpoints * use private tangle in create_did * tweak parallel settings * increase timeouts * fix faucet url * use sh instead of action * use working directory * fix syntax error * fix visibility * fix example path * use private tangle for wasm * disable pow * fix PoW setting * fix private tangle test * fix private tangle example, disable local PoW * fix formatting * change input path for readme example * Checkout only the required subdirectory * Use the private_tangle asset provided by hornet * Don't pipe through gzip apparently * Export env var * Fix private tangle download * Set minPoWScore parameter for Wasm examples * log protocol_parameters.json * Replace Shimmer Testnet with private network Co-authored-by: PhilippGackstatter --- .../actions/private-tangle/setup/action.yml | 38 +++++++++++++++++++ .../private-tangle/tear-down/action.yml | 12 ++++++ .github/workflows/build-and-test.yml | 15 ++++++++ README.md | 7 ++-- bindings/wasm/README.md | 6 ++- bindings/wasm/cypress/e2e/privateTangle.cy.js | 2 +- bindings/wasm/examples/src/util.ts | 8 ++-- examples/0_basic/0_create_did.rs | 10 ++--- examples/utils/utils.rs | 8 ++-- identity_iota/README.md | 7 ++-- 10 files changed, 91 insertions(+), 22 deletions(-) create mode 100644 .github/actions/private-tangle/setup/action.yml create mode 100644 .github/actions/private-tangle/tear-down/action.yml diff --git a/.github/actions/private-tangle/setup/action.yml b/.github/actions/private-tangle/setup/action.yml new file mode 100644 index 0000000000..f285e4621b --- /dev/null +++ b/.github/actions/private-tangle/setup/action.yml @@ -0,0 +1,38 @@ +name: 'private-tangle-setup' +description: 'Setup a private tangle' +runs: + using: "composite" + steps: + - name: Setup private tangle + shell: bash + run: | + # Download the private_tangle setup from the hornet repo. + mkdir private_tangle + cd private_tangle + # Use the output of https://api.github.com/repos/iotaledger/hornet/releases/latest once there's a 2.0 Hornet release. + DOWNLOAD_URL=$(curl "https://api.github.com/repos/iotaledger/hornet/releases" | jq -r '.[0].assets[] | select(.name | contains("private_tangle")) | .browser_download_url') + curl -L -o private_tangle.tar.gz $DOWNLOAD_URL + tar -xf private_tangle.tar.gz + + # Set minPoWScore = 1 since the default (0) doesn't work with wasm_miner.rs in iota.rs currently. + jq '.minPoWScore = $val' --argjson val 1 protocol_parameters.json > tmp.json && mv tmp.json protocol_parameters.json + jq --color-output . protocol_parameters.json + + # Manipulate and print config + jq '.restAPI.pow.enabled = $newVal' --argjson newVal false config_private_tangle.json > tmp.$$.json && mv tmp.$$.json config_private_tangle.json + jq --color-output . config_private_tangle.json + + # Start Tangle + sudo ./cleanup.sh + sudo ./bootstrap.sh + sudo ./run.sh -d + - name: Wait for tangle to start + shell: bash + run: wget -qO- https://raw.githubusercontent.com/eficode/wait-for/$WAIT_FOR_VERSION/wait-for | sh -s -- -t 60 http://localhost:14265/health -- echo "Tangle is up" + env: + WAIT_FOR_VERSION: 4df3f9262d84cab0039c07bf861045fbb3c20ab7 # v2.2.3 + - name: Wait for faucet to start + shell: bash + run: wget -qO- https://raw.githubusercontent.com/eficode/wait-for/$WAIT_FOR_VERSION/wait-for | sh -s -- -t 60 http://localhost:8091/api/info -- echo "Faucet is up" + env: + WAIT_FOR_VERSION: 4df3f9262d84cab0039c07bf861045fbb3c20ab7 # v2.2.3 diff --git a/.github/actions/private-tangle/tear-down/action.yml b/.github/actions/private-tangle/tear-down/action.yml new file mode 100644 index 0000000000..017cffee4b --- /dev/null +++ b/.github/actions/private-tangle/tear-down/action.yml @@ -0,0 +1,12 @@ +name: 'private-tangle-tear-down' +description: 'tear-down a private tangle' +runs: + using: "composite" + steps: + - name: Tear down private tangle + shell: bash + run: | + cd private_tangle + docker-compose down + cd .. + sudo rm -rf private_tangle diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 08b0b20324..2aff839052 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -126,6 +126,10 @@ jobs: command: build args: --workspace --tests --examples --all-features --release + - name: Start private tangle + if: matrix.os == 'ubuntu-latest' + uses: './.github/actions/private-tangle/setup' + - name: Run tests uses: actions-rs/cargo@v1 with: @@ -141,6 +145,10 @@ jobs: awk '$1 ~ /[0-9].*/' | \ parallel -k -j 4 --retries 3 ./target/release/examples/{} + - name: Tear down private tangle + if: matrix.os == 'ubuntu-latest' && always() + uses: './.github/actions/private-tangle/tear-down' + - name: Stop sccache uses: './.github/actions/rust/sccache/stop-sccache' with: @@ -215,10 +223,17 @@ jobs: name: identity-wasm-bindings-build path: bindings/wasm + - name: Start private tangle + uses: './.github/actions/private-tangle/setup' + - name: Run Wasm examples run: npm run test:examples working-directory: bindings/wasm + - name: Tear down private tangle + if: always() + uses: './.github/actions/private-tangle/tear-down' + # build-and-test-stronghold-nodejs: # needs: # - check-for-run-condition diff --git a/README.md b/README.md index 0ed6ba4a4b..d1daa5b0ee 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,8 @@ To try out the [examples](https://github.com/iotaledger/identity.rs/blob/HEAD/ex ## Example: Creating an Identity -The following code creates and publishes a new IOTA DID Document to the Shimmer Testnet. +The following code creates and publishes a new IOTA DID Document to a locally running private network. +See the [instructions](https://wiki.iota.org/hornet/develop/how_tos/private_tangle) on running your own private network. _Cargo.toml_ @@ -106,7 +107,7 @@ use iota_client::Client; use tokio::io::AsyncReadExt; // The endpoint of the IOTA node to use. -static API_ENDPOINT: &str = "https://api.testnet.shimmer.network/"; +static API_ENDPOINT: &str = "http://127.0.0.1:14265"; /// Demonstrates how to create a DID Document and publish it in a new Alias Output. #[tokio::main] @@ -135,7 +136,7 @@ async fn main() -> anyhow::Result<()> { let network_name: NetworkName = client.network_name().await?; println!("Your wallet address is: {}", address.to_bech32(network_name.as_ref())); - println!("Please request funds from https://faucet.testnet.shimmer.network/, then press Enter."); + println!("Please request funds from http://127.0.0.1:8091/, then press Enter."); tokio::io::stdin().read_u8().await?; // Create a new DID document with a placeholder DID. diff --git a/bindings/wasm/README.md b/bindings/wasm/README.md index 9cf93f1246..fd57b3a35a 100644 --- a/bindings/wasm/README.md +++ b/bindings/wasm/README.md @@ -56,6 +56,9 @@ The minimum supported version for node is: `v16` ## NodeJS Usage +The following code creates a new IOTA DID Document suitable for publishing to a locally running private network. +See the [instructions](https://wiki.iota.org/hornet/develop/how_tos/private_tangle) on running your own private network. +