Skip to content

feat: add resolve, resolveRepresentation and dereference functions#87

Merged
rongquan1 merged 1 commit into
alphafrom
feat/add-resolve-and-dereference-methods
Sep 24, 2025
Merged

feat: add resolve, resolveRepresentation and dereference functions#87
rongquan1 merged 1 commit into
alphafrom
feat/add-resolve-and-dereference-methods

Conversation

@Moiz47
Copy link
Copy Markdown
Contributor

@Moiz47 Moiz47 commented Sep 22, 2025

Summary

Added resolve, resolveRepresentation and dereference function as per w3c spec

Summary by CodeRabbit

  • New Features

    • Added DID resolution capabilities, enabling retrieval of DID documents.
    • Supports content negotiation for DID documents (application/did+json and application/did+ld+json), with appropriate context handling.
    • Introduced DID URL dereferencing, including access to specific verification methods via fragments.
    • Improved error handling for invalid DIDs/URLs and unsupported content types.
  • Tests

    • Expanded test coverage for resolution, content negotiation, and dereferencing scenarios, including success and error cases.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Sep 22, 2025

Walkthrough

Re-exports in did-web index now include resolve, resolveRepresentation, and dereference. The wellKnown/query module implements these functions with content-negotiated DID resolution and dereferencing, adding helpers/constants. Tests expanded to cover resolution, representation handling, dereferencing, and updated DID Document fixtures.

Changes

Cohort / File(s) Summary
Public API re-exports
packages/w3c-issuer/src/did-web/index.ts
Expanded export list to include resolve, resolveRepresentation, and dereference from wellKnown/query.
Resolver utilities and control flow
packages/w3c-issuer/src/did-web/wellKnown/query.ts
Added DID resolution utilities: resolve, resolveRepresentation, dereference; introduced SUPPORTED_CONTENT_TYPES, getResolver; updated imports to include DIDResolution types; implemented content negotiation and fragment dereferencing.
Tests for new resolution APIs
packages/w3c-issuer/src/did-web/wellKnown/query.test.ts
Added tests for resolve, resolveRepresentation, and dereference; extended fixtures (additional multikey-2); covered success, notFound, invalidDid, unsupported content types, and fragment dereferencing.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Client
  participant did-web API
  participant Resolver
  participant WebDIDResolver

  rect rgba(230,245,255,0.6)
  note over Client,did-web API: Resolve DID
  Client->>did-web API: resolve(did)
  did-web API->>Resolver: getResolver()
  Resolver->>WebDIDResolver: resolve(did)
  WebDIDResolver-->>Resolver: DIDResolutionResult
  Resolver-->>did-web API: result (normalized metadata)
  did-web API-->>Client: DIDResolutionResult
  end

  rect rgba(240,255,230,0.6)
  note over Client,did-web API: Resolve Representation (content-negotiation)
  Client->>did-web API: resolveRepresentation(did, { accept })
  did-web API->>Resolver: resolve(did)
  alt accept application/did+json
    did-web API->>did-web API: strip @context
  else accept application/did+ld+json
    did-web API->>did-web API: include @context
  else unsupported
    did-web API-->>Client: { didResolutionMetadata: representationNotSupported }
  end
  did-web API-->>Client: { didDocumentStream, didResolutionMetadata, didDocumentMetadata }
  end

  rect rgba(255,245,230,0.6)
  note over Client,did-web API: Dereference DID URL (fragment or full DID)
  Client->>did-web API: dereference(didUrl, { accept? })
  did-web API->>Resolver: resolve(did)
  alt has fragment
    did-web API->>did-web API: locate verificationMethod by fragment
    did-web API-->>Client: { contentStream: verificationMethod, dereferencingMetadata }
  else no fragment
    did-web API->>did-web API: serialize DID Document per accept
    did-web API-->>Client: { contentStream, dereferencingMetadata }
  end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

A whisker twitch, a hop of glee,
New paths to DIDs for buns like me!
Resolve, represent, and deref too—
I nibble bytes and follow through.
With keys aplenty, tests in sight,
I thump my paw: “All flows resolve right!” 🥕🐇

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The PR description contains only a one-line summary and does not follow the repository template; it is missing the required "Changes" section detailing what was modified, the "Issues" section linking related tickets, and the "Releases" section specifying channel/ETA, so reviewers lack necessary context about scope, tests, and release impact. Please update the PR description to follow the repository template by adding a "Changes" section that lists files and behavioral changes (including tests), an "Issues" section linking related issue(s) or story IDs, and a "Releases" section with target channel and ETA; also include any testing notes, CI status, and breaking-change or migration details so reviewers can fully assess impact.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title clearly and concisely describes the main change in the changeset — adding the three functions resolve, resolveRepresentation, and dereference — and aligns with the code and tests shown in the diff; it is a single short sentence and uses a conventional "feat:" prefix, so it is specific and relevant for reviewers scanning history.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/add-resolve-and-dereference-methods

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sonarqubecloud
Copy link
Copy Markdown

@Moiz47 Moiz47 requested a review from rongquan1 September 22, 2025 12:22
@rongquan1
Copy link
Copy Markdown
Contributor

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Sep 23, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (4)
packages/w3c-issuer/src/did-web/wellKnown/query.test.ts (1)

206-210: Reduce brittleness: compare objects, not exact JSON string ordering.

Property order in JSON.stringify can vary. Parsing and comparing object fields will be more robust.

Apply this diff:

-    expect(result.contentStream).toBe(
-      '{"id":"did:web:trustvc.github.io:did:1#multikey-1","type":"Multikey","controller":"did:web:trustvc.github.io:did:1","publicKeyMultibase":"zDnaemDNwi4G5eTzGfRooFFu5Kns3be6yfyVNtiaMhWkZbwtc"}',
-    );
+    const vm = JSON.parse(result.contentStream);
+    expect(vm).toMatchObject({
+      id: 'did:web:trustvc.github.io:did:1#multikey-1',
+      type: 'Multikey',
+      controller: 'did:web:trustvc.github.io:did:1',
+      publicKeyMultibase: 'zDnaemDNwi4G5eTzGfRooFFu5Kns3be6yfyVNtiaMhWkZbwtc',
+    });
packages/w3c-issuer/src/did-web/wellKnown/query.ts (3)

41-45: Reuse getResolver() for consistency and single instantiation path.

Avoid duplicating Resolver construction here; use the helper like elsewhere.

Apply this diff:

-  const resolver = new Resolver({
-    ...webGetResolver(),
-  });
+  const resolver = getResolver();

134-143: Return representationNotSupported for invalid accept in dereference.

Align dereference with resolveRepresentation by using representationNotSupported for unsupported accept, while keeping notFound for path/query cases.

Apply this diff:

-  const isInvalidRequest =
-    (accept && !SUPPORTED_CONTENT_TYPES.includes(accept)) || did.includes('?') || did.includes('/');
-
-  if (isInvalidRequest) {
+  if (accept && !SUPPORTED_CONTENT_TYPES.includes(accept)) {
+    return {
+      dereferencingMetadata: { error: 'representationNotSupported' },
+      contentStream: '',
+      contentMetadata: {},
+    };
+  }
+
+  if (did.includes('?') || did.includes('/')) {
     return {
       dereferencingMetadata: { error: 'notFound' },
       contentStream: '',
       contentMetadata: {},
     };
   }

59-72: Minor: prefer structured error result over throwing.

Spec-style operations generally return result objects with error metadata instead of throwing. Consider returning a DIDResolutionResult with didResolutionMetadata.error on failure for consistency (non-blocking).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 07b5fbe and 729c10e.

📒 Files selected for processing (3)
  • packages/w3c-issuer/src/did-web/index.ts (1 hunks)
  • packages/w3c-issuer/src/did-web/wellKnown/query.test.ts (3 hunks)
  • packages/w3c-issuer/src/did-web/wellKnown/query.ts (2 hunks)
🧰 Additional context used
🪛 GitHub Check: Tests / Run Tests (18.x)
packages/w3c-issuer/src/did-web/wellKnown/query.test.ts

[failure] 167-167: src/did-web/wellKnown/query.test.ts > query > resolveRepresentation > Should return correct contentType for requested accept value
AssertionError: expected '' to be truthy

  • Expected:
    ""
  • Received:
    false

❯ src/did-web/wellKnown/query.test.ts:167:40

🔇 Additional comments (1)
packages/w3c-issuer/src/did-web/index.ts (1)

3-3: Public API re-exports look good.

Adding resolve, resolveRepresentation, and dereference to the public surface is consistent and non-breaking.

Also applies to: 7-7

Comment on lines +163 to +172
it('Should return correct contentType for requested accept value', async () => {
const did = 'did:web:trustvc.github.io:did:1';
const result = await resolveRepresentation(did, { accept: 'application/did+json' });

expect(result.didDocumentStream).toBeTruthy();
expect(typeof result.didDocumentStream).toBe('string');
const parsedDocument = JSON.parse(result.didDocumentStream);
expect(parsedDocument).not.toHaveProperty('@context');
expect(result.didResolutionMetadata).toHaveProperty('contentType', 'application/did+json');
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

🧩 Analysis chain

CI failure points to implementation gap in resolveRepresentation.

The test is correct; result.didDocumentStream should not be empty for accept: 'application/did+json'. Current implementation can return '' when the resolver provides didDocumentStream instead of didDocument. See proposed fix in query.ts to support both shapes.

Please rerun tests after applying the query.ts fix I suggested.


🏁 Script executed:

#!/bin/bash
# Inspect implementation and tests referenced in the review comment
echo "Searching for resolveRepresentation symbol..."
rg -n "resolveRepresentation" || true

echo
echo "Showing packages/w3c-issuer/src/did-web/wellKnown/query.ts (if present):"
sed -n '1,240p' packages/w3c-issuer/src/did-web/wellKnown/query.ts || true

echo
echo "Showing packages/w3c-issuer/src/did-web/wellKnown/query.test.ts (if present):"
sed -n '1,240p' packages/w3c-issuer/src/did-web/wellKnown/query.test.ts || true

echo
echo "Searching for didDocumentStream / didDocument usages in that folder:"
rg -n "didDocumentStream|didDocument" packages/w3c-issuer/src/did-web/wellKnown || true

Length of output: 19706


resolveRepresentation ignores resolver's didDocumentStream — fix required

resolver.resolve may return didDocumentStream (string) instead of didDocument; current code only reads didDocument and sets didDocumentStream: '' when didDocument is absent, causing the test failure.

Location: packages/w3c-issuer/src/did-web/wellKnown/query.ts (destructuring around line ~102, response construction around line ~109).
Fix: prefer doc.didDocumentStream || (doc.didDocument ? JSON.stringify(doc.didDocument) : ''), and ensure didResolutionMetadata.contentType = accept ?? didResolutionMetadata.contentType. Rerun tests after applying.

🧰 Tools
🪛 GitHub Check: Tests / Run Tests (18.x)

[failure] 167-167: src/did-web/wellKnown/query.test.ts > query > resolveRepresentation > Should return correct contentType for requested accept value
AssertionError: expected '' to be truthy

  • Expected:
    ""
  • Received:
    false

❯ src/did-web/wellKnown/query.test.ts:167:40

🤖 Prompt for AI Agents
In packages/w3c-issuer/src/did-web/wellKnown/query.ts around lines ~100-110, the
current response creation ignores a resolver-returned didDocumentStream and
always sets didDocumentStream to '' when didDocument is absent; update the
response to set didDocumentStream = doc.didDocumentStream || (doc.didDocument ?
JSON.stringify(doc.didDocument) : '') so it uses the stream when provided, and
set didResolutionMetadata.contentType = accept ??
doc.didResolutionMetadata?.contentType (or existing metadata) so the requested
Accept is honored; apply these changes and rerun tests.

Comment on lines +82 to +121
export const resolveRepresentation = async (
did: string,
resolutionOptions?: DIDResolutionOptions,
) => {
const { accept } = resolutionOptions || {};

if (accept && !SUPPORTED_CONTENT_TYPES.includes(accept)) {
return {
didResolutionMetadata: {
error: 'representationNotSupported',
message: `Content type ${accept} is not supported.`,
},
didDocumentStream: '',
didDocumentMetadata: {},
};
}

try {
const resolver = getResolver();
const doc = await resolver.resolve(did, { accept });
const { didDocument, didResolutionMetadata, ...rest } = doc;
if (didDocument && accept === 'application/did+json') {
delete didDocument['@context'];
}

const response = {
...rest,
didDocumentStream: didDocument ? JSON.stringify(didDocument) : '',
didResolutionMetadata,
};

if (!didResolutionMetadata.error) {
response.didResolutionMetadata.contentType = accept ?? didResolutionMetadata.contentType;
}

return response;
} catch {
throw new Error('Failed to resolve did');
}
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix resolveRepresentation to support didDocumentStream and unblock failing test.

When passing accept, some resolvers return didDocumentStream (representation) instead of didDocument (data model). Current code only serializes didDocument, causing empty streams and the CI failure. Prefer resolver.resolveRepresentation when available and fall back to resolve; always build the stream; strip @context for application/did+json.

Apply this diff:

   try {
-    const resolver = getResolver();
-    const doc = await resolver.resolve(did, { accept });
-    const { didDocument, didResolutionMetadata, ...rest } = doc;
-    if (didDocument && accept === 'application/did+json') {
-      delete didDocument['@context'];
-    }
-
-    const response = {
-      ...rest,
-      didDocumentStream: didDocument ? JSON.stringify(didDocument) : '',
-      didResolutionMetadata,
-    };
-
-    if (!didResolutionMetadata.error) {
-      response.didResolutionMetadata.contentType = accept ?? didResolutionMetadata.contentType;
-    }
-
-    return response;
+    const resolver = getResolver();
+    // Prefer native resolveRepresentation if available; otherwise fallback to resolve.
+    const doc: any =
+      typeof (resolver as any).resolveRepresentation === 'function'
+        ? await (resolver as any).resolveRepresentation(did, { accept })
+        : await resolver.resolve(did, { accept });
+
+    const { didDocument, didResolutionMetadata = {}, didDocumentMetadata, didDocumentStream } = doc;
+
+    // Build the stream from whichever shape we got back.
+    let stream: string =
+      typeof didDocumentStream === 'string'
+        ? didDocumentStream
+        : didDocument
+          ? JSON.stringify(didDocument)
+          : '';
+
+    // Ensure JSON (non‑LD) representation excludes @context.
+    const effectiveContentType = accept ?? didResolutionMetadata.contentType;
+    if (stream && effectiveContentType === 'application/did+json') {
+      try {
+        const parsed = JSON.parse(stream);
+        delete parsed['@context'];
+        stream = JSON.stringify(parsed);
+      } catch {
+        // leave as-is if parsing fails
+      }
+    }
+
+    // Attach/override contentType when there is no error.
+    if (!didResolutionMetadata.error) {
+      didResolutionMetadata.contentType = effectiveContentType;
+    }
+
+    return {
+      didDocumentStream: stream,
+      didResolutionMetadata,
+      didDocumentMetadata: didDocumentMetadata ?? {},
+    };
   } catch {
     throw new Error('Failed to resolve did');
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const resolveRepresentation = async (
did: string,
resolutionOptions?: DIDResolutionOptions,
) => {
const { accept } = resolutionOptions || {};
if (accept && !SUPPORTED_CONTENT_TYPES.includes(accept)) {
return {
didResolutionMetadata: {
error: 'representationNotSupported',
message: `Content type ${accept} is not supported.`,
},
didDocumentStream: '',
didDocumentMetadata: {},
};
}
try {
const resolver = getResolver();
const doc = await resolver.resolve(did, { accept });
const { didDocument, didResolutionMetadata, ...rest } = doc;
if (didDocument && accept === 'application/did+json') {
delete didDocument['@context'];
}
const response = {
...rest,
didDocumentStream: didDocument ? JSON.stringify(didDocument) : '',
didResolutionMetadata,
};
if (!didResolutionMetadata.error) {
response.didResolutionMetadata.contentType = accept ?? didResolutionMetadata.contentType;
}
return response;
} catch {
throw new Error('Failed to resolve did');
}
};
export const resolveRepresentation = async (
did: string,
resolutionOptions?: DIDResolutionOptions,
) => {
const { accept } = resolutionOptions || {};
if (accept && !SUPPORTED_CONTENT_TYPES.includes(accept)) {
return {
didResolutionMetadata: {
error: 'representationNotSupported',
message: `Content type ${accept} is not supported.`,
},
didDocumentStream: '',
didDocumentMetadata: {},
};
}
try {
const resolver = getResolver();
// Prefer native resolveRepresentation if available; otherwise fallback to resolve.
const doc: any =
typeof (resolver as any).resolveRepresentation === 'function'
? await (resolver as any).resolveRepresentation(did, { accept })
: await resolver.resolve(did, { accept });
const { didDocument, didResolutionMetadata = {}, didDocumentMetadata, didDocumentStream } = doc;
// Build the stream from whichever shape we got back.
let stream: string =
typeof didDocumentStream === 'string'
? didDocumentStream
: didDocument
? JSON.stringify(didDocument)
: '';
// Ensure JSON (non-LD) representation excludes @context.
const effectiveContentType = accept ?? didResolutionMetadata.contentType;
if (stream && effectiveContentType === 'application/did+json') {
try {
const parsed = JSON.parse(stream);
delete parsed['@context'];
stream = JSON.stringify(parsed);
} catch {
// leave as-is if parsing fails
}
}
// Attach/override contentType when there is no error.
if (!didResolutionMetadata.error) {
didResolutionMetadata.contentType = effectiveContentType;
}
return {
didDocumentStream: stream,
didResolutionMetadata,
didDocumentMetadata: didDocumentMetadata ?? {},
};
} catch {
throw new Error('Failed to resolve did');
}
};
🤖 Prompt for AI Agents
In packages/w3c-issuer/src/did-web/wellKnown/query.ts around lines 82-121, the
code only handles didDocument (data model) and serializes it to
didDocumentStream, which breaks resolvers that return a didDocumentStream
(representation). Update to prefer calling resolver.resolveRepresentation(did, {
accept }) when the resolver exposes that method, falling back to
resolver.resolve(did, { accept }) otherwise; extract didResolutionMetadata,
didDocumentStream, and didDocument from the result; if didDocumentStream is
present use it as the stream, else build the stream by
JSON.stringify(didDocument) when didDocument exists; for accept ===
'application/did+json' ensure you strip '@context' from the JSON object (parse
stream or didDocument to an object, delete '@context', then stringify back)
before returning; set response.didResolutionMetadata.contentType = accept ??
didResolutionMetadata.contentType; preserve other metadata/rest fields; keep the
same error handling but make the thrown message clear.

@rongquan1 rongquan1 merged commit 493a248 into alpha Sep 24, 2025
30 of 33 checks passed
@rongquan1 rongquan1 deleted the feat/add-resolve-and-dereference-methods branch September 24, 2025 03:27
nghaninn pushed a commit that referenced this pull request Sep 24, 2025
@tradetrustimda
Copy link
Copy Markdown

🎉 This PR is included in version 1.3.0-alpha.9 🎉

The release is available on:

Your semantic-release bot 📦🚀

nghaninn pushed a commit that referenced this pull request Sep 24, 2025
@tradetrustimda
Copy link
Copy Markdown

🎉 This PR is included in version 1.3.0-alpha.11 🎉

The release is available on:

Your semantic-release bot 📦🚀

nghaninn pushed a commit that referenced this pull request Sep 24, 2025
@tradetrustimda
Copy link
Copy Markdown

🎉 This PR is included in version 1.3.0-alpha.11 🎉

The release is available on:

Your semantic-release bot 📦🚀

nghaninn pushed a commit that referenced this pull request Sep 24, 2025
# [1.3.0-alpha.12](https://github.com/TrustVC/w3c/compare/@trustvc/w3c-vc@1.3.0-alpha.11...@trustvc/w3c-vc@1.3.0-alpha.12) (2025-09-24)

### Features

* add resolve, resolveRepresentation and dereference functions ([#87](#87)) ([493a248](493a248))
@tradetrustimda
Copy link
Copy Markdown

🎉 This PR is included in version 1.3.0-alpha.12 🎉

The release is available on:

Your semantic-release bot 📦🚀

nghaninn pushed a commit that referenced this pull request Sep 24, 2025
# [1.3.0-alpha.12](https://github.com/TrustVC/w3c/compare/@trustvc/w3c-cli@1.3.0-alpha.11...@trustvc/w3c-cli@1.3.0-alpha.12) (2025-09-24)

### Features

* add resolve, resolveRepresentation and dereference functions ([#87](#87)) ([493a248](493a248))
@tradetrustimda
Copy link
Copy Markdown

🎉 This PR is included in version 1.3.0-alpha.12 🎉

The release is available on:

Your semantic-release bot 📦🚀

nghaninn pushed a commit that referenced this pull request Sep 24, 2025
# [1.3.0-alpha.12](https://github.com/TrustVC/w3c/compare/@trustvc/w3c@1.3.0-alpha.11...@trustvc/w3c@1.3.0-alpha.12) (2025-09-24)

### Features

* add resolve, resolveRepresentation and dereference functions ([#87](#87)) ([493a248](493a248))
@tradetrustimda
Copy link
Copy Markdown

🎉 This PR is included in version 1.3.0-alpha.12 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants