diff --git a/.changeset/cyan-laws-add.md b/.changeset/cyan-laws-add.md new file mode 100644 index 000000000..3413733f4 --- /dev/null +++ b/.changeset/cyan-laws-add.md @@ -0,0 +1,5 @@ +--- +"@web5/credentials": patch +--- + +Adding credential status diff --git a/packages/credentials/package.json b/packages/credentials/package.json index e126cb5c7..7b69fc334 100644 --- a/packages/credentials/package.json +++ b/packages/credentials/package.json @@ -78,7 +78,8 @@ "@sphereon/pex": "2.1.0", "@web5/common": "1.0.0", "@web5/crypto": "1.0.0", - "@web5/dids": "1.0.3" + "@web5/dids": "1.0.3", + "pako": "^2.1.0" }, "devDependencies": { "@playwright/test": "1.44.0", @@ -87,7 +88,8 @@ "@types/chai": "4.3.6", "@types/eslint": "8.56.10", "@types/mocha": "10.0.6", - "@types/node": "20.12.12", + "@types/node": "20.11.19", + "@types/pako": "^2.0.3", "@types/sinon": "17.0.2", "@typescript-eslint/eslint-plugin": "7.10.0", "@typescript-eslint/parser": "7.9.0", diff --git a/packages/credentials/src/index.ts b/packages/credentials/src/index.ts index 9374e7fd7..c17b88a43 100644 --- a/packages/credentials/src/index.ts +++ b/packages/credentials/src/index.ts @@ -1,5 +1,6 @@ export * from './jwt.js'; export * from './presentation-exchange.js'; +export * from './status-list-credential.js'; export * from './verifiable-credential.js'; export * from './verifiable-presentation.js'; export * as utils from './utils.js'; \ No newline at end of file diff --git a/packages/credentials/src/status-list-credential.ts b/packages/credentials/src/status-list-credential.ts new file mode 100644 index 000000000..4e1addf36 --- /dev/null +++ b/packages/credentials/src/status-list-credential.ts @@ -0,0 +1,251 @@ +import pako from 'pako'; +import { getCurrentXmlSchema112Timestamp } from './utils.js'; +import { VerifiableCredential, DEFAULT_VC_CONTEXT, DEFAULT_VC_TYPE, VcDataModel } from './verifiable-credential.js'; +import { Convert } from '@web5/common'; + +/** Status list VC context */ +export const DEFAULT_STATUS_LIST_VC_CONTEXT = 'https://w3id.org/vc/status-list/2021/v1'; + +const DEFAULT_STATUS_LIST_VC_TYPE = 'StatusList2021Credential'; + +/** + * The status purpose dictated by Status List 2021 spec. + * @see {@link https://www.w3.org/community/reports/credentials/CG-FINAL-vc-status-list-2021-20230102/#statuslist2021entry | Status List 2021 Entry} + */ +export enum StatusPurpose { + /** `revocation` purpose */ + revocation = 'revocation', + /** `suspension` purpose */ + suspension = 'suspension', +} + +/** + * The size of the bitstring in bits. + * The bitstring is 16KB in size. + */ +const BITSTRING_SIZE = 16 * 1024 * 8; // 16KiB in bits + +/** + * StatusListCredentialCreateOptions for creating a status list credential. + */ +export type StatusListCredentialCreateOptions = { + /** The id used for the resolvable path to the status list credential [String]. */ + statusListCredentialId: string, + /** The issuer URI of the credential, as a [String]. */ + issuer: string, + /** The status purpose of the status list cred, eg: revocation, as a [StatusPurpose]. */ + statusPurpose: StatusPurpose, + /** The credentials to be included in the status list credential, eg: revoked credentials, list of type [VerifiableCredential]. */ + credentialsToDisable: VerifiableCredential[] +}; + +/** + * StatusList2021Entry Credential status lookup information included in a Verifiable Credential that supports status lookup. + * Data model dictated by the Status List 2021 spec. + * + * @see {@link https://www.w3.org/community/reports/credentials/CG-FINAL-vc-status-list-2021-20230102/#example-example-statuslist2021credential | Status List 2021 Entry} + */ +export interface StatusList2021Entry { + /** The id of the status list entry. */ + id: string, + /** The type of the status list entry. */ + type: string, + /** The status purpose of the status list entry. */ + statusPurpose: string, + /** The index of the status entry in the status list. Poorly named by spec, should really be `entryIndex`. */ + statusListIndex: string, + /** URL to the status list. */ + statusListCredential: string +} + +/** + * `StatusListCredential` represents a digitally verifiable status list credential according to the + * [W3C Verifiable Credentials Status List v2021](https://www.w3.org/community/reports/credentials/CG-FINAL-vc-status-list-2021-20230102/). + * + * When a status list is published, the result is a verifiable credential that encapsulates the status list. + * + */ +export class StatusListCredential { + /** + * Create a [StatusListCredential] with a specific purpose, e.g., for revocation. + * + * @param statusListCredentialId The id used for the resolvable path to the status list credential [String]. + * @param issuer The issuer URI of the credential, as a [String]. + * @param statusPurpose The status purpose of the status list cred, eg: revocation, as a [StatusPurpose]. + * @param credentialsToDisable The credentials to be marked as revoked/suspended (status bit set to 1) in the status list. + * @returns A special [VerifiableCredential] instance that is a StatusListCredential. + * @throws Error If the status list credential cannot be created. + * + * Example: + * ``` + StatusListCredential.create({ + statusListCredentialId : 'https://statuslistcred.com/123', + issuer : issuerDid.uri, + statusPurpose : StatusPurpose.revocation, + credentialsToDisable : [credWithCredStatus] + }) + * ``` + */ + public static create(options: StatusListCredentialCreateOptions): VerifiableCredential { + const { statusListCredentialId, issuer, statusPurpose, credentialsToDisable } = options; + const indexesOfCredentialsToRevoke: number[] = this.validateStatusListEntryIndexesAreAllUnique(statusPurpose, credentialsToDisable); + const bitString = this.generateBitString(indexesOfCredentialsToRevoke); + + const credentialSubject = { + id : statusListCredentialId, + type : 'StatusList2021', + statusPurpose : statusPurpose, + encodedList : bitString, + }; + + const vcDataModel: VcDataModel = { + '@context' : [DEFAULT_VC_CONTEXT, DEFAULT_STATUS_LIST_VC_CONTEXT], + type : [DEFAULT_VC_TYPE, DEFAULT_STATUS_LIST_VC_TYPE], + id : statusListCredentialId, + issuer : issuer, + issuanceDate : getCurrentXmlSchema112Timestamp(), + credentialSubject : credentialSubject, + }; + + return new VerifiableCredential(vcDataModel); + } + + /** + * Validates if a given credential is part of the status list represented by a [VerifiableCredential]. + * + * @param credentialToValidate The [VerifiableCredential] to be validated against the status list. + * @param statusListCredential The [VerifiableCredential] representing the status list. + * @returns A [Boolean] indicating whether the `credentialToValidate` is part of the status list. + * + * This function checks if the given `credentialToValidate`'s status list index is present in the expanded status list derived from the `statusListCredential`. + * + * Example: + * ``` + * const isRevoked = StatusListCredential.validateCredentialInStatusList(credentialToCheck, statusListCred); + * ``` + */ + public static validateCredentialInStatusList( + credentialToValidate: VerifiableCredential, + statusListCredential: VerifiableCredential + ): boolean { + const statusListEntryValue = credentialToValidate.vcDataModel.credentialStatus! as StatusList2021Entry; + const credentialSubject = statusListCredential.vcDataModel.credentialSubject as any; + const statusListCredStatusPurpose = credentialSubject['statusPurpose'] as StatusPurpose; + const encodedListCompressedBitString = credentialSubject['encodedList'] as string; + + if (!statusListEntryValue.statusPurpose) { + throw new Error('status purpose in the credential to validate is undefined'); + } + + if (!statusListCredStatusPurpose) { + throw new Error('status purpose in the status list credential is undefined'); + } + + if (statusListEntryValue.statusPurpose !== statusListCredStatusPurpose) { + throw new Error('status purposes do not match between the credentials'); + } + + if (!encodedListCompressedBitString) { + throw new Error('compressed bitstring is null or empty'); + } + + return this.getBit(encodedListCompressedBitString, parseInt(statusListEntryValue.statusListIndex)); + } + + /** + * Validates that the status list entry index in all the given credentials are unique, + * and returns the unique index values. + * + * @param statusPurpose - The status purpose that all given credentials must match to. + * @param credentials - An array of VerifiableCredential objects each contain a status list entry index. + * @returns {number[]} An array of unique statusListIndex values. + * @throws {Error} If any validation fails. + */ + private static validateStatusListEntryIndexesAreAllUnique( + statusPurpose: StatusPurpose, + credentials: VerifiableCredential[] + ): number[] { + const uniqueIndexes = new Set(); + for (const vc of credentials) { + if (!vc.vcDataModel.credentialStatus) { + throw new Error('no credential status found in credential'); + } + + const statusList2021Entry: StatusList2021Entry = vc.vcDataModel.credentialStatus as StatusList2021Entry; + + if (statusList2021Entry.statusPurpose !== statusPurpose) { + throw new Error('status purpose mismatch'); + } + + if (uniqueIndexes.has(statusList2021Entry.statusListIndex)) { + throw new Error(`duplicate entry found with index: ${statusList2021Entry.statusListIndex}`); + } + + if(parseInt(statusList2021Entry.statusListIndex) < 0) { + throw new Error('status list index cannot be negative'); + } + + if(parseInt(statusList2021Entry.statusListIndex) >= BITSTRING_SIZE) { + throw new Error('status list index is larger than the bitset size'); + } + + uniqueIndexes.add(statusList2021Entry.statusListIndex); + } + + return Array.from(uniqueIndexes).map(index => parseInt(index)); + } + + /** + * Generates a Base64URL encoded, GZIP compressed bit string. + * + * @param indexOfBitsToTurnOn - The indexes of the bits to turn on (set to 1) in the bit string. + * @returns {string} The compressed bit string as a base64-encoded string. + */ + private static generateBitString(indexOfBitsToTurnOn: number[]): string { + // Initialize a Buffer with 16KB filled with zeros + const bitArray = new Uint8Array(BITSTRING_SIZE / 8); + + // set specified bits to 1 + indexOfBitsToTurnOn.forEach(index => { + const byteIndex = Math.floor(index / 8); + const bitIndex = index % 8; + + bitArray[byteIndex] = bitArray[byteIndex] | (1 << (7 - bitIndex)); // Set bit to 1 + }); + + // Compress the bit array with GZIP using pako + const compressed = pako.gzip(bitArray); + + // Return the base64-encoded string + const base64EncodedString = Convert.uint8Array(compressed).toBase64Url(); + + return base64EncodedString; + } + + /** + * Retrieves the value of a specific bit from a compressed base64 URL-encoded bitstring + * by decoding and decompressing a bitstring, then extracting a bit's value by its index. + * + * @param compressedBitstring A base64 URL-encoded string representing the compressed bitstring. + * @param bitIndex The zero-based index of the bit to retrieve from the decompressed bitstream. + * @returns {boolean} True if the bit at the specified index is 1, false if it is 0. + */ + private static getBit(compressedBitstring: string, bitIndex: number): boolean { + // Base64-decode the compressed bitstring + const compressedData = Convert.base64Url(compressedBitstring).toUint8Array(); + + // Decompress the data using pako + const decompressedData = pako.inflate(compressedData); + + // Find the byte index, and bit index within the byte. + const byteIndex = Math.floor(bitIndex / 8); + const bitIndexWithinByte = bitIndex % 8; + + const byte = decompressedData[byteIndex]; + + // Extracts the targeted bit by adjusting for bit's position from left to right. + const bitInteger = (byte >> (7 - bitIndexWithinByte)) & 1; + + return (bitInteger === 1); + } +} \ No newline at end of file diff --git a/packages/credentials/src/verifiable-credential.ts b/packages/credentials/src/verifiable-credential.ts index 46e382ac0..bcdd48ef1 100644 --- a/packages/credentials/src/verifiable-credential.ts +++ b/packages/credentials/src/verifiable-credential.ts @@ -6,6 +6,7 @@ import { utils as cryptoUtils } from '@web5/crypto'; import { Jwt } from './jwt.js'; import { SsiValidator } from './validators.js'; import { getCurrentXmlSchema112Timestamp, getXmlSchema112Timestamp } from './utils.js'; +import { DEFAULT_STATUS_LIST_VC_CONTEXT, StatusList2021Entry } from './status-list-credential.js'; /** The default Verifiable Credential context. */ export const DEFAULT_VC_CONTEXT = 'https://www.w3.org/2018/credentials/v1'; @@ -38,6 +39,8 @@ export type CredentialSchema = { * @param issuanceDate Optional. The issuance date of the credential, as a string. * Defaults to the current date if not specified. * @param expirationDate Optional. The expiration date of the credential, as a string. + * @param credentialStatus Optional. The credential status lookup information to see if credential is revoked. + * @param credentialSchema Optional. The credential schema of the credential. * @param evidence Optional. Evidence can be included by an issuer to provide the verifier with additional supporting information in a verifiable credential. */ export type VerifiableCredentialCreateOptions = { @@ -53,6 +56,8 @@ export type VerifiableCredentialCreateOptions = { issuanceDate?: string; /** The expiration date of the credential, as a string. */ expirationDate?: string; + /** The credential status lookup information. */ + credentialStatus?: StatusList2021Entry; /** The credential schema of the credential */ credentialSchema?: CredentialSchema; /** The evidence of the credential, as an array of any. */ @@ -159,7 +164,7 @@ export class VerifiableCredential { * @returns A [VerifiableCredential] instance. */ public static async create(options: VerifiableCredentialCreateOptions): Promise { - const { type, issuer, subject, data, issuanceDate, expirationDate, credentialSchema, evidence } = options; + const { type, issuer, subject, data, issuanceDate, expirationDate, credentialStatus, credentialSchema, evidence } = options; const jsonData = JSON.parse(JSON.stringify(data)); @@ -180,8 +185,14 @@ export class VerifiableCredential { ...jsonData }; + // create the @context value + const contexts: string[] = [DEFAULT_VC_CONTEXT]; + if (credentialStatus !== null) { + contexts.push(DEFAULT_STATUS_LIST_VC_CONTEXT); + } + const vcDataModel: VcDataModel = { - '@context' : [DEFAULT_VC_CONTEXT], + '@context' : contexts, type : Array.isArray(type) ? [DEFAULT_VC_TYPE, ...type] : (type ? [DEFAULT_VC_TYPE, type] : [DEFAULT_VC_TYPE]), @@ -189,8 +200,10 @@ export class VerifiableCredential { issuer : issuer, issuanceDate : issuanceDate || getCurrentXmlSchema112Timestamp(), credentialSubject : credentialSubject, + // Include optional properties only if they have values ...(expirationDate && { expirationDate }), + ...(credentialStatus && { credentialStatus }), ...(credentialSchema && { credentialSchema }), ...(evidence && { evidence }), }; diff --git a/packages/credentials/tests/status-list-credential.spec.ts b/packages/credentials/tests/status-list-credential.spec.ts new file mode 100644 index 000000000..bf1a2ffd7 --- /dev/null +++ b/packages/credentials/tests/status-list-credential.spec.ts @@ -0,0 +1,268 @@ +import type { BearerDid } from '@web5/dids'; + +import { expect } from 'chai'; +import { DidJwk } from '@web5/dids'; + +import { VerifiableCredential } from '../src/verifiable-credential.js'; +import { StatusListCredential, StatusPurpose } from '../src/status-list-credential.js'; + +import StatusListCredentialsCreateTestVector from '../../../web5-spec/test-vectors/status_list_credentials/create.json' assert { type: 'json' }; + +describe('Status List Credential Tests', () => { + let issuerDid: BearerDid; + let holderDid: BearerDid; + + class StreetCredibility { + constructor( + public localRespect: string, + public legit: boolean + ) {} + } + + beforeEach(async () => { + issuerDid = await DidJwk.create(); + holderDid = await DidJwk.create(); + }); + + describe('Status List Credential', () => { + it('create status list credential works', async () => { + const subjectDid = issuerDid; + + const credentialStatus = { + id : 'cred-with-status-id', + type : 'StatusList2021Entry', + statusPurpose : 'revocation', + statusListIndex : '94567', + statusListCredential : 'https://statuslistcred.com/123', + }; + + const credWithCredStatus = await VerifiableCredential.create({ + type : 'StreetCred', + issuer : issuerDid.uri, + subject : subjectDid.uri, + data : new StreetCredibility('high', true), + credentialStatus : credentialStatus + }); + + const credWithStatusContexts = credWithCredStatus.vcDataModel['@context'] as string[]; + + expect(credWithStatusContexts.some(context => context.includes('https://w3id.org/vc/status-list/2021/v1'))).to.be.true; + expect(credWithStatusContexts.some(context => context.includes('https://www.w3.org/2018/credentials/v1'))).to.be.true; + + expect(credWithCredStatus.vcDataModel.credentialStatus).to.deep.equal(credentialStatus); + + const statusListCred = StatusListCredential.create({ + statusListCredentialId : 'https://statuslistcred.com/123', + issuer : issuerDid.uri, + statusPurpose : StatusPurpose.revocation, + credentialsToDisable : [credWithCredStatus] + }); + + const statusListCredContexts = statusListCred.vcDataModel['@context']; + + expect(statusListCred.vcDataModel.id).to.equal('https://statuslistcred.com/123'); + expect(statusListCredContexts).to.include('https://www.w3.org/2018/credentials/v1'); + expect(statusListCredContexts).to.include('https://w3id.org/vc/status-list/2021/v1'); + expect(statusListCred.type).to.include('StatusList2021Credential'); + expect(statusListCred.issuer).to.equal(issuerDid.uri); + + const statusListCredSubject = statusListCred.vcDataModel.credentialSubject as any; + expect(statusListCredSubject['id']).to.equal('https://statuslistcred.com/123'); + expect(statusListCredSubject['type']).to.equal('StatusList2021'); + expect(statusListCredSubject['statusPurpose']).to.equal(StatusPurpose.revocation); + + expect(statusListCredSubject['encodedList']).to.equal('H4sIAAAAAAAAA-3OMQ0AAAgDsOHfNBp2kZBWQRMAAAAAAAAAAAAAAL6Z6wAAAAAAtQVQdb5gAEAAAA'); + }); + + it('should generate StatusListCredential from multiple revoked VerifiableCredentials', async () => { + const credentialStatus1 = { + id : 'cred-with-status-id', + type : 'StatusList2021Entry', + statusPurpose : 'revocation', + statusListIndex : '123', + statusListCredential : 'https://example.com/credentials/status/3' + }; + + const vc1 = await VerifiableCredential.create({ + type : 'StreetCred', + issuer : issuerDid.uri, + subject : holderDid.uri, + data : new StreetCredibility('high', true), + credentialStatus : credentialStatus1 + }); + + const credentialStatus2 = { + id : 'cred-with-status-id', + type : 'StatusList2021Entry', + statusPurpose : 'revocation', + statusListIndex : '124', + statusListCredential : 'https://example.com/credentials/status/3' + }; + + const vc2 = await VerifiableCredential.create({ + type : 'StreetCred', + issuer : issuerDid.uri, + subject : holderDid.uri, + data : new StreetCredibility('high', true), + credentialStatus : credentialStatus2 + }); + + + const credentialStatus3 = { + id : 'cred-with-status-id', + type : 'StatusList2021Entry', + statusPurpose : 'revocation', + statusListIndex : '1247', + statusListCredential : 'https://example.com/credentials/status/3' + }; + + const vc3 = await VerifiableCredential.create({ + type : 'StreetCred', + issuer : issuerDid.uri, + subject : holderDid.uri, + data : new StreetCredibility('high', true), + credentialStatus : credentialStatus3 + }); + + const statusListCredential = await StatusListCredential.create({ + statusListCredentialId : 'revocation-id', + issuer : issuerDid.uri, + statusPurpose : StatusPurpose.revocation, + credentialsToDisable : [vc1, vc2] + }); + + expect(statusListCredential).not.be.undefined; + expect(statusListCredential.subject).to.equal('revocation-id'); + + const credentialSubject = statusListCredential.vcDataModel.credentialSubject as any; + expect(credentialSubject['type']).to.equal('StatusList2021'); + expect(credentialSubject['statusPurpose']).to.equal('revocation'); + + // TODO: Check encoding across other sdks and spec - https://github.com/TBD54566975/web5-kt/issues/52 + expect(credentialSubject['encodedList']).to.equal('H4sIAAAAAAAAA-3BMQEAAAjAoMWwf1JvC3gBdUwAAAAAAAAAAAAAAAAAAADAuwUYSEbMAEAAAA'); + + expect(StatusListCredential.validateCredentialInStatusList(vc1, statusListCredential)).to.be.true; + expect(StatusListCredential.validateCredentialInStatusList(vc2, statusListCredential)).to.be.true; + expect(StatusListCredential.validateCredentialInStatusList(vc3, statusListCredential)).to.be.false; + }); + + it('should fail when generating StatusListCredential with duplicate indexes', async () => { + const subjectDid = issuerDid; + + const credentialStatus = { + id : 'cred-with-status-id', + type : 'StatusList2021Entry', + statusPurpose : 'revocation', + statusListIndex : '94567', + statusListCredential : 'https://statuslistcred.com/123', + }; + + const credWithCredStatus = await VerifiableCredential.create({ + type : 'StreetCred', + issuer : issuerDid.uri, + subject : subjectDid.uri, + data : new StreetCredibility('high', true), + credentialStatus : credentialStatus + }); + + expect(() => + StatusListCredential.create({ + statusListCredentialId : 'https://statuslistcred.com/123', + issuer : issuerDid.uri, + statusPurpose : StatusPurpose.revocation, + credentialsToDisable : [credWithCredStatus, credWithCredStatus] + }) + ).to.throw('duplicate entry found with index: 94567'); + }); + + it('should fail when generating StatusListCredential with negative index', async () => { + const subjectDid = issuerDid; + + const credentialStatus = { + id : 'cred-with-status-id', + type : 'StatusList2021Entry', + statusPurpose : 'revocation', + statusListIndex : '-3', + statusListCredential : 'https://statuslistcred.com/123', + }; + + const credWithCredStatus = await VerifiableCredential.create({ + type : 'StreetCred', + issuer : issuerDid.uri, + subject : subjectDid.uri, + data : new StreetCredibility('high', true), + credentialStatus : credentialStatus + }); + + expect(() => + StatusListCredential.create({ + statusListCredentialId : 'https://statuslistcred.com/123', + issuer : issuerDid.uri, + statusPurpose : StatusPurpose.revocation, + credentialsToDisable : [credWithCredStatus] + }) + ).to.throw('status list index cannot be negative'); + }); + + it('should fail when generating StatusListCredential with an index larger than maximum size', async () => { + const subjectDid = issuerDid; + + const credentialStatus = { + id : 'cred-with-status-id', + type : 'StatusList2021Entry', + statusPurpose : 'revocation', + statusListIndex : Number.MAX_SAFE_INTEGER.toString(), + statusListCredential : 'https://statuslistcred.com/123', + }; + + const credWithCredStatus = await VerifiableCredential.create({ + type : 'StreetCred', + issuer : issuerDid.uri, + subject : subjectDid.uri, + data : new StreetCredibility('high', true), + credentialStatus : credentialStatus + }); + + expect(() => + StatusListCredential.create({ + statusListCredentialId : 'https://statuslistcred.com/123', + issuer : issuerDid.uri, + statusPurpose : StatusPurpose.revocation, + credentialsToDisable : [credWithCredStatus] + }) + ).to.throw('status list index is larger than the bitset size'); + }); + }); + + describe('Web5TestVectorsStatusListCredentials', () => { + it('create', async () => { + const vectors = StatusListCredentialsCreateTestVector.vectors; + + for (const vector of vectors) { + const { input, output } = vector; + + const vcWithCredStatus = await VerifiableCredential.create({ + type : input.credential.type, + issuer : input.credential.issuer, + subject : input.credential.subject, + data : input.credential.credentialSubject, + credentialStatus : input.credential.credentialStatus + }); + + const statusListCred = StatusListCredential.create({ + statusListCredentialId : input.statusListCredential.statusListCredentialId, + issuer : input.statusListCredential.issuer, + statusPurpose : input.statusListCredential.statusPurpose as StatusPurpose, + credentialsToDisable : [vcWithCredStatus] + }); + + expect(StatusListCredential.validateCredentialInStatusList(vcWithCredStatus, statusListCred)).to.be.true; + + const statusListCredSubject = statusListCred.vcDataModel.credentialSubject as any; + expect(statusListCredSubject['type']).to.equal('StatusList2021'); + expect(statusListCredSubject['statusPurpose']).to.equal(input.statusListCredential.statusPurpose); + expect(statusListCredSubject['encodedList']).to.equal(output.encodedList); + } + }); + }); +}); \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ce6a977f9..d24d8f90f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -61,7 +61,7 @@ importers: version: 0.4.11 isomorphic-ws: specifier: ^5.0.0 - version: 5.0.0(ws@8.17.0) + version: 5.0.0(ws@8.13.0) level: specifier: 8.0.0 version: 8.0.0 @@ -338,6 +338,9 @@ importers: '@web5/dids': specifier: 1.0.3 version: link:../dids + pako: + specifier: ^2.1.0 + version: 2.1.0 devDependencies: '@playwright/test': specifier: 1.44.0 @@ -358,8 +361,11 @@ importers: specifier: 10.0.6 version: 10.0.6 '@types/node': - specifier: 20.12.12 - version: 20.12.12 + specifier: 20.11.19 + version: 20.11.19 + '@types/pako': + specifier: ^2.0.3 + version: 2.0.3 '@types/sinon': specifier: 17.0.2 version: 17.0.2 @@ -2180,7 +2186,7 @@ packages: resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} engines: {node: '>=10.10.0'} dependencies: - '@humanwhocodes/object-schema': 2.0.3 + '@humanwhocodes/object-schema': 2.0.2 debug: 4.3.4(supports-color@8.1.1) minimatch: 3.1.2 transitivePeerDependencies: @@ -2203,6 +2209,10 @@ packages: engines: {node: '>=12.22'} dev: true + /@humanwhocodes/object-schema@2.0.2: + resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==} + dev: true + /@humanwhocodes/object-schema@2.0.3: resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} dev: true @@ -3374,6 +3384,10 @@ packages: resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} dev: true + /@types/pako@2.0.3: + resolution: {integrity: sha512-bq0hMV9opAcrmE0Byyo0fY3Ew4tgOevJmQ9grUhpXQhYfyLJ1Kqg3P33JT5fdbT2AjeAjR51zqqVjAL/HMkx7Q==} + dev: true + /@types/parse5@6.0.3: resolution: {integrity: sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==} dev: true @@ -4689,7 +4703,7 @@ packages: requiresBuild: true dependencies: bare-events: 2.2.0 - bare-path: 2.1.2 + bare-path: 2.1.3 bare-stream: 1.0.0 dev: true optional: true @@ -4700,8 +4714,8 @@ packages: dev: true optional: true - /bare-path@2.1.2: - resolution: {integrity: sha512-o7KSt4prEphWUHa3QUwCxUI00R86VdjiuxmJK0iNVDHYPGo+HsDaVCnqCmPbf/MiW1ok8F4p3m8RTHlWk8K2ig==} + /bare-path@2.1.3: + resolution: {integrity: sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==} requiresBuild: true dependencies: bare-os: 2.3.0 @@ -7321,12 +7335,12 @@ packages: engines: {node: '>=10'} dev: true - /isomorphic-ws@5.0.0(ws@8.17.0): + /isomorphic-ws@5.0.0(ws@8.13.0): resolution: {integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==} peerDependencies: ws: '*' dependencies: - ws: 8.17.0 + ws: 8.13.0 dev: false /istanbul-lib-coverage@3.2.2: @@ -8579,6 +8593,10 @@ packages: resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} dev: true + /pako@2.1.0: + resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==} + dev: false + /parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -9978,7 +9996,7 @@ packages: tar-stream: 3.1.7 optionalDependencies: bare-fs: 2.3.0 - bare-path: 2.1.2 + bare-path: 2.1.3 dev: true /tar-stream@2.2.0: @@ -10674,7 +10692,6 @@ packages: optional: true utf-8-validate: optional: true - dev: true /ws@8.17.0: resolution: {integrity: sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==} @@ -10687,6 +10704,7 @@ packages: optional: true utf-8-validate: optional: true + dev: true /xml@1.0.1: resolution: {integrity: sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==} diff --git a/web5-spec b/web5-spec index 358054940..a582f4757 160000 --- a/web5-spec +++ b/web5-spec @@ -1 +1 @@ -Subproject commit 35805494018d16bb19194e41fd98184314648315 +Subproject commit a582f4757c00f8797985a756729e6e6c7407bc13