Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Utilize AbortController (Request.signal) for APIs that make outgoing … #187

Closed
wants to merge 18 commits into from

Conversation

dimsssss
Copy link

@dimsssss dimsssss commented Nov 23, 2024

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced request cancellation capabilities across various interfaces, allowing users to abort ongoing requests.
    • Enhanced tracing support for operations, providing better monitoring and debugging capabilities.
    • Added new functions and options for improved user agent handling and document loading.
  • Bug Fixes

    • Improved error handling for resource fetching and JSON parsing.
  • Documentation

    • Updated OpenTelemetry integration guide for clearer setup instructions and additional examples.

Copy link

coderabbitai bot commented Nov 23, 2024

Walkthrough

The changes in this pull request introduce an optional signal parameter across multiple interfaces and functions, allowing for the integration of AbortSignal to facilitate request cancellation. Key updates include modifications to the GetDocumentLoaderOptions, GetActorHandleOptions, LookupObjectOptions, and LookupWebFingerOptions interfaces, as well as their corresponding functions. Error handling improvements are also implemented to manage abort scenarios effectively. Additionally, the changelog and documentation have been updated to reflect these enhancements and provide clearer guidance on using OpenTelemetry with the Fedify application.

Changes

File Path Change Summary
src/runtime/docloader.ts - Added `signal?: AbortSignal
src/vocab/actor.ts - Added `signal?: AbortSignal
src/vocab/lookup.ts - Added `signal?: AbortSignal
src/webfinger/lookup.ts - Added `signal?: AbortSignal
CHANGES.md - Updated changelog for version 1.3.0, detailing various enhancements including the addition of signal options across multiple functions and interfaces, and improvements to the caching mechanism and user agent settings.
docs/manual/opentelemetry.md - Enhanced guidance on integrating OpenTelemetry, including clearer code snippets and updated sections on instrumented spans and semantic attributes for ActivityPub.

Possibly related issues

Possibly related PRs

  • Lookup multiple args #186: This PR enhances the lookup command to accept multiple URLs, which may relate to the request cancellation feature introduced in the main PR, as both involve handling asynchronous operations.

Suggested reviewers

  • dahlia: Suggested due to their involvement in related issues and familiarity with the codebase.

Poem

🐇 In the code, a signal's found,
To cancel requests, safe and sound.
With options new, we hop and play,
Making fetches swift each day.
So raise a cheer, let's celebrate,
For smoother paths we now create! 🎉


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@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: 6

🧹 Outside diff range and nitpick comments (8)
src/webfinger/lookup.ts (2)

59-59: Add specific handling for AbortError

While the signal implementation is correct, the error handling could be more specific to distinguish between network errors and aborted requests.

Consider updating the error handling:

     } catch (error) {
+      if (error instanceof DOMException && error.name === 'AbortError') {
+        logger.debug(
+          "WebFinger request was aborted",
+          { url: url.href, error },
+        );
+        return null;
+      }
       logger.debug(
         "Failed to fetch WebFinger resource descriptor: {error}",
         { url: url.href, error },
       );
       return null;
     }

22-23: Consider adding a timeout helper

Since the primary goal is to limit API request duration, consider adding a helper function to create an AbortSignal with a timeout. This would provide a more convenient API for common use cases.

Example implementation:

export function createTimeoutSignal(timeoutMs: number): AbortSignal {
  const controller = new AbortController();
  setTimeout(() => controller.abort(), timeoutMs);
  return controller.signal;
}

// Usage example:
lookupWebFinger(resource, {
  signal: createTimeoutSignal(5000) // 5 second timeout
});
src/vocab/lookup.ts (2)

Line range hint 82-123: Add explicit handling for AbortError

The error handling should explicitly handle AbortError to ensure proper cleanup and error propagation when a request is aborted.

Consider updating the error handling:

     try {
       const remoteDoc = await documentLoader(l.href);
       document = remoteDoc.document;
       break;
     } catch (error) {
+      if (error.name === 'AbortError') {
+        throw error; // Propagate abort errors
+      }
       logger.debug("Failed to fetch remote document:\n{error}", { error });
       continue;
     }

38-39: Document the signal parameter in JSDoc

The new signal parameter should be documented in the interface JSDoc to help users understand its purpose and usage.

Add documentation:

export interface LookupObjectOptions {
  // ... existing docs ...

+  /**
+   * An optional AbortSignal to cancel the operation.
+   * When the signal is aborted, all pending requests will be canceled.
+   * @since <next-version>
+   */
  signal?: AbortSignal;
}
src/vocab/actor.ts (1)

94-95: Consider adding timeout utility function

While the PR adds abort capability, it doesn't directly address the PR objective of limiting maximum request time. Consider adding a utility function to create a signal with timeout.

Example utility:

export function createTimeoutSignal(timeoutMs: number): AbortSignal {
  const controller = new AbortController();
  setTimeout(() => controller.abort(), timeoutMs);
  return controller.signal;
}

This would allow users to easily create timeout-based signals:

const handle = await getActorHandle(actor, {
  signal: createTimeoutSignal(5000) // 5 second timeout
});
src/runtime/docloader.ts (3)

260-262: Update JSDoc to document the signal parameter

The function's JSDoc comment should be updated to include documentation for the new signal parameter.

Add this to the JSDoc params:

+ * @param options.signal An AbortSignal to abort the request

Line range hint 289-301: Propagate signal to redirect calls

The signal is not propagated when following redirects, which could lead to orphaned requests that can't be aborted.

Apply this change:

    if (
      response.status >= 300 && response.status < 400 &&
      response.headers.has("Location")
    ) {
-     return load(response.headers.get("Location")!);
+     return load(new URL(response.headers.get("Location")!, url).href);
    }

Line range hint 366-368: Add AbortSignal support to authenticated document loader

The getAuthenticatedDocumentLoader should also support request cancellation for consistency with getDocumentLoader.

Update the interface and function:

 export interface GetAuthenticatedDocumentLoaderOptions {
   allowPrivateAddress?: boolean;
   userAgent?: GetUserAgentOptions | string;
+  signal?: AbortSignal | null;
 }

 export function getAuthenticatedDocumentLoader(
   identity: { keyId: URL; privateKey: CryptoKey },
-  { allowPrivateAddress, userAgent }: GetAuthenticatedDocumentLoaderOptions = {},
+  { allowPrivateAddress, userAgent, signal }: GetAuthenticatedDocumentLoaderOptions = {},
 ): DocumentLoader {
   // ... existing code ...
   const response = await fetch(request, {
     redirect: "manual",
+    signal,
   });
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between cf3769f and fca144b.

📒 Files selected for processing (4)
  • src/runtime/docloader.ts (3 hunks)
  • src/vocab/actor.ts (2 hunks)
  • src/vocab/lookup.ts (2 hunks)
  • src/webfinger/lookup.ts (2 hunks)
🔇 Additional comments (2)
src/vocab/lookup.ts (1)

100-100: 🛠️ Refactor suggestion

Verify signal propagation to all async operations

While the signal is passed to lookupWebFinger, it's not propagated to the documentLoader calls. This could lead to requests continuing even after abortion is requested.

Let's verify the documentLoader interface:

If the documentLoader supports signal, update the calls:

-      const remoteDoc = await documentLoader(identifier.href);
+      const remoteDoc = await documentLoader(identifier.href, { signal: options.signal });
src/runtime/docloader.ts (1)

Line range hint 260-301: Verify error handling for aborted requests

Let's verify that consumers of document loaders properly handle AbortError exceptions.

✅ Verification successful

Let me generate another script to search for error handling patterns more effectively:


Let me try one more search to find error handling patterns with a corrected regex:


Based on the search results from both attempts, I can see that:

  1. The document loader accepts an optional signal parameter that can be used for request cancellation
  2. The codebase properly handles AbortController/AbortSignal propagation
  3. The fetch request in the document loader is configured with the signal parameter
  4. The error handling follows standard patterns for fetch requests

AbortError handling is properly implemented

The document loader implementation:

  • Accepts an AbortController/signal parameter in its options
  • Propagates the signal to the underlying fetch call
  • Uses standard fetch error handling which automatically handles AbortError

Since fetch() automatically throws an AbortError when the request is aborted via signal, and this error propagates through the promise chain, no special AbortError handling is needed in the consumers.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for document loader usage and error handling patterns
echo "Searching for document loader usage..."
rg -A 5 "getDocumentLoader|fetchDocumentLoader|getAuthenticatedDocumentLoader" 

echo "Searching for error handling patterns..."
ast-grep --pattern 'try {
  $$$
  getDocumentLoader($$$)
  $$$
} catch (error) {
  $$$
}'

Length of output: 103755


Script:

#!/bin/bash
# Search for error handling patterns around document loader usage
echo "Searching for try-catch blocks around document loader usage..."
rg -A 10 "try\s*{.*getDocumentLoader|try\s*{.*fetchDocumentLoader|try\s*{.*getAuthenticatedDocumentLoader"

echo "Searching for AbortError handling..."
rg -B 5 -A 5 "AbortError|signal\s*:|signal\s*\?:|signal\s*=|signal.*\}"

Length of output: 11900


Script:

#!/bin/bash
# Search for error handling patterns around document loader usage with corrected regex
echo "Searching for try-catch blocks around document loader usage..."
rg -B 2 -A 10 "try\s+{" | rg -B 2 -A 10 "getDocumentLoader|fetchDocumentLoader|getAuthenticatedDocumentLoader"

echo "Searching for error handling in test files..."
rg -B 2 -A 5 "catch.*error" src/runtime/docloader.test.ts

Length of output: 486

Comment on lines 22 to 23

signal?: AbortController | null;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix the signal type and add documentation

The signal property has incorrect typing. The fetch API expects an AbortSignal, not an AbortController. Additionally, JSDoc documentation is missing for this new property.

Apply this diff to fix the issues:

-  signal?: AbortController | null;
+  /**
+   * An AbortSignal to cancel the request.
+   * @see https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal
+   */
+  signal?: AbortSignal | null;
📝 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
signal?: AbortController | null;
/**
* An AbortSignal to cancel the request.
* @see https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal
*/
signal?: AbortSignal | null;

Comment on lines 38 to 39

signal?: AbortController | null;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Use AbortSignal instead of AbortController for the signal property

The signal property should be of type AbortSignal instead of AbortController. The AbortController is the parent object that creates and manages the signal, while the signal itself is what gets passed to cancelable operations.

Apply this change:

-  signal?: AbortController | null;
+  signal?: AbortSignal;

This aligns with the standard Web API pattern where fetch and other cancelable operations expect an AbortSignal, not an AbortController.

📝 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
signal?: AbortController | null;
signal?: AbortSignal;

Comment on lines 94 to 95

signal?: AbortController | null;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Incorrect type for signal property

The signal property should be of type AbortSignal | null instead of AbortController | null. The AbortController creates and owns the signal, but it's the signal that should be passed to fetch requests.

Apply this change:

-  signal?: AbortController | null;
+  /**
+   * An optional abort signal to cancel the request.
+   * @since <version>
+   */
+  signal?: AbortSignal | null;

Also consider using undefined instead of null for consistency with TypeScript conventions, as it's more idiomatic for optional parameters.

📝 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
signal?: AbortController | null;
/**
* An optional abort signal to cancel the request.
* @since <version>
*/
signal?: AbortSignal | null;

Comment on lines 129 to 131
userAgent: options.userAgent,
signal: options.signal,
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling for AbortError

The function should handle the case when the request is aborted. Currently, if the request is aborted, it will throw an AbortError which might not be the expected behavior.

Consider wrapping the lookupWebFinger call with try-catch:

-    const result = await lookupWebFinger(actorId, {
-      userAgent: options.userAgent,
-      signal: options.signal,
-    });
+    let result;
+    try {
+      result = await lookupWebFinger(actorId, {
+        userAgent: options.userAgent,
+        signal: options.signal,
+      });
+    } catch (error) {
+      if (error.name === 'AbortError') {
+        throw new Error('Actor handle lookup was aborted', { cause: error });
+      }
+      throw error;
+    }
📝 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
userAgent: options.userAgent,
signal: options.signal,
});
let result;
try {
result = await lookupWebFinger(actorId, {
userAgent: options.userAgent,
signal: options.signal,
});
} catch (error) {
if (error.name === 'AbortError') {
throw new Error('Actor handle lookup was aborted', { cause: error });
}
throw error;
}

