{"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, 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 = {\n    [DidPeerPurpose.Assertion]: didDocument.assertionMethod,\n    [DidPeerPurpose.Encryption]: didDocument.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]: didDocument.authentication,\n    [DidPeerPurpose.CapabilityInvocation]: didDocument.capabilityInvocation,\n    [DidPeerPurpose.CapabilityDelegation]: didDocument.capabilityDelegation,\n  }\n\n  let did = 'did:peer:2'\n\n  const keys: { id: string; encoded: string }[] = []\n\n  for (const [purpose, entries] of Object.entries(purposeMapping)) {\n    // Not all entries are required to be defined\n    if (entries === undefined) continue\n\n    // Dereference all entries to full verification methods\n    const dereferenced = entries.map((entry) =>\n      typeof entry === 'string' ? didDocument.dereferenceVerificationMethod(entry) : entry\n    )\n\n    // Transform all verification methods into a fingerprint (multibase, multicodec)\n    for (const entry of dereferenced) {\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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"}