Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 120 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,39 +86,66 @@ const doc = await resolver.resolve('did:ethr:0xF3beAC30C498D9E26865F34fCAa57dBB9

## Caching

Resolving DID Documents can be expensive. It is in most cases best to cache DID documents. Caching has to be
specifically enabled using the `cache` parameter
Resolving DID Documents can be expensive. It is in most cases best to cache DID documents. Caching is controlled via the `cache` option, which accepts a boolean value or a custom `DIDCache` function.

The built-in cache uses a Map, but does not have an automatic TTL, so entries don't expire. This is fine in most web,
mobile and serverless contexts. If you run a long-running process you may want to use an existing configurable caching
system.
### Built-in caching

The built-in Cache can be enabled by passing in a `true` value to the constructor:
The built-in cache uses a `Map` and does not have an automatic TTL, so entries don't expire. This is fine in most web, mobile and serverless contexts. If you run a long-running process, consider using a custom cache with expiration.

Enable the built-in cache by passing `cache: true` to the constructor:

```js
const resolver = new DIDResolver({
const resolver = new Resolver({
ethr,
web
}, {
cache: true
})
```

Here is an example using `js-cache` which has not been tested.
### Disabling cache

To disable caching entirely, pass `cache: false`:

```js
const resolver = new Resolver({
ethr,
web
}, {
cache: false
})
```

### Per-call cache control

You can override the global cache setting on a per-call basis using the `cache` option in `DIDResolutionOptions`:

```js
// Global cache enabled, but disable for this specific resolution
const doc = await resolver.resolve('did:ethr:0xabcd...', { cache: false })

// Global cache disabled, but enable for this specific resolution
const doc = await resolver.resolve('did:ethr:0xabcd...', { cache: true })
```

### Custom cache implementation

For advanced use cases, implement a custom `DIDCache` function. It receives the parsed DID, a resolve function, and optional resolution options:

```js
var cache = require('js-cache')
const customCache : DIDCache = (parsed, resolve) => {
// DID spec requires to not cache if no-cache param is set
if (parsed.params && parsed.params['no-cache'] === 'true') return await resolve()
const customCache: DIDCache = async (parsed, resolve, options) => {
// Respect per-call cache control
if (options?.cache === false) return await resolve()

const cached = cache.get(parsed.didUrl)
if (cached !== undefined) return cached

const doc = await resolve()
cache.set(parsed, doc, 60000)
cache.set(parsed.didUrl, doc, 60000) // 60s TTL
return doc
}

const resolver = new DIDResolver({
const resolver = new Resolver({
ethr,
web
}, {
Expand Down Expand Up @@ -173,3 +200,82 @@ import MyMethod from 'mymethod-did-resolver'
const myResolver = MyMethod.getResolver()
const resolver = new DIDResolver(myResolver)
```

### Per-Method Parsing

If your method has special syntax constraints beyond the base DID Core v1.0 spec, you can attach a method-specific parser to your resolver. This allows stricter syntax validation without duplicate parsing work.

**Architecture:** Method-specific parsers receive a DID Core v1.0 compliant `ParsedDID` result from the global parser and only check method-specific syntax constraints. This ensures all DIDs pass DID Core v1.0 parsing first, then method-specific syntax rules. The parser is part of the resolver enhancement — both are exported together from `getResolver()`.

```js
export function getResolver() {
async function resolve(
did: string,
parsed: ParsedDID,
didResolver: Resolver,
options: DIDResolutionOptions
): Promise<DIDDocument> {
console.log(parsed)
// {method: 'mymethod', id: 'abcdefg', did: 'did:mymethod:abcdefg/some/path#fragment=123', path: '/some/path', fragment: 'fragment=123'}

const didDoc = ... // lookup doc
// If you need to lookup another did as part of resolving this did document, the primary DIDResolver object is passed in as well
const parentDID = await didResolver.resolve(...)

// Return the DIDResolutionResult object
return {
didResolutionMetadata: { contentType: 'application/did+ld+json' },
didDocument: didDoc,
didDocumentMetadata: { ... }
}
}

function parser(parsed: ParsedDID): ParsedDID | null {
// Receive DID Core v1.0 compliant ParsedDID from global parser
// Only add method-specific syntax constraints

// Example: enforce minimum id length for this method
if (parsed.id.length < 5) {
return null // Reject with invalidDid error
}

// Can refine/enrich the parsed result if needed
return {
...parsed,
methodSpecificField: 'value'
}
}

// Attach parser to resolver — this is required for the parser to be used
resolve.parser = parser

return { myMethod: resolve }
}
```

Consumers register the resolver normally — the parser is part of it:

```js
import { Resolver } from 'did-resolver'
import MyMethod from 'mymethod-did-resolver'

// For a single method
const resolver = new Resolver(MyMethod.getResolver())

// For multiple methods, flatten them together
import OtherMethod from 'other-did-resolver'
const resolver = new Resolver({
...MyMethod.getResolver(),
...OtherMethod.getResolver(),
})
```

**Benefits:**

- Method libraries control their own parse→resolve pipeline as a cohesive unit
- Parser is discovered automatically via the resolver's `.parser` property
- No duplicate parsing work
- All DIDs guaranteed to pass DID Core v1.0 parsing first
- Method parsers only implement method-specific syntax constraints
- Parsers can refine/enrich the parsed result if needed
- If parser returns `null`, resolution fails with `invalidDid` error
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "did-resolver",
"version": "5.0.1",
"version": "6.0.0",
"description": "Resolve DID documents",
"source": "src/resolver.ts",
"main": "./lib/resolver.cjs",
Expand Down
Loading