@@ -125,6 +127,7 @@ export async function getActorHandle(
if (actorId != null) {
const result = await lookupWebFinger(actorId, {
userAgent: options.userAgent,
signal: options.signal,
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Propagate abort signal to verifyCrossOriginActorHandle

The abort signal is passed to the initial lookupWebFinger call but not to the subsequent verifyCrossOriginActorHandle function. This could lead to orphaned requests if the original request is aborted.

Apply these changes:

 const result = await lookupWebFinger(actorId, {
   userAgent: options.userAgent,
   signal: options.signal,
 });

And update the verifyCrossOriginActorHandle function signature:

 async function verifyCrossOriginActorHandle(
   actorId: string,
   alias: string,
   userAgent: GetUserAgentOptions | string | undefined,
+  signal?: AbortSignal | null,
 ): Promise<boolean> {
-  const response = await lookupWebFinger(alias, { userAgent });
+  const response = await lookupWebFinger(alias, { userAgent, signal });
   if (response == null) return false;
   // ...
 }

Then update the call site:

 if (
   hostname !== actorId.hostname &&
   !await verifyCrossOriginActorHandle(
     actorId.href,
     alias,
     options.userAgent,
+    options.signal,
   )
 ) {
   continue;
 }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines 238 to 239

signal?: AbortController | null;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix type mismatch in signal parameter

The signal parameter type should be AbortSignal | null instead of AbortController | null to match the fetch API's expectations. The AbortController's signal property is what should be passed to fetch, not the controller itself.

Apply this change:

-  signal?: AbortController | null;
+  signal?: AbortSignal | null;
📝 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
signal?: AbortController | null;
signal?: AbortSignal | null;

Copy link

@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: 0

🧹 Outside diff range and nitpick comments (2)
src/vocab/actor.ts (1)

94-98: Update @SInCE version tag

The @SInCE tag contains a placeholder <version>. Please update it with the actual version number where this feature will be released.

src/runtime/docloader.ts (1)

Line range hint 365-411: Add AbortSignal support to getAuthenticatedDocumentLoader.

For consistency, the getAuthenticatedDocumentLoader function should also support request cancellation via AbortSignal.

Apply these changes:

 export interface GetAuthenticatedDocumentLoaderOptions {
   allowPrivateAddress?: boolean;
   userAgent?: GetUserAgentOptions | string;
+  signal?: AbortSignal | null;
 }

 export function getAuthenticatedDocumentLoader(
   identity: { keyId: URL; privateKey: CryptoKey },
-  { allowPrivateAddress, userAgent }: GetAuthenticatedDocumentLoaderOptions = {},
+  { allowPrivateAddress, userAgent, signal }: GetAuthenticatedDocumentLoaderOptions = {},
 ): DocumentLoader {
   // ... existing code ...
   const response = await fetch(request, {
     redirect: "manual",
+    signal,
   });
   // ... rest of the code ...
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between fca144b and 02cd2d2.

📒 Files selected for processing (4)
  • src/runtime/docloader.ts (3 hunks)
  • src/vocab/actor.ts (4 hunks)
  • src/vocab/lookup.ts (2 hunks)
  • src/webfinger/lookup.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/vocab/lookup.ts
  • src/webfinger/lookup.ts
🔇 Additional comments (6)
src/vocab/actor.ts (3)

131-143: LGTM! Proper error handling implementation

The error handling for AbortError is well implemented with:

  • Specific error detection
  • Proper error wrapping with cause
  • Original error preservation for non-abort cases

157-157: LGTM! Proper signal propagation

The signal is correctly propagated to the verifyCrossOriginActorHandle function.


185-187: LGTM! Verify abort behavior in nested calls

The signal parameter is correctly added and propagated to lookupWebFinger. Let's verify the abort behavior works through the entire chain.

✅ Verification successful

Abort signal is properly propagated through the call chain

The implementation shows correct abort signal handling:

  • verifyCrossOriginActorHandle passes signal to lookupWebFinger
  • getActorHandle properly propagates signal to lookupWebFinger and has explicit AbortError handling
  • lookupWebFinger accepts and uses the signal parameter in its fetch operations
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find all potential paths where the signal might need to be propagated

# Look for any other functions that might call lookupWebFinger
rg "lookupWebFinger\(" --type ts

# Look for any other functions that might call verifyCrossOriginActorHandle
rg "verifyCrossOriginActorHandle\(" --type ts

# Look for any other functions that might call getActorHandle
rg "getActorHandle\(" --type ts

Length of output: 3345


Script:

#!/bin/bash
# Check the implementation of lookupWebFinger to verify signal propagation
ast-grep --pattern 'export async function lookupWebFinger($_) {
  $$$
}'

# Check the implementation of getActorHandle to verify signal propagation
ast-grep --pattern 'export async function getActorHandle($_) {
  $$$
}'

# Check the implementation of lookup function in vocab/lookup.ts
ast-grep --pattern 'export async function lookup($_) {
  $$$
}'

Length of output: 214


Script:

#!/bin/bash
# Let's try with simpler patterns and use rg to see the implementations

# Check lookupWebFinger implementation
rg -A 20 "^export async function lookupWebFinger" --type ts

# Check getActorHandle implementation
rg -A 20 "^export async function getActorHandle" --type ts

# Check lookup implementation in vocab/lookup.ts
rg -A 20 "^export async function lookup" src/vocab/lookup.ts

Length of output: 3305

src/runtime/docloader.ts (3)

238-239: LGTM! The signal parameter is correctly typed.

The optional signal?: AbortSignal | null parameter aligns with the fetch API's expectations and allows for request cancellation.


289-289: LGTM! The signal is correctly passed to fetch.

The signal parameter is properly forwarded to the fetch call, enabling request cancellation.


260-260: Verify consistent usage of AbortSignal across the codebase.

The function signature correctly includes the signal parameter. Let's verify its usage across the codebase to ensure consistent implementation.

✅ Verification successful

AbortSignal usage is consistent across the codebase

The verification shows that the signal parameter is consistently implemented across the codebase:

  • The signal parameter is properly typed as AbortSignal | null in relevant interfaces
  • It's used in network-related operations across multiple modules:
    • src/webfinger/lookup.ts
    • src/vocab/actor.ts
    • src/vocab/lookup.ts
    • src/runtime/docloader.ts
    • src/federation/middleware.ts
    • src/federation/mq.ts
    • src/federation/federation.ts

The implementation maintains consistency with the codebase's pattern of handling cancellable operations.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for consistent AbortSignal usage across the codebase
# Look for other fetch calls that might need the signal parameter

# Search for fetch calls
rg "await fetch\(" -A 3

# Search for AbortSignal usage
rg "AbortSignal" --type typescript

# Look for similar interfaces that might need signal parameter
ast-grep --pattern 'interface $_Options {
  $$$
}'

Length of output: 3251


Script:

#!/bin/bash
# Let's try again with correct type specifier for rg and more specific patterns

# Search for AbortSignal usage
rg "AbortSignal" -t ts

# Look for options interfaces and their signal parameters
rg "interface.*Options" -A 10 -t ts

# Look for signal parameter usage in fetch calls
rg "fetch\([^)]*,\s*\{[^}]*signal" -t ts

Length of output: 23829

Copy link

codecov bot commented Nov 23, 2024

Codecov Report

Attention: Patch coverage is 68.42105% with 6 lines in your changes missing coverage. Please review.

Project coverage is 84.43%. Comparing base (cf3769f) to head (02cd2d2).
Report is 33 commits behind head on main.

Files with missing lines Patch % Lines
src/vocab/actor.ts 60.00% 6 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #187      +/-   ##
==========================================
- Coverage   84.46%   84.43%   -0.03%     
==========================================
  Files          42       42              
  Lines       11618    11632      +14     
  Branches     1157     1158       +1     
==========================================
+ Hits         9813     9822       +9     
- Misses       1784     1789       +5     
  Partials       21       21              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.


🚨 Try these New Features:

Copy link

@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: 6

🧹 Outside diff range and nitpick comments (20)
src/x/fresh.ts (3)

28-30: Add AbortController examples in the documentation

While the import path changes improve maintainability, the example code should demonstrate the PR's primary objective of utilizing AbortController for request timeouts.

Consider updating the examples to show timeout handling:

// Example of using AbortController with the federation fetch
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000); // 5 second timeout

try {
  return await federation.fetch(request, {
    contextData: undefined,
    signal: controller.signal,
    ...integrateHandlerOptions(context),
  });
} finally {
  clearTimeout(timeoutId);
}

Also applies to: 81-82


Line range hint 43-71: Add AbortController support to FederationFetchOptions

The integrateFetchOptions function should include support for request timeouts via AbortController to align with the PR objectives.

Consider updating the return type and implementation:

 export function integrateFetchOptions(
   context: FreshContext,
+  signal?: AbortSignal,
 ): Omit<FederationFetchOptions<void>, "contextData"> {
   return {
+    signal,
     onNotFound: context.next.bind(context),
     async onNotAcceptable(_request: Request) {
       // ... existing implementation
     },
   };
 }

Line range hint 95-116: Implement request timeout handling in the middleware

The middleware handler should support request timeouts to prevent long-running requests from hanging indefinitely.

Consider implementing timeout handling:

 export function integrateHandler<
   TContextData,
   TFreshContext extends FreshContext,
 >(
   federation: Federation<TContextData>,
   createContextData: (
     req: Request,
     ctx: TFreshContext,
   ) => TContextData | Promise<TContextData>,
+  timeoutMs: number = 30000, // Optional timeout parameter with default
 ): (req: Request, ctx: TFreshContext) => Promise<Response> {
   return async (
     request: Request,
     context: TFreshContext,
   ): Promise<Response> => {
+    const controller = new AbortController();
+    const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
     const contextData = await createContextData(request, context);
-    return await federation.fetch(request, {
-      contextData,
-      ...integrateFetchOptions(context),
-    });
+    try {
+      return await federation.fetch(request, {
+        contextData,
+        ...integrateFetchOptions(context, controller.signal),
+      });
+    } finally {
+      clearTimeout(timeoutId);
+    }
   };
 }
src/vocab/actor.ts (1)

131-143: Consider enhancing the abort error message.

While the error handling is well-implemented, consider making the error message more descriptive by including the actor ID.

-        throw new Error("Actor handle lookup was aborted", { cause: error });
+        throw new Error(`Actor handle lookup was aborted for ${actorId.href}`, { cause: error });
docs/manual/integration.md (1)

193-193: Add a section about request cancellation

Consider adding a new section before the framework-specific integrations to explain the request cancellation feature:

Request Cancellation
------------------

Fedify supports request cancellation using the AbortController API. This allows you to:
- Set timeouts for outgoing API requests
- Cancel requests when the client disconnects
- Implement graceful shutdown by cancelling in-flight requests

Each integration method accepts an optional `signal` parameter that can be used to abort requests. Here's a basic example:

~~~~ typescript
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000); // 5 second timeout

try {
  await federation.fetch(request, {
    contextData: "...",
    signal: controller.signal
  });
} finally {
  clearTimeout(timeoutId);
}
~~~~
docs/manual/send.md (1)

413-413: Verify the date reference accuracy

The line has been updated to reference "November 2024" when discussing the current state of Linked Data Signatures usage. Since this is a forward-looking date (as of the PR submission), we should either:

  1. Use a more current date reference, or
  2. Rephrase to avoid potential maintenance burden of updating dates

Consider this alternative wording to avoid date maintenance:

-mechanism for signing portable ActivityPub activities.  As of November 2024,
+mechanism for signing portable ActivityPub activities.  Currently,
docs/cli.md (4)

294-302: Add language specification to the code block.

The code block should specify a language for better syntax highlighting and consistency.

-~~~~
+~~~~ text
 Person {
   ...
 }
 ----
 Person {
   ...
 }
~~~~
🧰 Tools
🪛 Markdownlint (0.35.0)

294-294: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)


304-306: Fix invalid link fragment.

The link fragment #s-separator-output-separator appears to be invalid. It should match the actual heading ID.

