{"version":3,"file":"peerDidNumAlgo2.mjs","names":[],"sources":["../../../../../src/modules/dids/methods/peer/peerDidNumAlgo2.ts"],"sourcesContent":["import { CredoError } from '../../../../error'\nimport type { JsonObject } from '../../../../types'\nimport { JsonEncoder, JsonTransformer } from '../../../../utils'\nimport { PublicJwk } from '../../../kms'\nimport type { DidDocument, DidPurpose, VerificationMethod } from '../../domain'\nimport { DidDocumentService } from '../../domain'\nimport { DidDocumentBuilder } from '../../domain/DidDocumentBuilder'\nimport {\n  getPublicJwkFromVerificationMethod,\n  getVerificationMethodsForPublicJwk,\n} from '../../domain/key-type/keyDidMapping'\nimport { parseDid } from '../../domain/parse'\n\nenum DidPeerPurpose {\n  Assertion = 'A',\n  Encryption = 'E',\n  Verification = 'V',\n  CapabilityInvocation = 'I',\n  CapabilityDelegation = 'D',\n  Service = 'S',\n}\n\nfunction isDidPeerKeyPurpose(purpose: string): purpose is Exclude<DidPeerPurpose, DidPeerPurpose.Service> {\n  return purpose !== DidPeerPurpose.Service && Object.values(DidPeerPurpose).includes(purpose as DidPeerPurpose)\n}\n\nconst didPeerAbbreviations: { [key: string]: string | undefined } = {\n  type: 't',\n  DIDCommMessaging: 'dm',\n  serviceEndpoint: 's',\n  routingKeys: 'r',\n  accept: 'a',\n}\n\nconst didPeerExpansions: { [key: string]: string | undefined } = {\n  t: 'type',\n  dm: 'DIDCommMessaging',\n  s: 'serviceEndpoint',\n  r: 'routingKeys',\n  a: 'accept',\n}\n\nexport function didToNumAlgo2DidDocument(did: string) {\n  const parsed = parseDid(did)\n  const identifierWithoutNumAlgo = parsed.id.substring(2)\n\n  // Get a list of all did document entries splitted by .\n  const entries = identifierWithoutNumAlgo.split('.')\n  const didDocument = new DidDocumentBuilder(did)\n  let serviceIndex = 0\n  let keyIndex = 1\n\n  for (const entry of entries) {\n    // Remove the purpose identifier to get the service or key content\n    const entryContent = entry.substring(1)\n    // Get the purpose identifier\n    const purpose = entry[0]\n\n    // Handle service entry first\n    if (purpose === DidPeerPurpose.Service) {\n      let services = JsonEncoder.fromBase64Url(entryContent)\n\n      // Make sure we have an array of services (can be both json or array)\n      services = Array.isArray(services) ? services : [services]\n\n      for (let service of services) {\n        // Expand abbreviations used for service key/values\n        service = expandServiceAbbreviations(service)\n\n        service.id = `${did}#${service.type.toLowerCase()}-${serviceIndex++}`\n\n        didDocument.addService(JsonTransformer.fromJSON(service, DidDocumentService))\n      }\n    }\n    // Otherwise we can be sure it is a key\n    else {\n      // Decode the fingerprint, and extract the verification method(s)\n      const publicJwk = PublicJwk.fromFingerprint(entryContent)\n      const verificationMethods = getVerificationMethodsForPublicJwk(publicJwk, did)\n\n      // Add all verification methods to the did document\n      for (const verificationMethod of verificationMethods) {\n        verificationMethod.id = `${did}#key-${keyIndex++}`\n        addVerificationMethodToDidDocument(didDocument, verificationMethod, purpose)\n      }\n    }\n  }\n\n  return didDocument.build()\n}\n\nexport function didDocumentToNumAlgo2Did(didDocument: DidDocument) {\n  const purposeMapping: Array<[DidPeerPurpose, DidPurpose]> = [\n    [DidPeerPurpose.Assertion, 'assertionMethod'],\n    [DidPeerPurpose.Encryption, 'keyAgreement'],\n    // FIXME: should verification be authentication or verificationMethod\n    // verificationMethod is general so it doesn't make a lot of sense to add\n    // it to the verificationMethod list\n    [DidPeerPurpose.Verification, 'authentication'],\n    [DidPeerPurpose.CapabilityInvocation, 'capabilityInvocation'],\n    [DidPeerPurpose.CapabilityDelegation, 'capabilityDelegation'],\n  ]\n\n  let did = 'did:peer:2'\n\n  const keys: { id: string; encoded: string }[] = []\n\n  for (const [purpose, entries] of purposeMapping) {\n    // Not all entries are required to be defined\n    // Dereference all entries to full verification methods\n    // Transform all verification methods into a fingerprint (multibase, multicodec)\n    for (const entry of didDocument.findVerificationMethodsByPurpose([entries])) {\n      const key = getPublicJwkFromVerificationMethod(entry)\n\n      // Encode as '.PurposeFingerprint'\n      const encoded = `.${purpose}${key.fingerprint}`\n\n      keys.push({ id: entry.id, encoded })\n    }\n  }\n\n  const prefix = 'key-'\n  if (!keys.every((key) => key.id.split('#')[1]?.startsWith(prefix))) {\n    throw new CredoError('Ids for keys within DID Document for did:peer:2 creation must follow the pattern `#key-n`')\n  }\n\n  // Add all encoded keys ordered by their id (#key-1, #key-2, etc.)\n  did += keys\n    .sort((a, b) => {\n      const aFragment = a.id.split('#')[1]\n      const bFragment = b.id.split('#')[1]\n      const aIndex = Number(aFragment.replace(prefix, ''))\n      const bIndex = Number(bFragment.replace(prefix, ''))\n\n      return aIndex - bIndex\n    })\n    .map((key) => key.encoded)\n    .join('')\n\n  if (didDocument.service && didDocument.service.length > 0) {\n    const abbreviatedServices = didDocument.service.map((service) => {\n      // Transform to JSON, remove id property\n      const serviceJson = JsonTransformer.toJSON(service)\n      serviceJson.id = undefined\n\n      return abbreviateServiceJson(serviceJson)\n    })\n\n    for (const abbreviatedService of abbreviatedServices) {\n      const encodedService = JsonEncoder.toBase64Url(abbreviatedService)\n      did += `.${DidPeerPurpose.Service}${encodedService}`\n    }\n  }\n\n  return did\n}\n\nfunction expandServiceAbbreviations(service: JsonObject) {\n  const expand = (abbreviated: string) => didPeerExpansions[abbreviated] ?? abbreviated\n  const expandJson = (json: unknown): unknown => {\n    if (!json) return json\n    if (typeof json === 'number') return json\n    if (typeof json === 'string') return expand(json)\n    if (Array.isArray(json)) return json.map(expandJson)\n    if (typeof json === 'object')\n      return Object.entries(json as Record<string, unknown>).reduce(\n        (jsonBody, [key, value]) => ({\n          // biome-ignore lint/performance/noAccumulatingSpread: no explanation\n          ...jsonBody,\n          [expand(key)]: expandJson(value),\n        }),\n        {}\n      )\n  }\n\n  const fullService = expandJson(service) as Record<string, unknown>\n\n  // Handle the case where a legacy DIDComm v2 service has been encoded in the did:peer:2.\n  // We use the legacy `DIDComm` type (over `DIDCommMessaging`)\n  if ('t' in service && service.t === 'dm' && typeof service.serviceEndpoint === 'string') {\n    return {\n      ...fullService,\n      type: 'DIDComm',\n    }\n  }\n\n  return fullService\n}\n\nfunction abbreviateServiceJson(service: JsonObject) {\n  const abbreviate = (expanded: string) => didPeerAbbreviations[expanded] ?? expanded\n\n  const abbreviatedService = Object.entries(service).reduce(\n    (serviceBody, [key, value]) => ({\n      // biome-ignore lint/performance/noAccumulatingSpread: no explanation\n      ...serviceBody,\n      [abbreviate(key)]: abbreviate(value as string),\n    }),\n    {}\n  )\n\n  return abbreviatedService\n}\n\nfunction addVerificationMethodToDidDocument(\n  didDocument: DidDocumentBuilder,\n  verificationMethod: VerificationMethod,\n  purpose: string\n) {\n  const purposeMapping = {\n    [DidPeerPurpose.Assertion]: didDocument.addAssertionMethod.bind(didDocument),\n    [DidPeerPurpose.Encryption]: didDocument.addKeyAgreement.bind(didDocument),\n    // FIXME: should verification be authentication or verificationMethod\n    // verificationMethod is general so it doesn't make a lot of sense to add\n    // it to the verificationMethod list\n    [DidPeerPurpose.Verification]: didDocument.addAuthentication.bind(didDocument),\n    [DidPeerPurpose.CapabilityInvocation]: didDocument.addCapabilityInvocation.bind(didDocument),\n    [DidPeerPurpose.CapabilityDelegation]: didDocument.addCapabilityDelegation.bind(didDocument),\n  }\n\n  // Verify the purpose is a did peer key purpose (service excluded)\n  if (isDidPeerKeyPurpose(purpose)) {\n    const addVerificationMethod = purposeMapping[purpose]\n\n    // Add the verification method based on the method from the mapping\n    addVerificationMethod(verificationMethod)\n  } else {\n    throw new CredoError(`Unsupported peer did purpose '${purpose}'`)\n  }\n}\n"],"mappings":"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"}