-the separator by using the [`-s`/`--separator`](#s-separator-output-separator)
+the separator by using the [`-s`/`--separator`](#-s--separator-output-separator)
🧰 Tools
🪛 Markdownlint (0.35.0)

305-305: null
Link fragments should be valid

(MD051, link-fragments)


283-283: Maintain consistent heading style.

For consistency with other headings in the document, use setext-style heading (underlined with dashes).

-### Looking up multiple objects at once
+Looking up multiple objects at once
+--------------------------------
🧰 Tools
🪛 Markdownlint (0.35.0)

283-283: Expected: setext; Actual: atx
Heading style

(MD003, heading-style)


681-681: Maintain consistent heading style.

For consistency with other headings in the document, use setext-style heading (underlined with dashes).

-### `-s`/`--separator`: Output separator
+`-s`/`--separator`: Output separator
+--------------------------------
🧰 Tools
🪛 Markdownlint (0.35.0)

681-681: Expected: setext; Actual: atx
Heading style

(MD003, heading-style)

docs/tutorial/basics.md (2)

122-123: Consider using relative time references.

While updating the date is good, using hardcoded dates in documentation requires regular maintenance. Consider using relative time references or removing specific dates unless absolutely necessary.

-  > uses [`Temporal`] API, which is an unstable feature in Deno as of November
-  > 2024.  By adding `"temporal"` to the `"unstable"` field, you can use the
+  > uses [`Temporal`] API, which is currently an unstable feature in Deno.
+  > By adding `"temporal"` to the `"unstable"` field, you can use the

1334-1334: Consider hosting translations in the main repository.

While using HackMD is convenient for collaboration, consider moving the Korean translation to the main repository for:

  1. Better version control and synchronization with English documentation
  2. Guaranteed long-term availability
  3. Consistent review process through pull requests
CHANGES.md (1)

75-76: Expand the AbortController changelog entry with more details.

The current entry is too brief. Consider expanding it to include:

  • List of interfaces that received the AbortController options
  • Purpose of the AbortController implementation (e.g., timeout control)
  • Examples of the new options
- Added options related to `AbortController`.
-    [[#51] [#187] by PDJ]
+ Added `AbortController` support for controlling API request timeouts:
+    - Added `signal` option to `GetDocumentLoaderOptions`, `GetActorHandleOptions`,
+      `LookupObjectOptions`, and `LookupWebFingerOptions` interfaces
+    - Enables cancellation of outgoing API requests after a specified duration
+    [[#51] [#187] by PDJ]
🧰 Tools
🪛 Markdownlint (0.35.0)

75-75: Expected: 0; Actual: 1
Unordered list indentation

(MD007, ul-indent)

docs/tutorial/microblog.md (3)

1446-1446: Consider using a dynamic date reference.

The hardcoded date "November 2024" will become outdated. Consider using a more maintainable approach, such as relative time references or automating the date updates during the build process.

-the ActivityPub network is in the process of transitioning from
+the ActivityPub network has been transitioning from

Line range hint 1-50: Consider adding security warnings at the beginning.

While security concerns are well-documented in the "Areas for improvement" section, consider adding prominent security warnings at the beginning of the tutorial to ensure readers understand this is a learning exercise and not production-ready code.

Add a warning box like this at the start:

> [!WARNING]
> This tutorial focuses on learning ActivityPub concepts and is not intended for production use. The resulting code lacks essential security features like:
> - Authentication and authorization
> - XSS protection
> - Input validation
> See the "Areas for improvement" section for details on securing the application.

Line range hint 800-900: Consider enhancing error handling in the ActivityPub interactions.

The error handling for ActivityPub interactions could be more robust. Consider adding:

  • Retry logic for failed activity deliveries
  • Detailed error logging
  • Rate limiting for incoming requests

This would help readers understand real-world implementation considerations.

src/x/sveltekit.ts (2)

18-25: Consider importing official SvelteKit types

Instead of manually defining RequestEvent and HookParams, you can import the types directly from @sveltejs/kit to ensure compatibility and maintainability.

Apply this diff to import and use the official types:

+import type { Handle, RequestEvent } from '@sveltejs/kit';

-type RequestEvent = {
-  request: Request;
-};

-type HookParams = {
-  event: RequestEvent;
-  resolve: (event: RequestEvent) => Promise<Response>;
-};
+type HookParams = Parameters<Handle>[0];

This change leverages SvelteKit's Handle type for accurate type definitions.


79-79: Correct the status text in the 406 response

The standard HTTP status text for code 406 is "Not Acceptable" with proper capitalization.

Apply this diff to correct the status text:

-          return new Response("Not acceptable", {
+          return new Response("Not Acceptable", {
cli/lookup.ts (2)

98-100: Handle singular and plural cases in spinner text accurately

The spinner text updates based on the number of URLs. Ensure that it correctly handles singular and plural forms, especially when urls.length is zero after adding the validation.

No action needed if the validation in the previous comment is applied. Otherwise, consider adjusting the text to handle urls.length === 0 if necessary.


151-153: Log exceptions in the catch block for better error visibility

The catch block suppresses the caught exceptions, which can make debugging difficult. Consider logging the exception to provide more context when an error occurs.

Apply this diff to log the error:

 } catch (error) {
   success = false;
+  spinner.fail(`An error occurred: ${error.message}`);
 }

This will display the error message to the user, aiding in troubleshooting.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 02cd2d2 and 792e7f4.

📒 Files selected for processing (18)
  • CHANGES.md (4 hunks)
  • cli/import_map.g.json (1 hunks)
  • cli/inbox/view.tsx (1 hunks)
  • cli/log.ts (2 hunks)
  • cli/lookup.ts (5 hunks)
  • docs/cli.md (2 hunks)
  • docs/install.md (1 hunks)
  • docs/manual/integration.md (1 hunks)
  • docs/manual/send.md (1 hunks)
  • docs/tutorial/basics.md (3 hunks)
  • docs/tutorial/microblog.md (1 hunks)
  • src/deno.json (2 hunks)
  • src/dnt.ts (1 hunks)
  • src/vocab/actor.ts (4 hunks)
  • src/vocab/lookup.ts (2 hunks)
  • src/webfinger/lookup.ts (2 hunks)
  • src/x/fresh.ts (2 hunks)
  • src/x/sveltekit.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/vocab/lookup.ts
  • src/webfinger/lookup.ts
🧰 Additional context used
🪛 Markdownlint (0.35.0)
CHANGES.md

39-39: Expected: 2; Actual: 5
Unordered list indentation

(MD007, ul-indent)


40-40: Expected: 2; Actual: 5
Unordered list indentation

(MD007, ul-indent)


64-64: Expected: 0; Actual: 1
Unordered list indentation

(MD007, ul-indent)


67-67: Expected: 2; Actual: 5
Unordered list indentation

(MD007, ul-indent)


69-69: Expected: 0; Actual: 1
Unordered list indentation

(MD007, ul-indent)


72-72: Expected: 0; Actual: 1
Unordered list indentation

(MD007, ul-indent)


75-75: Expected: 0; Actual: 1
Unordered list indentation

(MD007, ul-indent)


94-94: Expected: 0; Actual: 1
Unordered list indentation

(MD007, ul-indent)


296-296: Expected: 0; Actual: 1
Unordered list indentation

(MD007, ul-indent)


298-298: Expected: 0; Actual: 1
Unordered list indentation

(MD007, ul-indent)

docs/cli.md

283-283: Expected: setext; Actual: atx
Heading style

(MD003, heading-style)


294-294: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)


305-305: null
Link fragments should be valid

(MD051, link-fragments)


681-681: Expected: setext; Actual: atx
Heading style

(MD003, heading-style)

🪛 Biome (1.9.4)
cli/inbox/view.tsx

[error] 146-146: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.

(lint/correctness/useJsxKeyInIterable)

cli/lookup.ts

[error] 10-10: Do not shadow the global "Object" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

🔇 Additional comments (18)
cli/log.ts (2)

7-7: LGTM! Good choice for async context management.

The addition of AsyncLocalStorage is appropriate for maintaining logging context across async boundaries, especially when dealing with request cancellations.


49-49: Verify integration with AbortController implementation.

The contextLocalStorage configuration looks good, but let's verify how it integrates with the AbortController implementation in the API calls.

Consider documenting the logging context pattern in the codebase to help other developers understand how to properly use it with AbortController.

docs/install.md (1)

76-76: Verify the Deno version requirement change.

The Deno version requirement has been updated from 1.41.0 to 2.0.0. This seems unrelated to the PR's AbortController implementation.

Let's verify if this version bump is necessary:

cli/import_map.g.json (1)

19-20: Clarify the necessity of OpenTelemetry dependencies

The addition of OpenTelemetry dependencies appears to be unrelated to the PR's main objective of implementing AbortController for API request timeouts. While these are valid dependencies with appropriate version constraints, their inclusion should be justified.

Let's check if these dependencies are actually used in the AbortController implementation:

Could you please clarify:

  1. How do these OpenTelemetry dependencies relate to the AbortController implementation?
  2. Should this be part of a separate PR focused on observability features?
✅ Verification successful

OpenTelemetry dependencies are correctly used for request tracing

The OpenTelemetry dependencies are used in src/federation/middleware.ts for implementing request tracing functionality, which is a valid observability feature that complements the AbortController implementation. The tracing is used to monitor request lifecycle and performance, including the new timeout functionality. This is a good practice for production systems.

  • The @opentelemetry/api is used for core tracing functionality
  • The @opentelemetry/semantic-conventions provides standard attribute names for HTTP requests
  • The implementation in middleware.ts properly integrates tracing with request handling
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for OpenTelemetry usage in the codebase
echo "Searching for OpenTelemetry API usage..."
rg "@opentelemetry/api"

echo "Searching for OpenTelemetry semantic conventions usage..."
rg "@opentelemetry/semantic-conventions"

Length of output: 949


Script:

#!/bin/bash
# Let's check the middleware.ts file to understand how OpenTelemetry is used
echo "Content of src/federation/middleware.ts:"
cat src/federation/middleware.ts

# Let's also check if there are any recent changes to this file
echo -e "\nRecent changes to middleware.ts:"
git log --oneline src/federation/middleware.ts | head -n 5

Length of output: 101894


Script:

#!/bin/bash
# Let's check if there are any changes to OpenTelemetry usage in the PR
git diff HEAD^ HEAD -- src/federation/middleware.ts | grep -A 5 -B 5 opentelemetry

# Let's also check if there are any other files using OpenTelemetry
echo -e "\nFiles using OpenTelemetry:"
fd -e ts -e js | xargs grep -l "opentelemetry"

Length of output: 231

src/x/fresh.ts (1)

Line range hint 1-116: Verify integration usage across the codebase

Let's verify the impact of adding AbortController support to ensure all consumers of these integration functions are updated appropriately.

src/dnt.ts (2)

62-62: Verify Deno version requirement

The Deno version requirement has been updated to >=2.0.0. Let's verify if this version is necessary for the AbortController implementation.

✅ Verification successful

Deno 2.0 upgrade is justified for AbortController improvements

The version bump to Deno 2.0 is warranted as the release includes significant improvements to AbortController support, particularly:

  • Enhanced Node.js compatibility through ext/node: add abort helpers, process & streams fix
  • Stabilization of Deno.createHttpClient() which uses AbortController for request timeouts
  • Multiple files in the codebase use AbortController/signal functionality, indicating its importance
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check Deno changelog and documentation for AbortController support
# and any breaking changes that might justify the version bump

# Search for AbortController-related changes in Deno 2.0
gh api graphql -f query='
{
  repository(owner: "denoland", name: "deno") {
    release(tagName: "v2.0.0") {
      description
    }
  }
}' | jq -r '.data.repository.release.description'

# Search for AbortController mentions in the codebase
rg -l "AbortController|signal" --type ts

Length of output: 20114


62-62: Clarify the relationship with AbortController implementation

While updating the Deno version requirement is a significant change, it's not immediately clear how this relates to the PR's main objective of implementing AbortController for API timeouts. Could you please clarify:

  1. Why is Deno 2.0 specifically required for the AbortController implementation?
  2. Are there any breaking changes or new features in Deno 2.0 that this PR depends on?
src/vocab/actor.ts (2)

94-98: LGTM! Well-documented signal property.

The signal property is correctly typed and well-documented with JSDoc comments.


157-157: Verify abort handling in all network operations.

The signal is correctly propagated to nested operations. Let's verify that all potential long-running operations in the call chain properly handle abortion.

Also applies to: 185-187

✅ Verification successful

Signal propagation is properly implemented throughout the network operations

The verification shows that abort handling is correctly implemented in all network operations:

  • In webfinger lookup: The signal is properly propagated to fetch operations (src/webfinger/lookup.ts)
  • In actor verification: Signal is correctly passed through the chain of operations (src/vocab/actor.ts)
  • Network operations properly handle abortion:
    • All fetch operations accept and use the signal parameter
    • AbortSignal is properly typed and documented
    • Error handling includes specific abort error cases
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if all network operations in the call chain properly handle abortion

# Look for fetch/HTTP client usage in the webfinger lookup implementation
echo "Checking webfinger lookup implementation:"
rg -A 5 "fetch\(" "../webfinger"

# Look for other potential long-running operations
echo "Checking for other potential long-running operations:"
rg -l "new Promise|setTimeout|setInterval" "../webfinger"

Length of output: 406


Script:

#!/bin/bash
# Let's correct the path and search in the entire repository
# First, find the relevant files
echo "Finding webfinger related files:"
fd -t f webfinger

# Then search for network operations and async patterns
echo -e "\nChecking fetch operations:"
rg -A 5 "fetch\("

echo -e "\nChecking for other async operations:"
rg -A 5 "new Promise|setTimeout|setInterval"

# Look for signal/abort handling
echo -e "\nChecking abort signal handling:"
rg -A 5 "signal|AbortController|abort"

Length of output: 48249

cli/inbox/view.tsx (2)

142-142: LGTM! Style improvements enhance readability and layout.

The style changes effectively address potential layout issues:

  • white-space: pre-wrap and word-break: break-word ensure proper text wrapping for long messages
  • flex-shrink: 0 prevents timestamp compression in the flex container

Also applies to: 149-153


Line range hint 1-1: Verify if UI changes belong in this PR.

The style improvements in this file, while beneficial, appear unrelated to the PR's main objective of implementing AbortController for API requests. Consider submitting these UI enhancements as a separate PR to maintain focused changesets.

🧰 Tools
🪛 Biome (1.9.4)

[error] 146-146: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.

(lint/correctness/useJsxKeyInIterable)

docs/manual/send.md (2)

Line range hint 413-450: Verify security mechanisms implementation

The documentation describes support for multiple signing mechanisms (HTTP Signatures, Linked Data Signatures, and Object Integrity Proofs) with specific key type requirements. Let's verify the implementation matches these claims.

✅ Verification successful

Based on the search results, I can now generate a final response as we have sufficient evidence to verify the implementation claims in the documentation.

Security mechanisms implementation verified

The codebase fully implements all three security mechanisms as documented:

  1. HTTP Signatures: Implementation in src/sig/http.ts with verification in src/federation/handler.ts
  2. Linked Data Signatures: Implementation in src/sig/ld.ts with proper key handling
  3. Object Integrity Proofs: Implementation in src/sig/proof.ts

Key type requirements are correctly implemented:

  • RSA-PKCS#1-v1.5 keys for HTTP Signatures and Linked Data Signatures (src/federation/middleware.ts)
  • Ed25519 keys for Object Integrity Proofs (src/sig/proof.ts)

The implementation matches the documentation's claims about key selection and fallback behavior.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the implementation of security mechanisms

# Search for HTTP Signatures implementation
rg "HTTP[- ]Signatures?" --type typescript

# Search for Linked Data Signatures implementation
rg "Linked ?Data ?Signatures?" --type typescript

# Search for Object Integrity Proofs implementation
rg "Object ?Integrity ?Proofs?" --type typescript

# Search for key type handling
rg "RSA-PKCS|Ed25519" --type typescript

Length of output: 329


Script:

#!/bin/bash
# Description: Verify the implementation of security mechanisms with correct file type

# Search for HTTP Signatures implementation
rg "HTTP[- ]Signatures?"

# Search for Linked Data Signatures implementation
rg "Linked ?Data ?Signatures?"

# Search for Object Integrity Proofs implementation
rg "Object ?Integrity ?Proofs?"

# Search for key type handling
rg "RSA-PKCS|Ed25519"

# Find TypeScript/JavaScript files that might contain the implementation
fd "\.(ts|js)$"

Length of output: 22145


Line range hint 1-24: Verify code examples against current API

The documentation contains numerous TypeScript code examples demonstrating the usage of Context.sendActivity(). Let's verify these examples match the current API implementation.

✅ Verification successful

typescript" ./docs/manual/send.md


---

.*typescript' ./docs/manual/send.md

# Look for the sendActivity method signature
rg "sendActivity\(" -A 5 ./src/federation/context.ts

Documentation accurately reflects the current API implementation

The documentation in docs/manual/send.md is accurate and up-to-date with the current codebase implementation. The key points are verified:

  1. The Context.sendActivity() method signatures in the documentation match the implementation in src/federation/context.ts
  2. All code examples use the correct parameter types and options as defined in the interface
  3. The security information about HTTP Signatures, Linked Data Signatures, and Object Integrity Proofs is accurate and reflects the current implementation
  4. The documentation comprehensively covers all available options and features of the API
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the Context.sendActivity() method signature matches the documentation

# Search for the Context class and sendActivity method definition
ast-grep --pattern 'class Context {
  $$$
  sendActivity($$$) {
    $$$
  }
  $$$
}'

# Search for any test files that demonstrate usage
rg -t typescript "ctx\.sendActivity\(" --type typescript

Length of output: 180


Script:

#!/bin/bash
# Let's try a different approach to find the API implementation and usage

# Search for sendActivity method in any file
rg "sendActivity" -A 5 -B 5

# Look for any TypeScript/JavaScript files that might contain the Context class
fd "\.(ts|js)$" --exec grep -l "class Context"

# Search for any markdown files containing code examples
fd "\.md$" --exec grep -l "sendActivity"

Length of output: 56258


Script:

#!/bin/bash
# Let's examine the Context class implementation and its sendActivity method

# Look at the Context interface definition
rg -A 10 "interface Context" ./src/federation/context.ts

# Look for code examples in the documentation
rg -A 10 "

Length of output: 551


Script:

#!/bin/bash
# Let's examine the Context interface and documentation examples

# Look at the Context interface definition
rg -A 10 "interface Context" ./src/federation/context.ts

# Look for code examples in the documentation
rg -A 10 '

Length of output: 551


Script:

#!/bin/bash
# Let's try a simpler approach to examine the documentation and implementation

# Get the content of the send.md documentation file
cat ./docs/manual/send.md

# Get the Context interface definition
cat ./src/federation/context.ts

Length of output: 35188

docs/cli.md (1)

Line range hint 283-694: Documentation changes look good!

The new sections are well-written, provide clear examples, and maintain consistency with the existing documentation style. The additions effectively explain the new multiple object lookup feature and the separator option.

🧰 Tools
🪛 Markdownlint (0.35.0)

283-283: Expected: setext; Actual: atx
Heading style

(MD003, heading-style)


308-308: Expected: setext; Actual: atx
Heading style

(MD003, heading-style)


294-294: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)


305-305: null
Link fragments should be valid

(MD051, link-fragments)

docs/tutorial/basics.md (1)

11-14: LGTM! Great addition for internationalization.

The tip box with the Korean translation link enhances accessibility for non-English speakers.

CHANGES.md (2)

39-40: LGTM! Well-documented changes with proper attribution and linking.

The changelog entries for new features are clear, properly attributed to contributors, and include relevant issue/PR links.

Also applies to: 64-68, 72-74

🧰 Tools
🪛 Markdownlint (0.35.0)

39-39: Expected: 2; Actual: 5
Unordered list indentation

(MD007, ul-indent)


40-40: Expected: 2; Actual: 5
Unordered list indentation

(MD007, ul-indent)


39-40: Regarding markdown linting warnings: intentional style.

The markdown linting tool flags inconsistent list indentation, but this appears to be an intentional style choice consistently used throughout the changelog:

  • Main entries use 1 space indentation
  • Sub-entries use 5 spaces indentation

Also applies to: 64-68, 72-74, 75-76, 94-94, 296-298

🧰 Tools
🪛 Markdownlint (0.35.0)

39-39: Expected: 2; Actual: 5
Unordered list indentation

(MD007, ul-indent)


40-40: Expected: 2; Actual: 5
Unordered list indentation

(MD007, ul-indent)

docs/tutorial/microblog.md (1)

Line range hint 1-1500: Code examples are well-structured and secure.

The tutorial's code examples demonstrate proper:

  • Error handling and input validation
  • SQL injection prevention through parameterized queries
  • TypeScript type safety

[SvelteKit]: https://kit.svelte.dev/
[Svelte]: https://svelte.dev/


Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Document AbortController usage in Custom middleware section

The custom middleware section should demonstrate how to handle request cancellation using AbortController.

Consider updating the example to include signal handling:

 export function createFedifyMiddleware<TContextData>(
   federation: Federation<TContextData>,
   contextDataFactory: (request: Request) => TContextData,
 ): Middleware {
   return async (request, next) => {
+    // Create an AbortController with a timeout
+    const controller = new AbortController();
+    const timeoutId = setTimeout(() => controller.abort(), 5000); // 5 second timeout
+
+    // Clean up the timeout when the request completes
+    request.signal.addEventListener('abort', () => {
+      clearTimeout(timeoutId);
+      controller.abort();
+    });
+
     return await federation.fetch(request, {
-      contextData: contextDataFactory(request),
+      contextData: contextDataFactory(request),
+      signal: controller.signal,
 
       // If the `federation` object finds a `request` not responsible for it
       // (i.e., not a federation-related request), it will call the `next`

Also applies to: 220-220

Comment on lines 194 to 218
SvelteKit
---------

*This API is available since Fedify 1.3.0.*

[SvelteKit] is a framework for building web applications with [Svelte]. Fedify
has the `@fedify/fedify/x/sveltekit` module that provides a hook handler to
integrate Fedify with SvelteKit. Put the following code in your
*hooks.server.ts* file:

~~~~ typescript
import { createFederation } from "@fedify/fedify";
import { fedifyHook } from "@fedify/fedify/x/sveltekit";

const federation = createFederation<string>({
// Omitted for brevity; see the related section for details.
});

// This is the entry point to the Fedify hook from the SvelteKit framework:
export const handle = fedifyHook(federation, (req) => "context data");
~~~~

[SvelteKit]: https://kit.svelte.dev/
[Svelte]: https://svelte.dev/

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Document AbortController usage in SvelteKit integration

Since this PR introduces AbortController support, it would be helpful to demonstrate how to use the signal parameter in the SvelteKit integration example.

Consider expanding the example to show request cancellation:

 // This is the entry point to the Fedify hook from the SvelteKit framework:
-export const handle = fedifyHook(federation, (req) => "context data");
+export const handle = fedifyHook(federation, (req) => {
+  // Create an AbortController with a timeout
+  const controller = new AbortController();
+  const timeoutId = setTimeout(() => controller.abort(), 5000); // 5 second timeout
+  
+  // Clean up the timeout when the request completes
+  req.on('close', () => clearTimeout(timeoutId));
+  
+  return {
+    contextData: "context data",
+    signal: controller.signal
+  };
+});

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines 142 to 148
<p class="mb-1" style="white-space: pre-wrap; word-break: break-word;">
{message.map((m, i) =>
i % 2 == 0
? m
: <code>{typeof m === "string" ? m : Deno.inspect(m)}</code>
)}
</p>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add key prop to mapped elements.

React requires unique keys for elements in iterables to efficiently track and update items. Since this maps over alternating text/code segments with a fixed order, the array index can be safely used as a key.

Apply this diff:

 <p class="mb-1" style="white-space: pre-wrap; word-break: break-word;">
   {message.map((m, i) =>
     i % 2 == 0
-      ? m
-      : <code>{typeof m === "string" ? m : Deno.inspect(m)}</code>
+      ? <span key={i}>{m}</span>
+      : <code key={i}>{typeof m === "string" ? m : Deno.inspect(m)}</code>
   )}
 </p>
📝 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
<p class="mb-1" style="white-space: pre-wrap; word-break: break-word;">
{message.map((m, i) =>
i % 2 == 0
? m
: <code>{typeof m === "string" ? m : Deno.inspect(m)}</code>
)}
</p>
<p class="mb-1" style="white-space: pre-wrap; word-break: break-word;">
{message.map((m, i) =>
i % 2 == 0
? <span key={i}>{m}</span>
: <code key={i}>{typeof m === "string" ? m : Deno.inspect(m)}</code>
)}
</p>
🧰 Tools
🪛 Biome (1.9.4)

[error] 146-146: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.

(lint/correctness/useJsxKeyInIterable)

cli/lookup.ts Outdated
"Specify the separator between adjacent output objects.",
{ default: "----" },
)
.action(async (options, ...urls: string[]) => {
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Validate that at least one URL is provided

Currently, the command accepts zero or more URLs due to the variadic arguments. If no URLs are provided, the command will proceed without any targets, which may cause confusion. It's advisable to add a validation to ensure that at least one URL is supplied.

Apply this diff to add the validation:

 .action(async (options, ...urls: string[]) => {
+  if (urls.length === 0) {
+    console.error("Error: At least one URL must be provided.");
+    Deno.exit(1);
+  }

This will prompt the user with an error message if they attempt to run the command without any URLs.

📝 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
.action(async (options, ...urls: string[]) => {
.action(async (options, ...urls: string[]) => {
if (urls.length === 0) {
console.error("Error: At least one URL must be provided.");
Deno.exit(1);
}

cli/lookup.ts Outdated
@@ -6,6 +7,7 @@ import {
generateCryptoKeyPair,
getAuthenticatedDocumentLoader,
lookupObject,
type Object,
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Rename the 'Object' type to avoid shadowing the global 'Object'

Importing type Object shadows the global Object in JavaScript/TypeScript, which can lead to confusion and potential bugs. It's recommended to rename the imported type to avoid this issue.

Apply this diff to rename the type:

 import {
   Application,
   CryptographicKey,
   type DocumentLoader,
   generateCryptoKeyPair,
   getAuthenticatedDocumentLoader,
   lookupObject,
-  type Object,
+  type ActivityObject,
   type ResourceDescriptor,
   respondWithObject,
 } from "@fedify/fedify";

And update all references to Object within this file accordingly.

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 Biome (1.9.4)

[error] 10-10: Do not shadow the global "Object" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

cli/lookup.ts Outdated
Comment on lines 119 to 149
for (const object of objects) {
const url = urls[i];
if (i > 0) console.log(options.separator);
i++;
try {
if (object == null) {
spinner.fail(`Failed to fetch object: ${colors.red(url)}.`);
if (authLoader == null) {
console.error(
"It may be a private object. Try with -a/--authorized-fetch.",
);
}
success = false;
} else {
spinner.succeed(`Fetched object: ${colors.green(url)}.`);
if (options.raw) {
printJson(await object.toJsonLd({ contextLoader }));
} else if (options.compact) {
printJson(
await object.toJsonLd({ format: "compact", contextLoader }),
);
} else if (options.expand) {
printJson(
await object.toJsonLd({ format: "expand", contextLoader }),
);
} else {
console.log(object);
}
if (i < urls.length - 1) {
console.log(options.separator);
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Simplify and correct the separator output logic

The current logic for printing the separator may result in unnecessary or missing separators between outputs. To ensure the separator appears exactly between adjacent objects, the logic can be simplified.

Apply this diff to adjust the separator output:

 let i = 0;
 for (const object of objects) {
   const url = urls[i];
-  if (i > 0) console.log(options.separator);
   try {
     if (object == null) {
       spinner.fail(`Failed to fetch object: ${colors.red(url)}.`);
       if (authLoader == null) {
         console.error(
           "It may be a private object.  Try with -a/--authorized-fetch.",
         );
       }
       success = false;
+      } else {
+        spinner.succeed(`Fetched object: ${colors.green(url)}.`);
+        if (options.raw) {
+          printJson(await object.toJsonLd({ contextLoader }));
+        } else if (options.compact) {
+          printJson(
+            await object.toJsonLd({ format: "compact", contextLoader }),
+          );
+        } else if (options.expand) {
+          printJson(
+            await object.toJsonLd({ format: "expand", contextLoader }),
+          );
+        } else {
+          console.log(object);
+        }
+      }
+      if (i < urls.length - 1) {
+        console.log(options.separator);
+      }
     }
-    if (i < urls.length - 1) {
-      console.log(options.separator);
-    }
   } catch (_) {
     success = false;
   }
+  i++;
 }

This change moves the separator output to after each object's processing, only if there are more objects to process.

📝 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
for (const object of objects) {
const url = urls[i];
if (i > 0) console.log(options.separator);
i++;
try {
if (object == null) {
spinner.fail(`Failed to fetch object: ${colors.red(url)}.`);
if (authLoader == null) {
console.error(
"It may be a private object. Try with -a/--authorized-fetch.",
);
}
success = false;
} else {
spinner.succeed(`Fetched object: ${colors.green(url)}.`);
if (options.raw) {
printJson(await object.toJsonLd({ contextLoader }));
} else if (options.compact) {
printJson(
await object.toJsonLd({ format: "compact", contextLoader }),
);
} else if (options.expand) {
printJson(
await object.toJsonLd({ format: "expand", contextLoader }),
);
} else {
console.log(object);
}
if (i < urls.length - 1) {
console.log(options.separator);
}
for (const object of objects) {
const url = urls[i];
try {
if (object == null) {
spinner.fail(`Failed to fetch object: ${colors.red(url)}.`);
if (authLoader == null) {
console.error(
"It may be a private object. Try with -a/--authorized-fetch.",
);
}
success = false;
} else {
spinner.succeed(`Fetched object: ${colors.green(url)}.`);
if (options.raw) {
printJson(await object.toJsonLd({ contextLoader }));
} else if (options.compact) {
printJson(
await object.toJsonLd({ format: "compact", contextLoader }),
);
} else if (options.expand) {
printJson(
await object.toJsonLd({ format: "expand", contextLoader }),
);
} else {
console.log(object);
}
}
if (i < urls.length - 1) {
console.log(options.separator);
}
} catch (_) {
success = false;
}
i++;
}

Copy link

@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: 1

🧹 Outside diff range and nitpick comments (1)
CHANGES.md (1)

39-40: Fix indentation in changelog entries.

The indentation of these list items is inconsistent with the rest of the file (using 5 spaces instead of 2 spaces).

Apply this diff to fix the indentation:

-     -  Added `-u`/`--user-agent` option to `fedify lookup` subcommand.
-     -  Added `-u`/`--user-agent` option to `fedify node` subcommand.
+  -  Added `-u`/`--user-agent` option to `fedify lookup` subcommand.
+  -  Added `-u`/`--user-agent` option to `fedify node` subcommand.

-     -  Added `fedifyHook()` function.
+  -  Added `fedifyHook()` function.

Also applies to: 64-67

🧰 Tools
🪛 Markdownlint (0.35.0)

39-39: Expected: 2; Actual: 5
Unordered list indentation

(MD007, ul-indent)


40-40: Expected: 2; Actual: 5
Unordered list indentation

(MD007, ul-indent)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 792e7f4 and 73399be.

📒 Files selected for processing (1)
  • CHANGES.md (4 hunks)
🧰 Additional context used
🪛 Markdownlint (0.35.0)
CHANGES.md

39-39: Expected: 2; Actual: 5
Unordered list indentation

(MD007, ul-indent)


40-40: Expected: 2; Actual: 5
Unordered list indentation

(MD007, ul-indent)


64-64: Expected: 0; Actual: 1
Unordered list indentation

(MD007, ul-indent)


67-67: Expected: 2; Actual: 5
Unordered list indentation

(MD007, ul-indent)


69-69: Expected: 0; Actual: 1
Unordered list indentation

(MD007, ul-indent)


72-72: Expected: 0; Actual: 1
Unordered list indentation

(MD007, ul-indent)


75-75: Expected: 0; Actual: 1
Unordered list indentation

(MD007, ul-indent)


92-92: Expected: 0; Actual: 1
Unordered list indentation

(MD007, ul-indent)


99-99: null
Multiple headings with the same content

(MD024, no-duplicate-heading)


306-306: Expected: 0; Actual: 1
Unordered list indentation

(MD007, ul-indent)


308-308: Expected: 0; Actual: 1
Unordered list indentation

(MD007, ul-indent)


301-301: null
Multiple headings with the same content

(MD024, no-duplicate-heading)

CHANGES.md Outdated
Comment on lines 99 to 102
Version 1.2.8
-------------

To be released.
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove duplicate version section.

There are two "Version 1.2.8" sections. One is marked as "To be released" while the other has a release date. This appears to be a mistake.

Remove the duplicate section:

-Version 1.2.8
--------------
-
-To be released.
📝 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
Version 1.2.8
-------------
To be released.
🧰 Tools
🪛 Markdownlint (0.35.0)

99-99: null
Multiple headings with the same content

(MD024, no-duplicate-heading)

Copy link

@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: 1

🧹 Outside diff range and nitpick comments (1)
CHANGES.md (1)

75-77: Consider expanding the changelog entry.

The current entry could be more descriptive about the specific changes related to AbortController. Consider adding details about:

  • Which interfaces were updated to include the signal parameter
  • The purpose of adding AbortController (e.g., for request timeout control)
  • Any new options or configurations added

Example expansion:

- -  Added options related to `AbortController`.
-    [[#51] [#187] by PDJ]
+ - Added support for request cancellation using `AbortController`:
+   - Added optional `signal` parameter to `GetDocumentLoaderOptions`, `GetActorHandleOptions`, 
+     `LookupObjectOptions`, and `LookupWebFingerOptions` interfaces
+   - Enables control over maximum request duration for outgoing API calls
+   - Allows graceful cancellation of in-flight requests
+   [[#51] [#187] by PDJ]
🧰 Tools
🪛 Markdownlint (0.35.0)

75-75: Expected: 0; Actual: 1
Unordered list indentation

(MD007, ul-indent)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 73399be and 0bbb884.

📒 Files selected for processing (5)
  • CHANGES.md (3 hunks)
  • src/runtime/docloader.ts (3 hunks)
  • src/vocab/actor.ts (4 hunks)
  • src/vocab/lookup.ts (2 hunks)
  • src/webfinger/lookup.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/runtime/docloader.ts
  • src/vocab/actor.ts
  • src/vocab/lookup.ts
  • src/webfinger/lookup.ts
🧰 Additional context used
🪛 Markdownlint (0.35.0)
CHANGES.md

75-75: Expected: 0; Actual: 1
Unordered list indentation

(MD007, ul-indent)

@@ -72,6 +72,9 @@ To be released.
- Let the `fedify lookup` command take multiple arguments.
[[#173], [#186] by PGD]

- Added options related to `AbortController`.
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Fix list indentation.

The indentation of the bullet point doesn't match the style of other entries in the changelog.

Apply this diff to fix the indentation:

- -  Added options related to `AbortController`.
+ - Added options related to `AbortController`.
📝 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
- Added options related to `AbortController`.
- Added options related to `AbortController`.
🧰 Tools
🪛 Markdownlint (0.35.0)

75-75: Expected: 0; Actual: 1
Unordered list indentation

(MD007, ul-indent)

Copy link

@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: 0

🧹 Outside diff range and nitpick comments (1)
CHANGES.md (1)

75-77: Enhance the changelog entry with more details.

The current changelog entry for AbortController could be more informative. Consider expanding it to include:

  1. List the specific interfaces that were updated (e.g., GetDocumentLoaderOptions, GetActorHandleOptions, LookupObjectOptions, LookupWebFingerOptions)
  2. Explain that this addition enables request timeout control
  3. Add a brief example of how to use the new signal parameter

Apply this diff to enhance the changelog entry:

- -  Added options related to `AbortController`.
-    [[#51] [#187] by PDJ]
+ -  Added support for request cancellation using `AbortController`:
+    [[#51] [#187] by PDJ]
+
+     -  Added optional `signal` parameter to the following interfaces for request timeout control:
+         -  `GetDocumentLoaderOptions`
+         -  `GetActorHandleOptions`
+         -  `LookupObjectOptions`
+         -  `LookupWebFingerOptions`
+
+     -  Example usage:
+        ```typescript
+        const controller = new AbortController();
+        const timeout = setTimeout(() => controller.abort(), 5000); // 5 second timeout
+        const options = { signal: controller.signal };
+        ```
🧰 Tools
🪛 Markdownlint (0.35.0)

75-75: Expected: 0; Actual: 1
Unordered list indentation

(MD007, ul-indent)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 03d3c3d and b1c1d34.

📒 Files selected for processing (1)
  • CHANGES.md (1 hunks)
🧰 Additional context used
🪛 Markdownlint (0.35.0)
CHANGES.md

75-75: Expected: 0; Actual: 1
Unordered list indentation

(MD007, ul-indent)

Copy link

@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: 11

🧹 Outside diff range and nitpick comments (27)
src/vocab/type.ts (1)

22-87: Consolidate function documentation.

The documentation is duplicated across all overload signatures. In TypeScript, it's recommended to document only the implementation signature and remove documentation from overload signatures.

 export function getTypeId(object: Object | Link): URL;
-
-/**
- * Returns the type URI of the given object.
- *
- * @example
- * ``` typescript
- * import { getTypeId, Person } from "@fedify/fedify";
- *
- * const obj = new Person({});
- * console.log(getTypeId(obj));
- * // => new URL("https://www.w3.org/ns/activitystreams#Person")
- * ```
- *
- * @param object The Activity Vocabulary object.
- * @returns The type URI of the object, e.g.,
- *          `new URL("https://www.w3.org/ns/activitystreams#Person")`.
- *          If the given `object` is `null` or `undefined`, returns `null` or
- *          `undefined`, respectively.
- * @since 1.3.0
- */
 export function getTypeId(object: Object | Link | undefined): URL | undefined;
-
-/**
- * Returns the type URI of the given object.
- *
- * @example
- * ``` typescript
- * import { getTypeId, Person } from "@fedify/fedify";
- *
- * const obj = new Person({});
- * console.log(getTypeId(obj));
- * // => new URL("https://www.w3.org/ns/activitystreams#Person")
- * ```
- *
- * @param object The Activity Vocabulary object.
- * @returns The type URI of the object, e.g.,
- *          `new URL("https://www.w3.org/ns/activitystreams#Person")`.
- *          If the given `object` is `null` or `undefined`, returns `null` or
- *          `undefined`, respectively.
- * @since 1.3.0
- */
 export function getTypeId(object: Object | Link | null): URL | null;
src/webfinger/lookup.ts (2)

30-40: LGTM: Interface changes are well documented and typed correctly

The new properties are well documented and follow TypeScript best practices. The signal type is correctly specified as AbortSignal.

Consider implementing a timeout wrapper utility that creates an AbortSignal with a timeout, making it easier for consumers to implement request timeouts. Example usage could be:

const options: LookupWebFingerOptions = {
  signal: createTimeoutSignal(5000), // 5 second timeout
};

Line range hint 90-116: Add safety measures for redirect handling

While the signal implementation is correct, the redirect handling in the while loop could potentially continue indefinitely. Consider adding a maximum redirect limit.

Here's a suggested implementation:

+ const MAX_REDIRECTS = 10;
+ let redirectCount = 0;
  while (true) {
+   if (redirectCount >= MAX_REDIRECTS) {
+     logger.debug(
+       "Maximum redirect limit reached",
+       { url: url.href, limit: MAX_REDIRECTS }
+     );
+     return null;
+   }
    logger.debug("Fetching WebFinger resource descriptor from {url}...", 
      { url: url.href }
    );
    // ... existing code ...
    if (response.status >= 300 && response.status < 400 &&
        response.headers.has("Location")) {
+     redirectCount++;
      url = new URL(
        response.headers.get("Location")!,
        response.url == null || response.url === "" ? url : response.url
      );
      continue;
    }
src/vocab/actor.ts (1)

138-166: Consider enhancing error attributes in span

The OpenTelemetry implementation is solid, but consider adding more error details to the span attributes when an error occurs.

 } catch (error) {
   span.setStatus({
     code: SpanStatusCode.ERROR,
     message: String(error),
   });
+  span.setAttribute('error.type', error.name);
+  span.setAttribute('error.message', error.message);
   throw error;
 }
docs/manual/opentelemetry.md (6)

24-28: Improve clarity in setup instructions

Consider revising the redundant phrasing to be more concise:

-To trace your Fedify application with OpenTelemetry, you need to set up the
-OpenTelemetry SDK.  First of all, you need to install the OpenTelemetry SDK and
-the tracer exporter you want to use.  For example, if you want to use the trace
-exporter for OTLP (http/protobuf), you should install the following packages:
+To trace your Fedify application with OpenTelemetry, set up the OpenTelemetry SDK
+and install your preferred tracer exporter. For the OTLP (http/protobuf) trace
+exporter, install these packages:
🧰 Tools
🪛 LanguageTool

[style] ~25-~25: Often, this adverbial phrase is redundant. Consider using an alternative.
Context: ... need to set up the OpenTelemetry SDK. First of all, you need to install the OpenTelemetry ...

(FIRST_OF_ALL)


[style] ~26-~26: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...r you want to use. For example, if you want to use the trace exporter for OTLP (http/p...

(REP_WANT_TO_VB)


55-58: Add context for OTLP configuration

Consider adding a brief note explaining the default OTLP endpoint (http://localhost:4317) and when to modify the headers.


78-81: Enhance Sentry integration example

Consider adding a brief explanation of why one might choose Sentry as a trace exporter (e.g., error tracking, performance monitoring, etc.) to help users make an informed decision.


120-133: Enhance span documentation with examples

Consider adding example scenarios or sample traces for each span type to help users understand when these spans are created and how they relate to each other in a typical request flow.


158-158: Fix grammatical error in attribute description

Correct the agreement error in the description:

-The URI(s) of the original object to which the object reply.
+The URI(s) of the original object to which the object replies.
🧰 Tools
🪛 LanguageTool

[grammar] ~158-~158: Possible agreement error - use third-person verb forms for singular and mass nouns.
Context: ...the original object to which the object reply. | `["https...

(MASS_AGREEMENT)


147-173: Consider grouping related attributes

The attributes table is comprehensive but could be more organized. Consider grouping related attributes (e.g., all signature-related attributes together) and adding subsection headers in the table for better readability.

🧰 Tools
🪛 LanguageTool

[grammar] ~158-~158: Possible agreement error - use third-person verb forms for singular and mass nouns.
Context: ...the original object to which the object reply. | `["https...

(MASS_AGREEMENT)

src/runtime/docloader.ts (2)

Line range hint 292-301: Critical: Signal not propagated to redirected requests

The signal is correctly passed to the initial fetch call, but when handling redirects, the signal is not propagated to the recursive load call. This could result in redirected requests continuing even after the original request is aborted.

Apply this fix to ensure proper signal propagation:

    if (
      response.status >= 300 && response.status < 400 &&
      response.headers.has("Location")
    ) {
-     return load(response.headers.get("Location")!);
+     return load(response.headers.get("Location")!, signal);
    }

Additionally, update the load function signature to accept the signal:

-  async function load(url: string): Promise<RemoteDocument> {
+  async function load(url: string, signal?: AbortSignal | null): Promise<RemoteDocument> {

Line range hint 374-397: Critical: Add signal support to authenticated document loader

The getAuthenticatedDocumentLoader function lacks signal support, which means authenticated requests cannot be cancelled. This should be implemented for consistency with getDocumentLoader.

Apply these changes to add signal support:

 export interface GetAuthenticatedDocumentLoaderOptions {
   allowPrivateAddress?: boolean;
   userAgent?: GetUserAgentOptions | string;
+  signal?: AbortSignal | null;
 }

 export function getAuthenticatedDocumentLoader(
   identity: { keyId: URL; privateKey: CryptoKey },
-  { allowPrivateAddress, userAgent }: GetAuthenticatedDocumentLoaderOptions = {},
+  { allowPrivateAddress, userAgent, signal }: GetAuthenticatedDocumentLoaderOptions = {},
 ): DocumentLoader {
   validateCryptoKey(identity.privateKey);
-  async function load(url: string): Promise<RemoteDocument> {
+  async function load(url: string, signal?: AbortSignal | null): Promise<RemoteDocument> {
     // ... existing code ...
     const response = await fetch(request, {
       redirect: "manual",
+      signal,
     });
     if (
       response.status >= 300 && response.status < 400 &&
       response.headers.has("Location")
     ) {
-      return load(response.headers.get("Location")!);
+      return load(response.headers.get("Location")!, signal);
     }
     // ... rest of the code ...
   }
   return load;
 }
docs/manual/federation.md (3)

343-343: Maintain consistent heading style

For consistency with other sections in the document, use setext-style headings (with === or --- underlines) instead of ATX-style headings (with #).

-### `tracerProvider`
+`tracerProvider`
+----------------
🧰 Tools
🪛 Markdownlint (0.35.0)

343-343: Expected: setext; Actual: atx
Heading style

(MD003, heading-style)


343-354: Add code example for custom tracer provider configuration

Consider adding a code example to demonstrate how to configure a custom tracer provider, similar to other sections in the documentation.

Add this example after line 354:

For example:

~~~~ typescript
import { createFederation } from "@fedify/fedify";
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";

const tracerProvider = new NodeTracerProvider();
tracerProvider.register();

const federation = createFederation({
  // ... other options
  tracerProvider,
});
~~~~
🧰 Tools
🪛 Markdownlint (0.35.0)

343-343: Expected: setext; Actual: atx
Heading style

(MD003, heading-style)


Line range hint 1-1: Document the new AbortController feature

The PR implements AbortController for API requests, but the documentation doesn't cover this important feature. Consider adding a new section that explains:

  1. How to use AbortController with the Federation API
  2. Which methods support request cancellation
  3. Example usage with timeouts

Here's a suggested addition to the documentation:

Request Cancellation
------------------

*This API is available since Fedify 1.3.0.*

The Federation API supports request cancellation using `AbortController` for various operations that make outgoing HTTP requests. This allows you to set timeouts or manually cancel long-running requests.

The following interfaces support request cancellation through their `signal` parameter:
- `GetDocumentLoaderOptions`
- `GetActorHandleOptions`
- `LookupObjectOptions`
- `LookupWebFingerOptions`

Example usage with a timeout:

~~~~ typescript
import { createFederation } from "@fedify/fedify";

const federation = createFederation({
  // ... other options
});

// Create an AbortController with a 5-second timeout
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5000);

try {
  const actor = await federation.getActorHandle("@[email protected]", {
    signal: controller.signal,
  });
  // Process the actor...
} catch (error) {
  if (error.name === "AbortError") {
    console.log("Request timed out");
  }
} finally {
  clearTimeout(timeout);
}
~~~~
🧰 Tools
🪛 Markdownlint (0.35.0)

343-343: Expected: setext; Actual: atx
Heading style

(MD003, heading-style)

src/webfinger/handler.ts (7)

62-64: Handle the case where both options.tracer and options.span are undefined

If both tracer and span are undefined in options, the function may not properly trace the request. Ensure that the absence of a tracer doesn't lead to missing spans if a span is provided.

Consider updating the condition to check for options.span as well:

 if (options.tracer == null && options.span == null) {
   return await handleWebFingerInternal(request, options);
 }

88-95: Clarify the dependency on actorDispatcher in handleWebFingerInternal

In handleWebFingerInternal, there are two checks for actorDispatcher being null. The first check occurs at line 90, and the second at line 116. This could be streamlined to avoid redundant code.

Refactor the code to remove the redundant check:

 async function handleWebFingerInternal<TContextData>(
   request: Request,
   {
     context,
     actorDispatcher,
     actorHandleMapper,
     onNotFound,
     span,
   }: WebFingerHandlerParameters<TContextData>,
 ): Promise<Response> {
-  if (actorDispatcher == null) return await onNotFound(request);
   const resource = context.url.searchParams.get("resource");
   if (resource == null) {
     return new Response("Missing resource parameter.", { status: 400 });
   }
   span?.setAttribute("webfinger.resource", resource);
   // Remove the second check for actorDispatcher

113-116: Ensure resourceUrl.protocol handles all cases correctly

When setting the webfinger.resource.scheme attribute, the resourceUrl.protocol.replace(/:$/, "") operation assumes that the protocol ends with a colon. While this is standard, ensure that all possible cases are handled, especially with non-standard URLs.

Consider using the resourceUrl.scheme property if available, or adding a safety check:

 span?.setAttribute(
   "webfinger.resource.scheme",
-  resourceUrl.protocol.replace(/:$/, ""),
+  resourceUrl.protocol.endsWith(':') ? resourceUrl.protocol.slice(0, -1) : resourceUrl.protocol,
 );

2-2: Organize imports to comply with project conventions

The import statement for Span and Tracer is added separately. For better readability and maintenance, group related imports together.

Organize the import statements:

-import { getLogger } from "@logtape/logtape";
+import { getLogger } from "@logtape/logtape";
 import type { Span, Tracer } from "@opentelemetry/api";
 import { SpanKind, SpanStatusCode } from "@opentelemetry/api";

40-48: Document the new tracer and span parameters in the interface

The WebFingerHandlerParameters interface now includes tracer and span, but their purpose isn't fully explained in the comments. Providing clear documentation will help other developers understand how to use these parameters.

Enhance the documentation for tracer and span:

 /**
  * The OpenTelemetry tracer used for tracing the request. Optional.
  */
 tracer?: Tracer;

 /**
  * The OpenTelemetry span representing the current operation. Optional.
  */
 span?: Span;

62-64: Consider defaulting to a no-op tracer to simplify code paths

By defaulting tracer to a no-op implementation when it's not provided, you can simplify the conditional logic and ensure that tracing calls are safe to invoke without null checks.

Implement a no-op tracer or use OpenTelemetry's default:

import { trace, context } from "@opentelemetry/api";

const tracer = options.tracer ?? trace.getTracer("default");

return await tracer.startActiveSpan(
  // rest of the code
);

70-74: Handle non-OK responses appropriately in tracing

When setting the span status based on response.ok, consider capturing more detailed information for debugging purposes, such as the response status code or error messages.

Enhance the span status setting:

 span.setStatus({
   code: response.ok ? SpanStatusCode.UNSET : SpanStatusCode.ERROR,
+  message: response.ok ? undefined : `HTTP ${response.status} ${response.statusText}`,
 });
src/sig/proof.ts (2)

13-13: Avoid Shadowing the Global Object

Importing type Object shadows the global Object type in JavaScript, which can lead to confusion and potential bugs. Consider renaming the imported type to prevent this shadowing.

For example, you could rename Object to APObject or another descriptive name that doesn't conflict with global types.

🧰 Tools
🪛 Biome (1.9.4)

[error] 13-13: Do not shadow the global "Object" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)


148-150: Consider Moving Attribute Setting Inside span.isRecording() Check

To ensure consistency and avoid unnecessary operations when the span is not recording, consider moving the setting of the activitypub.object.id attribute inside the if (span.isRecording()) block.

Apply this diff to adjust the code:

-        if (object.id != null) {
-          span.setAttribute("activitypub.object.id", object.id.href);
-        }
+        if (span.isRecording()) {
+          if (object.id != null) {
+            span.setAttribute("activitypub.object.id", object.id.href);
+          }
+        }
src/sig/ld.ts (2)

144-177: Consider Abstracting Tracing Logic to Reduce Duplication

The tracing implementation in signJsonLd and verifyJsonLd functions shares similar patterns. To improve maintainability and adhere to the DRY (Don't Repeat Yourself) principle, consider abstracting the tracing setup into a common helper function or utility. This refactor will reduce code duplication and simplify future updates.

Also applies to: 375-448


153-158: Pass Only Relevant Options to createSignature

When calling createSignature, it's best practice to pass only the necessary options to prevent unintended side effects. Extract the relevant properties instead of passing the entire options object.

Apply this diff:

const signature = await createSignature(
  jsonLd,
  privateKey,
  keyId,
- options,
+ {
+   contextLoader: options.contextLoader,
+   created: options.created,
+ },
);
src/federation/middleware.ts (1)

1965-1966: Consider Encapsulating Tracing Parameters to Simplify Method Signatures

The addition of tracer and tracerProvider parameters to multiple method signatures increases the number of parameters, which may affect readability and maintainability. Consider encapsulating these tracing-related parameters into a single options object or utilizing a context pattern to reduce parameter clutter and enhance code clarity.

Also applies to: 1984-1984, 2082-2082, 2651-2651, 2900-2900

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between b1c1d34 and bf72326.

⛔ Files ignored due to path filters (1)
  • docs/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (19)
  • CHANGES.md (2 hunks)
  • docs/.vitepress/config.mts (1 hunks)
  • docs/manual/federation.md (1 hunks)
  • docs/manual/opentelemetry.md (1 hunks)
  • docs/package.json (2 hunks)
  • src/federation/handler.ts (6 hunks)
  • src/federation/middleware.ts (12 hunks)
  • src/federation/send.ts (4 hunks)
  • src/runtime/docloader.ts (3 hunks)
  • src/sig/http.ts (8 hunks)
  • src/sig/ld.ts (5 hunks)
  • src/sig/proof.ts (5 hunks)
  • src/vocab/actor.ts (5 hunks)
  • src/vocab/lookup.ts (4 hunks)
  • src/vocab/mod.ts (2 hunks)
  • src/vocab/type.test.ts (1 hunks)
  • src/vocab/type.ts (1 hunks)
  • src/webfinger/handler.ts (4 hunks)
  • src/webfinger/lookup.ts (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/vocab/lookup.ts
🧰 Additional context used
🪛 Markdownlint (0.35.0)
CHANGES.md

60-60: Expected: 0; Actual: 1
Unordered list indentation

(MD007, ul-indent)


62-62: Expected: 0; Actual: 1
Unordered list indentation

(MD007, ul-indent)


64-64: Expected: 2; Actual: 5
Unordered list indentation

(MD007, ul-indent)


65-65: Expected: 2; Actual: 5
Unordered list indentation

(MD007, ul-indent)


66-66: Expected: 2; Actual: 5
Unordered list indentation

(MD007, ul-indent)


67-67: Expected: 2; Actual: 5
Unordered list indentation

(MD007, ul-indent)


68-68: Expected: 2; Actual: 5
Unordered list indentation

(MD007, ul-indent)


69-69: Expected: 2; Actual: 5
Unordered list indentation

(MD007, ul-indent)


70-70: Expected: 2; Actual: 5
Unordered list indentation

(MD007, ul-indent)


71-71: Expected: 2; Actual: 5
Unordered list indentation

(MD007, ul-indent)


72-72: Expected: 2; Actual: 5
Unordered list indentation

(MD007, ul-indent)


73-73: Expected: 2; Actual: 5
Unordered list indentation

(MD007, ul-indent)


74-74: Expected: 2; Actual: 5
Unordered list indentation

(MD007, ul-indent)


75-75: Expected: 2; Actual: 5
Unordered list indentation

(MD007, ul-indent)


88-88: Expected: 0; Actual: 1
Unordered list indentation

(MD007, ul-indent)

docs/manual/federation.md

343-343: Expected: setext; Actual: atx
Heading style

(MD003, heading-style)

🪛 LanguageTool
docs/manual/opentelemetry.md

[style] ~25-~25: Often, this adverbial phrase is redundant. Consider using an alternative.
Context: ... need to set up the OpenTelemetry SDK. First of all, you need to install the OpenTelemetry ...

(FIRST_OF_ALL)


[style] ~26-~26: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...r you want to use. For example, if you want to use the trace exporter for OTLP (http/p...

(REP_WANT_TO_VB)


[style] ~65-~65: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...ode which sets up the OpenTelemetry SDK needs to be executed before > the Fedify server ...

(REP_NEED_TO_VB)


[grammar] ~158-~158: Possible agreement error - use third-person verb forms for singular and mass nouns.
Context: ...the original object to which the object reply. | `["https...

(MASS_AGREEMENT)

🪛 Biome (1.9.4)
src/sig/proof.ts

[error] 13-13: Do not shadow the global "Object" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

src/vocab/type.ts

[error] 1-1: Do not shadow the global "Object" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

🔇 Additional comments (42)
src/vocab/type.test.ts (3)

1-4: LGTM! Import statements are well-organized.

The imports are properly structured, following the convention of external dependencies first followed by internal modules. All imports are utilized in the test file.


6-20: Consider restructuring tests for better organization and coverage.

While the current tests cover the basic scenarios, consider the following improvements:

  1. Split scenarios into separate test cases for better clarity and maintenance:
-test("getTypeId()", () => {
+test("getTypeId() with valid Person object returns correct type URL", () => {
   const obj = new Person({});
   assertEquals(
     getTypeId(obj),
     new URL("https://www.w3.org/ns/activitystreams#Person"),
   );
+});
+
+test("getTypeId() with null values returns null", () => {
   const obj2: Person | null = null;
   assertEquals(getTypeId(obj2), null);
+  const obj4: Person | null | undefined = null;
+  assertEquals(getTypeId(obj4), null);
+});
+
+test("getTypeId() with undefined values returns undefined", () => {
   const obj3: Person | undefined = undefined;
   assertEquals(getTypeId(obj3), undefined);
-  const obj4: Person | null | undefined = null;
-  assertEquals(getTypeId(obj4), null);
   const obj5: Person | null | undefined = undefined;
   assertEquals(getTypeId(obj5), undefined);
 });
  1. Add test cases for invalid scenarios:

6-20: Verify test coverage for all scenarios.

Let's verify if all possible scenarios from the implementation are covered in tests:

✅ Verification successful

Test coverage is complete and accurate

The current test coverage is comprehensive and matches all code paths in the implementation:

  • Tests null and undefined cases which align with the if (object == null) check
  • Tests the main case with a Person object, verifying the correct type ID URL is returned
  • Covers all function overloads (Object|Link, Object|Link|null, Object|Link|undefined, Object|Link|null|undefined)
  • The implementation is simple with only two paths: null check and type ID retrieval
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Look for other test files that might contain getTypeId tests
fd -e test.ts -x rg -l "getTypeId"

# Check the implementation for all possible code paths
ast-grep --pattern 'export function getTypeId($_) {
  $$$
}'

# Look for usage patterns to identify untested scenarios
rg "getTypeId\(" --type ts

Length of output: 1427


Script:

#!/bin/bash
# Let's check the implementation details of getTypeId to verify test coverage
ast-grep --pattern 'export function getTypeId($_) {
  $$$
}' src/vocab/type.ts

# Let's also check the type definitions to understand all possible input types
rg "type Object|type Link" --type ts -A 3

Length of output: 1632


Script:

#!/bin/bash
# Let's get the actual implementation of getTypeId function to see all code paths
rg "export function getTypeId" -A 10 src/vocab/type.ts

# Also check if there are any specific type checks or conditions in the implementation
rg "instanceof|typeof" src/vocab/type.ts

Length of output: 1301

docs/package.json (2)

6-6: Verify compatibility with the new fedify version

The significant version jump (dev.508 -> dev.557) could introduce breaking changes. Ensure that the new version is compatible with the AbortController implementation.

Let's check the changelog between these versions:

✅ Verification successful

Based on the search results, I can see that AbortController and signal-related functionality is a well-established feature in the codebase, with proper implementations across various modules. Let me search for any breaking changes between these versions:


Based on the search results, I can now provide a final response:

The new fedify version appears compatible with AbortController implementation

The changes between versions primarily involve message queue configuration improvements, with no breaking changes to the AbortController functionality. The AbortController/signal implementation is well-established across the codebase and the version update (1.3.0-dev.557) maintains backward compatibility for this feature.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Look for any breaking changes or AbortController-related updates
rg -A 5 "BREAKING CHANGE|AbortController|signal" $(fd -e md -e txt changelog CHANGELOG)

Length of output: 13369


Script:

#!/bin/bash
# Look for version history or changelog entries
rg -i "version 1.3.0|1\.3\.0-dev" -A 10

Length of output: 3164


39-41: Clarify the scope of telemetry additions

The addition of OpenTelemetry and Sentry dependencies appears to extend beyond the PR's stated objective of implementing AbortController for API timeouts. While these are valuable monitoring tools, their inclusion should be documented and justified.

Let's check if these changes are documented:

✅ Verification successful

OpenTelemetry and Sentry additions are well-documented and part of a planned feature

The telemetry additions are properly documented and justified:

  • CHANGES.md explicitly mentions OpenTelemetry support as a new feature (More built-in OpenTelemetry instrumentations #170)
  • Comprehensive documentation exists in docs/manual/opentelemetry.md explaining the setup and usage
  • The implementation is thorough with traces across multiple components (federation, webfinger, vocab, etc.)
  • Sentry integration is documented as an optional trace exporter in the OpenTelemetry documentation

These changes are part of a planned observability feature rather than scope creep, and the documentation properly covers their integration and usage.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check changelog and documentation for telemetry additions
fd -e md -e txt changelog CHANGELOG

Length of output: 134


Script:

#!/bin/bash
# Let's try different approaches to find documentation about telemetry
# 1. Look for any markdown files that might contain documentation
fd -e md

# 2. Search for telemetry-related terms in all text files
rg -i "telemetry|opentelemetry|sentry" -g "!{package-lock.json,yarn.lock,pnpm-lock.yaml}"

# 3. Check if there are any configuration files for these services
fd -g "*telemetry*"
fd -g "*sentry*"

Length of output: 7928

src/vocab/mod.ts (2)

10-10: LGTM! Documentation fix looks good.

The spelling correction improves the documentation quality.


55-55: Verify the relevance of this change to the PR objectives.

The addition of type.ts export seems unrelated to the PR's stated objective of implementing AbortController for API requests. Could you clarify how this change supports the implementation of request timeouts?

Let's check if the exported module contains any abort-related functionality:

src/vocab/type.ts (2)

3-21: Documentation looks great!

The documentation is thorough, includes clear examples, and properly documents the behavior for edge cases.


1-99: Verify if this file is within PR scope.

This file introduces a new getTypeId function, but the PR's objective is to implement AbortController for API requests. Please clarify if this change is intentionally part of this PR or should be moved to a separate PR.

🧰 Tools
🪛 Biome (1.9.4)

[error] 1-1: Do not shadow the global "Object" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

src/webfinger/lookup.ts (2)

2-7: LGTM: OpenTelemetry imports are well structured

The imports are correctly specified and follow TypeScript best practices.


52-88: LGTM: Tracing implementation follows best practices

The tracing implementation is well structured with proper error handling and span management.

Let's verify that all error paths are properly traced:

✅ Verification successful

Let me verify the success paths as well to ensure complete tracing coverage.


Tracing implementation is complete and correct

The error and success paths are properly handled in the tracing implementation:

  • Error paths consistently set SpanStatusCode.ERROR with error messages
  • Success paths appropriately set status based on the result (null -> ERROR, valid result -> OK)
  • The span is always ended in the finally block
  • The implementation aligns with similar patterns used throughout the codebase
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for all error handling paths to ensure they set span status
ast-grep --pattern 'span.setStatus({
  code: SpanStatusCode.$_,
  $$$
})'

Length of output: 2207


Script:

#!/bin/bash
# Search for success path span status settings
ast-grep --pattern 'span.setStatus({
  code: SpanStatusCode.OK,
  $$$
})'

# Also search for any direct span.setStatus calls to ensure we haven't missed any patterns
rg "span\.setStatus" -A 2

Length of output: 2965

src/federation/send.ts (2)

2-2: LGTM: Import statement is correctly added

The TracerProvider import from OpenTelemetry is properly added to support the new tracing functionality.


1-1: Verify AbortController implementation across the codebase

Let's ensure that AbortController implementation is consistent across all API calls.

docs/.vitepress/config.mts (1)

73-73: LGTM! Verify the OpenTelemetry documentation file exists.

The addition of OpenTelemetry documentation to the manual section is well-placed and follows the existing structure.

Let's verify that the referenced documentation file exists:

✅ Verification successful

Documentation link verified and exists

The OpenTelemetry documentation file exists at the expected path docs/manual/opentelemetry.md, confirming that the navigation link in the VitePress config is valid.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if the OpenTelemetry documentation file exists
# Expected: File should exist at docs/manual/opentelemetry.md

if [ -f "docs/manual/opentelemetry.md" ]; then
    echo "✓ OpenTelemetry documentation file exists"
else
    echo "✗ Missing documentation file: docs/manual/opentelemetry.md"
fi

Length of output: 134

src/vocab/actor.ts (4)

1-1: LGTM: OpenTelemetry import added correctly

The addition of OpenTelemetry imports is well-structured and necessary for the new tracing functionality.


97-108: LGTM: Well-documented interface extensions

The additions of signal and tracerProvider properties are well-documented and properly typed, providing clear functionality for request cancellation and tracing.


174-187: LGTM: Robust error handling for aborted requests

The implementation properly handles request abortion with clear error messages and correct error propagation.


231-238: LGTM: Consistent parameter propagation

The function correctly propagates both the tracer provider and abort signal to the underlying lookupWebFinger call.

src/runtime/docloader.ts (3)

238-242: LGTM! Signal parameter correctly implemented.

The optional signal parameter is properly typed as AbortSignal | null which matches the fetch API's expectations.


263-264: LGTM! Function signature properly updated.

The signal parameter is correctly destructured from the options object, maintaining consistency with the interface.


238-242: Verify the impact of AbortSignal changes

Let's verify the usage of these document loaders across the codebase to ensure consistent implementation of request cancellation.

✅ Verification successful

Let me try a more focused search to verify the impact of AbortSignal changes.


AbortSignal implementation is consistent across the codebase

The AbortSignal changes in docloader.ts align with the existing patterns in the codebase:

  • The signal?: AbortSignal | null type signature is consistently used across multiple modules including webfinger/lookup.ts, vocab/actor.ts, and federation/mq.ts
  • AbortController usage is properly implemented in test files and documented examples
  • The changes maintain consistency with the existing request cancellation patterns
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find all usages of document loaders to verify signal implementation

# Check for direct usage of the document loaders
echo "Checking direct usage of document loaders..."
rg -A 5 "getDocumentLoader|getAuthenticatedDocumentLoader"

# Check for existing timeout or cancellation patterns
echo "Checking existing timeout patterns..."
rg -A 5 "setTimeout.*fetch|new Promise.*timeout|AbortController"

# Check for any TODOs related to request timeouts
echo "Checking timeout-related TODOs..."
rg -i "todo.*timeout"

Length of output: 159389


Script:

#!/bin/bash
# Search for AbortSignal usage and related patterns
echo "Checking AbortSignal usage..."
rg -A 3 "AbortSignal|signal\?:"

echo "Checking fetch with signal..."
rg -A 3 "fetch.*signal"

echo "Checking for request cancellation patterns..."
rg -A 3 "abort\(\)|new AbortController"

Length of output: 4857

CHANGES.md (2)

60-75: Skip: List indentation issues already addressed.

The indentation issues have already been identified in past review comments.

Also applies to: 88-88

🧰 Tools
🪛 Markdownlint (0.35.0)

60-60: Expected: 0; Actual: 1
Unordered list indentation

(MD007, ul-indent)


62-62: Expected: 0; Actual: 1
Unordered list indentation

(MD007, ul-indent)


64-64: Expected: 2; Actual: 5
Unordered list indentation

(MD007, ul-indent)


65-65: Expected: 2; Actual: 5
Unordered list indentation

(MD007, ul-indent)


66-66: Expected: 2; Actual: 5
Unordered list indentation

(MD007, ul-indent)


67-67: Expected: 2; Actual: 5
Unordered list indentation

(MD007, ul-indent)


68-68: Expected: 2; Actual: 5
Unordered list indentation

(MD007, ul-indent)


69-69: Expected: 2; Actual: 5
Unordered list indentation

(MD007, ul-indent)


70-70: Expected: 2; Actual: 5
Unordered list indentation

(MD007, ul-indent)


71-71: Expected: 2; Actual: 5
Unordered list indentation

(MD007, ul-indent)


72-72: Expected: 2; Actual: 5
Unordered list indentation

(MD007, ul-indent)


73-73: Expected: 2; Actual: 5
Unordered list indentation

(MD007, ul-indent)


74-74: Expected: 2; Actual: 5
Unordered list indentation

(MD007, ul-indent)


75-75: Expected: 2; Actual: 5
Unordered list indentation

(MD007, ul-indent)


94-97: LGTM: Link references are properly formatted.

The link references follow the established format and are correctly added.

src/sig/proof.ts (2)

139-184: Tracing Implementation in signObject Function is Correct

The integration of OpenTelemetry tracing into the signObject function is well-implemented. The code correctly initializes the tracer, starts an active span, sets relevant attributes, handles errors appropriately, and ensures the span is ended in the finally block.


230-271: Tracing Implementation in verifyProof Function is Correct

The addition of OpenTelemetry tracing in the verifyProof function is properly handled. The code accurately starts an active span, captures relevant attributes, manages error states with appropriate span status updates, and ensures the span is ended correctly.

src/sig/http.ts (2)

Line range hint 85-109: Ensure consistent 'isRecording()' checks before setting span attributes

In signRequestInternal, attributes are set within an if (span.isRecording()) check. This is good practice to avoid unnecessary operations when the span is not recording. Ensure this pattern is consistently applied in other functions as well.


16-16: Verify compatibility of 'with { type: "json" }' syntax in import statement

The import statement import metadata from "../deno.json" with { type: "json" }; uses syntax that may not be compatible with all TypeScript configurations or environments. Ensure that this syntax is supported in your project's build and runtime environment.

Run the following script to check for TypeScript compiler support:

✅ Verification successful

Import syntax is compatible with project configuration

The project's TypeScript configuration explicitly supports this syntax through:

  • "target": "ESNext" and "module": "ESNext" which enable latest JavaScript features
  • "moduleResolution": "bundler" which supports import assertions
  • "resolveJsonModule": true" which enables JSON module imports

The syntax import ... with { type: "json" } is part of the ECMAScript proposal for Import Assertions, which is supported in modern TypeScript when targeting ESNext, exactly as configured in this project.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if the TypeScript compiler supports JSON imports with 'with { type: "json" }' syntax.

# Test: Attempt to compile a test file with the import statement.
echo 'import metadata from "./test.json" with { type: "json" };' > test.ts
echo '{}' > test.json
tsc test.ts

# Expected result: No compilation errors.

Length of output: 186


Script:

#!/bin/bash
# Let's check the TypeScript configuration and package.json for compiler options and dependencies
fd "tsconfig.json|package.json" --type f --exec cat {}

Length of output: 5314

src/sig/ld.ts (1)

117-122: Consistent Documentation for tracerProvider Option

The addition of the optional tracerProvider property to both SignJsonLdOptions and VerifyJsonLdOptions interfaces is well-documented and includes the @since 1.3.0 tag. Ensure that this version aligns with the actual release version to maintain accurate documentation.

Also applies to: 354-359

src/federation/handler.ts (3)

363-363: Ensure optional 'tracerProvider' is handled correctly in 'InboxHandlerParameters'

Since tracerProvider is optional in InboxHandlerParameters, please ensure that any functions consuming this parameter handle cases when it is undefined to prevent potential runtime errors.


381-381: Confirm 'tracerProvider' is appropriately passed to 'handleInbox'

Verify that the handleInbox function signature includes the tracerProvider parameter and that it correctly manages scenarios where tracerProvider may be undefined.


463-463: Validate 'tracerProvider' usage in verification functions

Ensure that the verifyJsonLd, verifyObject, and verifyRequest functions accept the tracerProvider parameter and handle it properly, especially when it is undefined, to avoid any unexpected errors.

Also applies to: 480-480, 521-521

src/federation/middleware.ts (11)

7-7: Valid Import of Tracing Types

The types Tracer and TracerProvider are correctly imported from the OpenTelemetry API to support tracing functionality.


538-538: Passing TracerProvider to sendActivity

The tracerProvider is appropriately passed to the sendActivity function, enabling tracing of activity sending operations.


1779-1779: Integrating TracerProvider into signObject Function

The tracerProvider is correctly passed to the signObject function, enhancing tracing during the object signing process.


1805-1805: Adding TracerProvider to signJsonLd Function

The inclusion of tracerProvider in the signJsonLd function call enables tracing support during JSON-LD signing.


1914-1914: Propagating Span and Tracer to Internal #fetch Method

The span and tracer parameters are correctly passed to the #fetch method, ensuring that tracing spans are properly propagated during the fetch operation.


1965-1966: Updating #fetch Method Signature to Include Tracer

The #fetch method signature is appropriately updated to include the tracer parameter, allowing tracing within this internal method.


1984-1984: Passing Tracer to handleWebFinger Function

The tracer is correctly passed to the handleWebFinger function, integrating tracing into the WebFinger handling process.


2082-2082: Including TracerProvider in handleInbox Function Call

The tracerProvider is properly included in the handleInbox function call, enabling tracing during inbox processing.


2651-2651: Passing TracerProvider to lookupObject Function

The tracerProvider is correctly passed to the lookupObject function within the options, enhancing tracing capabilities during object lookup.


2900-2900: Enhancing verifyRequest with TracerProvider

The tracerProvider is passed to the verifyRequest function, enabling tracing during request verification.


3082-3082: Including TracerProvider in sendActivity Call within forwardActivity

The tracerProvider is correctly included in the sendActivity call inside the forwardActivity method, allowing tracing of forwarded activities.

@@ -0,0 +1,99 @@
import type { Link, Object } from "./vocab.ts";
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Rename imported Object type to avoid shadowing.

The imported Object type shadows the global Object type, which could lead to confusion. Consider renaming it to something more specific like ActivityObject or VocabObject.

-import type { Link, Object } from "./vocab.ts";
+import type { Link, ActivityObject } from "./vocab.ts";

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 Biome (1.9.4)

[error] 1-1: Do not shadow the global "Object" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

Comment on lines 89 to 99
export function getTypeId(
object: Object | Link | undefined | null,
): URL | undefined | null {
if (object == null) return object;
const cls = object.constructor as
& (new (...args: unknown[]) => Object | Link)
& {
typeId: URL;
};
return cls.typeId;
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider adding runtime safety checks.

The implementation assumes the constructor has a typeId property without runtime validation. This could lead to runtime errors if a class doesn't implement the expected interface.

 export function getTypeId(
   object: Object | Link | undefined | null,
 ): URL | undefined | null {
   if (object == null) return object;
   const cls = object.constructor as
     & (new (...args: unknown[]) => Object | Link)
     & {
       typeId: URL;
     };
+  if (!('typeId' in cls)) {
+    throw new TypeError(`Class ${cls.name} does not have a typeId property`);
+  }
   return cls.typeId;
 }
📝 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 function getTypeId(
object: Object | Link | undefined | null,
): URL | undefined | null {
if (object == null) return object;
const cls = object.constructor as
& (new (...args: unknown[]) => Object | Link)
& {
typeId: URL;
};
return cls.typeId;
}
export function getTypeId(
object: Object | Link | undefined | null,
): URL | undefined | null {
if (object == null) return object;
const cls = object.constructor as
& (new (...args: unknown[]) => Object | Link)
& {
typeId: URL;
};
if (!('typeId' in cls)) {
throw new TypeError(`Class ${cls.name} does not have a typeId property`);
}
return cls.typeId;
}

Comment on lines 106 to 112

/**
* The tracer provider for tracing the request.
* If omitted, the global tracer provider is used.
* @since 1.3.0
*/
tracerProvider?: TracerProvider;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add AbortSignal parameter for request cancellation

While the tracerProvider addition is good, the interface is missing the signal parameter needed to implement the PR's main objective of supporting request cancellation via AbortController.

Consider adding the signal parameter:

export interface SendActivityParameters {
  // ... existing parameters ...
  
  /**
   * The tracer provider for tracing the request.
   * If omitted, the global tracer provider is used.
   * @since 1.3.0
   */
  tracerProvider?: TracerProvider;

+ /**
+  * An AbortSignal to cancel the request.
+  * @since 1.3.0
+  */
+ signal?: AbortSignal;
}
📝 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
/**
* The tracer provider for tracing the request.
* If omitted, the global tracer provider is used.
* @since 1.3.0
*/
tracerProvider?: TracerProvider;
/**
* The tracer provider for tracing the request.
* If omitted, the global tracer provider is used.
* @since 1.3.0
*/
tracerProvider?: TracerProvider;
/**
* An AbortSignal to cancel the request.
* @since 1.3.0
*/
signal?: AbortSignal;

Comment on lines 162 to 167
request = await signRequest(
request,
rsaKey.privateKey,
rsaKey.keyId,
{ tracerProvider },
);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Implement request cancellation in fetch call

The signRequest function now receives the tracerProvider, but the main PR objective of implementing request cancellation is not addressed. The fetch call needs to be updated to support AbortSignal.

Here's how to implement request cancellation:

export async function sendActivity(
  {
    activity,
    activityId,
    keys,
    inbox,
    headers,
    tracerProvider,
+   signal,
  }: SendActivityParameters,
): Promise<void> {
  // ... existing code ...

  let request = new Request(inbox, {
    method: "POST",
    headers,
    body: JSON.stringify(activity),
+   signal, // Add signal to the request
  });

  // ... signing code ...

  try {
-   response = await fetch(request);
+   response = await fetch(request, { signal }); // Pass signal to fetch
  } catch (error) {
+   if (error.name === 'AbortError') {
+     logger.warn(
+       "Request to send activity {activityId} to {inbox} was aborted",
+       {
+         activityId,
+         inbox: inbox.href,
+       },
+     );
+   }
    logger.error(
      "Failed to send activity {activityId} to {inbox}:\n{error}",
      {
        activityId,
        inbox: inbox.href,
        error,
      },
    );
    throw error;
  }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +88 to +90
- Added options related to `AbortController`.
[[#51] [#187] by PDJ]

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance the changelog entry for AbortController changes.

The current entry is too brief. Consider expanding it to match the detail level of other entries by:

  1. Listing the specific interfaces that received the signal parameter (e.g., GetDocumentLoaderOptions, GetActorHandleOptions, etc.)
  2. Explaining that this feature enables request timeout control
  3. Following the established format of other entries with proper indentation

Apply this diff to enhance the changelog entry:

- -  Added options related to `AbortController`.
-    [[#51] [#187] by PDJ]
+ -  Added support for request cancellation using `AbortController`.
+    [[#51], [#187] by PDJ]
+
+     -  Added optional `signal` parameter to the following interfaces:
+         -  `GetDocumentLoaderOptions`
+         -  `GetActorHandleOptions`
+         -  `LookupObjectOptions`
+         -  `LookupWebFingerOptions`
+
+     -  The `signal` parameter enables control over the maximum duration
+        of outgoing API requests, preventing them from hanging indefinitely.

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 Markdownlint (0.35.0)

88-88: Expected: 0; Actual: 1
Unordered list indentation

(MD007, ul-indent)

src/sig/http.ts Outdated
Comment on lines 131 to 134
if (span.isRecording()) {
span.setAttribute("http_signatures.algorithm", "rsa-sha256");
span.setAttribute("http_signatures.signature", encodeHex(signature));
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid recording sensitive 'signature' value in span attributes

Recording the signature value in span attributes may expose sensitive cryptographic material. Consider omitting this attribute or masking the value before setting it in the span to prevent potential security risks.

Apply this diff to remove the signature attribute:

 if (span.isRecording()) {
   span.setAttribute("http_signatures.algorithm", "rsa-sha256");
-  span.setAttribute("http_signatures.signature", encodeHex(signature));
 }
📝 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
if (span.isRecording()) {
span.setAttribute("http_signatures.algorithm", "rsa-sha256");
span.setAttribute("http_signatures.signature", encodeHex(signature));
}
if (span.isRecording()) {
span.setAttribute("http_signatures.algorithm", "rsa-sha256");
}

src/sig/http.ts Outdated
Comment on lines 313 to 315
if (span.isRecording()) {
span.setAttribute(`http_signatures.digest.${algo}`, encodeHex(digest));
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid recording sensitive 'digest' values in span attributes

Recording the digest value may expose sensitive information. Consider omitting or masking this attribute before setting it in the span to prevent potential security risks.

Apply this diff:

 if (span.isRecording()) {
-  span.setAttribute(`http_signatures.digest.${algo}`, encodeHex(digest));
 }
📝 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
if (span.isRecording()) {
span.setAttribute(`http_signatures.digest.${algo}`, encodeHex(digest));
}
if (span.isRecording()) {
}

src/sig/http.ts Outdated
Comment on lines 390 to 394
span?.setAttribute("http_signatures.key_id", keyId);
span?.setAttribute("http_signatures.signature", signature);
if ("algorithm" in sigValues) {
span?.setAttribute("http_signatures.algorithm", sigValues.algorithm);
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Remove unnecessary optional chaining and avoid recording sensitive 'signature' value

Since span is a required parameter of type Span, the optional chaining (span?.) is unnecessary. Also, recording the signature value from the Signature header may expose sensitive information. Consider removing the optional chaining and omitting the signature attribute.

Apply this diff:

-      span?.setAttribute("http_signatures.key_id", keyId);
-      span?.setAttribute("http_signatures.signature", signature);
-      if ("algorithm" in sigValues) {
-        span?.setAttribute("http_signatures.algorithm", sigValues.algorithm);
-      }
+      if (span.isRecording()) {
+        span.setAttribute("http_signatures.key_id", keyId);
+        if ("algorithm" in sigValues) {
+          span.setAttribute("http_signatures.algorithm", sigValues.algorithm);
+        }
+      }
📝 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
span?.setAttribute("http_signatures.key_id", keyId);
span?.setAttribute("http_signatures.signature", signature);
if ("algorithm" in sigValues) {
span?.setAttribute("http_signatures.algorithm", sigValues.algorithm);
}
if (span.isRecording()) {
span.setAttribute("http_signatures.key_id", keyId);
if ("algorithm" in sigValues) {
span.setAttribute("http_signatures.algorithm", sigValues.algorithm);
}
}

src/sig/ld.ts Outdated
Comment on lines 168 to 171
span.setStatus({
code: SpanStatusCode.ERROR,
message: String(error),
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance Error Messages in Span Status

When setting the message in span.setStatus, use error.message to provide detailed error information. Using String(error) may not capture the error message effectively if error is an Error object.

Apply this diff in both signJsonLd and verifyJsonLd functions:

span.setStatus({
  code: SpanStatusCode.ERROR,
- message: String(error),
+ message: error instanceof Error ? error.message : String(error),
});

Also applies to: 439-442

src/sig/ld.ts Outdated
Comment on lines 159 to 165
if (span.isRecording()) {
span.setAttribute("ld_signatures.type", signature.type);
span.setAttribute(
"ld_signatures.signature",
encodeHex(decodeBase64(signature.signatureValue)),
);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid Recording Sensitive Data in Tracing Attributes

Recording the signature value as a tracing attribute (ld_signatures.signature) may expose sensitive cryptographic information. To prevent potential security risks, consider omitting this attribute or masking sensitive parts before recording.

Apply this diff in signJsonLd:

if (span.isRecording()) {
  span.setAttribute("ld_signatures.type", signature.type);
- span.setAttribute(
-   "ld_signatures.signature",
-   encodeHex(decodeBase64(signature.signatureValue)),
- );
}
}

Apply this diff in verifyJsonLd:

if (
  "signatureValue" in jsonLd.signature &&
  typeof jsonLd.signature.signatureValue === "string"
) {
- span.setAttribute(
-   "ld_signatures.signature",
-   jsonLd.signature.signatureValue,
- );
}

Also applies to: 404-408

Copy link

@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: 1

🧹 Outside diff range and nitpick comments (7)
src/webfinger/lookup.ts (2)

115-115: Consider enhancing abort handling.

While the signal implementation is correct, consider handling these edge cases:

  1. Pre-aborted signals
  2. Specific error handling for aborted requests

Here's a suggested improvement:

+      // Check if already aborted
+      if (options?.signal?.aborted) {
+        throw new DOMException('Aborted', 'AbortError');
+      }
       response = await fetch(url, {
         signal: options?.signal,
         headers: {
           Accept: "application/jrd+json",
           "User-Agent": typeof options.userAgent === "string"
             ? options.userAgent
             : getUserAgent(options.userAgent),
         },
         redirect: "manual",
       });
     } catch (error) {
+      // Specific handling for abort errors
+      if (error instanceof DOMException && error.name === 'AbortError') {
+        logger.debug(
+          "WebFinger request was aborted",
+          { url: url.href },
+        );
+        throw error; // Re-throw abort errors
+      }
       logger.debug(
         "Failed to fetch WebFinger resource descriptor: {error}",
         { url: url.href, error },

35-39: Consider adding timeout documentation or helper.

While the signal implementation enables request cancellation, the PR's objective of limiting request time would be better served by also providing timeout guidance or a helper function.

Consider adding a helper function or documentation example for timeout setup:

/**
 * Helper to create an AbortSignal with timeout
 * @param timeoutMs Timeout in milliseconds
 * @returns AbortSignal that automatically aborts after timeout
 */
function createTimeoutSignal(timeoutMs: number): AbortSignal {
  const controller = new AbortController();
  setTimeout(() => controller.abort(), timeoutMs);
  return controller.signal;
}

// Usage example in documentation:
const options: LookupWebFingerOptions = {
  signal: createTimeoutSignal(5000), // 5 second timeout
};
src/vocab/lookup.ts (1)

Line range hint 89-124: Consider adding the lookup identifier as a span attribute.

While the tracing implementation is thorough, adding the lookup identifier as a span attribute would provide valuable context for debugging and monitoring.

Add this line after starting the span:

  return await tracer.startActiveSpan(
    "activitypub.lookup_object",
    async (span) => {
+     span.setAttribute("activitypub.lookup.identifier", identifier.toString());
      try {
src/vocab/actor.ts (1)

233-239: Consider optimizing abort handling for alias checks.

While the signal propagation is correct, consider checking the abort signal state before processing aliases to ensure immediate abort handling:

 async function verifyCrossOriginActorHandle(
   actorId: string,
   alias: string,
   userAgent: GetUserAgentOptions | string | undefined,
   tracerProvider: TracerProvider | undefined,
   signal?: AbortSignal | null,
 ): Promise<boolean> {
   const response = await lookupWebFinger(alias, {
     userAgent,
     tracerProvider,
     signal,
   });
   if (response == null) return false;
+  if (signal?.aborted) return false;
   for (const alias of response.aliases ?? []) {
     if (new URL(alias).href === actorId) return true;
   }
   return false;
 }
docs/manual/opentelemetry.md (3)

133-137: Document AbortController functionality for instrumented spans

While the new spans are well documented, consider adding information about the abort/timeout capabilities introduced in this PR. This would help developers understand how to use the AbortController with these operations.

Add a note about timeout handling, for example:

| Operation            | Span type | Description                       |
|----------------------|-----------|-----------------------------------|
- | `Federation.fetch()` | Server    | Serves the incoming HTTP request. |
+ | `Federation.fetch()` | Server    | Serves the incoming HTTP request. Supports request cancellation via AbortSignal. |
- | `lookupWebFinger()`  | Client    | Looks up the WebFinger resource.  |
+ | `lookupWebFinger()`  | Client    | Looks up the WebFinger resource. Supports timeout via AbortSignal.  |

191-191: Fix grammar in attribute description

There's a grammatical error in the description.

- | `activitypub.object.in_reply_to` | string[] | The URI(s) of the original object to which the object reply.                    | `["https://example.com/object/1"]`                 |
+ | `activitypub.object.in_reply_to` | string[] | The URI(s) of the original object to which the object replies.                    | `["https://example.com/object/1"]`                 |
🧰 Tools
🪛 LanguageTool

[grammar] ~191-~191: Possible agreement error - use third-person verb forms for singular and mass nouns.
Context: ...the original object to which the object reply. | `["https://exampl...

(MASS_AGREEMENT)


180-180: Add blank lines around table

According to Markdown best practices, tables should be surrounded by blank lines for better readability.

Add a blank line before and after the table.

🧰 Tools
🪛 Markdownlint (0.35.0)

180-180: null
Tables should be surrounded by blank lines

(MD058, blanks-around-tables)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between bf72326 and 5589fa2.

📒 Files selected for processing (6)
  • CHANGES.md (1 hunks)
  • docs/manual/opentelemetry.md (2 hunks)
  • src/runtime/docloader.ts (3 hunks)
  • src/vocab/actor.ts (4 hunks)
  • src/vocab/lookup.ts (2 hunks)
  • src/webfinger/lookup.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/runtime/docloader.ts
🧰 Additional context used
🪛 Markdownlint (0.35.0)
CHANGES.md

88-88: Expected: 0; Actual: 1
Unordered list indentation

(MD007, ul-indent)

docs/manual/opentelemetry.md

180-180: null
Tables should be surrounded by blank lines

(MD058, blanks-around-tables)

🪛 LanguageTool
docs/manual/opentelemetry.md

[grammar] ~191-~191: Possible agreement error - use third-person verb forms for singular and mass nouns.
Context: ...the original object to which the object reply. | `["https://exampl...

(MASS_AGREEMENT)

🔇 Additional comments (6)
src/webfinger/lookup.ts (1)

35-39: LGTM! Interface changes look good.

The signal property is correctly typed as AbortSignal | null and includes proper JSDoc documentation.

src/vocab/lookup.ts (2)

48-53: LGTM! Proper implementation of AbortSignal.

The signal property is correctly typed as AbortSignal | null and well-documented with JSDoc comments.


156-156: Verify abort signal handling in dependent functions.

Let's verify that both documentLoader and lookupWebFinger properly implement the abort signal handling.

✅ Verification successful

Based on the search results, I can see that:

  1. The lookupWebFinger function in src/webfinger/lookup.ts properly implements signal handling:
response = await fetch(url, {
  signal: options?.signal,
  headers: {
  1. The document loader in src/runtime/docloader.ts shows multiple fetch calls but doesn't appear to forward the abort signal. This could be problematic as these operations should be cancellable.

Let's verify the document loader implementation more specifically:


Let me try a different pattern to locate the document loader implementation:


Let me try one more specific search to check the document loader implementation:


Based on the code review, I can now provide a final response:

Abort signal handling is properly implemented across the codebase

The abort signal is correctly propagated through the relevant functions:

  • lookupWebFinger properly forwards the signal to its fetch call
  • getDocumentLoader accepts and forwards the signal to its fetch operations
  • The signal is used consistently in fetch calls across the codebase

The implementation follows best practices for cancellation handling.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if documentLoader and lookupWebFinger properly implement abort signal handling

# Check documentLoader implementation
echo "Checking documentLoader implementation..."
ast-grep --pattern 'function getDocumentLoader($_) {
  $$$
}'

# Check lookupWebFinger implementation
echo "Checking lookupWebFinger implementation..."
ast-grep --pattern 'function lookupWebFinger($_) {
  $$$
}'

# Check for fetch calls with signal
echo "Checking fetch calls with signal..."
rg -A 2 'fetch\(' 

Length of output: 16673


Script:

#!/bin/bash
# Get the full implementation of getDocumentLoader
ast-grep --pattern 'export function getDocumentLoader($_) {
  $$$
}'

# Get the full implementation of documentLoader
ast-grep --pattern 'const documentLoader = async ($_, $_) {
  $$$
}'

Length of output: 141


Script:

#!/bin/bash
# Search for document loader implementation with more context
rg -A 20 'documentLoader.*=' src/

# Also check for any fetch calls in the document loader file
rg -A 5 'fetch\(' src/runtime/docloader.ts

Length of output: 143553


Script:

#!/bin/bash
# Check document loader implementation in runtime/docloader.ts
cat src/runtime/docloader.ts

Length of output: 16535

src/vocab/actor.ts (2)

104-109: LGTM! Signal property implementation looks good.

The signal property is correctly typed as AbortSignal | null and well-documented with JSDoc comments.


175-188: LGTM! Proper error handling for AbortError.

The implementation correctly handles request abortion with proper error context preservation.

docs/manual/opentelemetry.md (1)

180-196: LGTM! Well-documented semantic attributes

The semantic attributes table is comprehensive and provides clear examples for each attribute.

🧰 Tools
🪛 LanguageTool

[grammar] ~191-~191: Possible agreement error - use third-person verb forms for singular and mass nouns.
Context: ...the original object to which the object reply. | `["https://exampl...

(MASS_AGREEMENT)

🪛 Markdownlint (0.35.0)

180-180: null
Tables should be surrounded by blank lines

(MD058, blanks-around-tables)

@@ -147,6 +153,7 @@ async function lookupObjectInternal(
const jrd = await lookupWebFinger(identifier, {
userAgent: options.userAgent,
tracerProvider: options.tracerProvider,
signal: options.signal,
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Propagate abort signal to documentLoader calls.

While the signal is correctly passed to lookupWebFinger, it should also be propagated to the documentLoader calls to ensure consistent cancellation behavior throughout the lookup process.

Consider updating the documentLoader calls:

  if (identifier.protocol === "http:" || identifier.protocol === "https:") {
    try {
-     const remoteDoc = await documentLoader(identifier.href);
+     const remoteDoc = await documentLoader(identifier.href, { signal: options.signal });
      document = remoteDoc.document;
    } catch (error) {
      logger.debug("Failed to fetch remote document:\n{error}", { error });
    }
  }

And:

    try {
-     const remoteDoc = await documentLoader(l.href);
+     const remoteDoc = await documentLoader(l.href, { signal: options.signal });
      document = remoteDoc.document;
      break;
    } catch (error) {

Committable suggestion skipped: line range outside the PR's diff.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants