Skip to content

feat: sd-jwt revocation flow#357

Open
sagarkhole4 wants to merge 8 commits intofeat/oidc-main-syncfrom
feat/sd-jwt-revocation-flow
Open

feat: sd-jwt revocation flow#357
sagarkhole4 wants to merge 8 commits intofeat/oidc-main-syncfrom
feat/sd-jwt-revocation-flow

Conversation

@sagarkhole4
Copy link
Copy Markdown

@sagarkhole4 sagarkhole4 commented Mar 30, 2026

Feat/sd jwt revocation flow

Summary by CodeRabbit

  • New Features

    • Credential revocation: Added revoke endpoint to revoke issued credentials
    • Status list configuration: New settings for server URL, API key, and default size
    • CLI support: Added command-line parameters for status list configuration
    • SD-JWT module: Integrated SD-JWT Virtual Credential support
  • Documentation

    • Updated API documentation with new revocation endpoint

…e sessions and integrate SdJwtVcModule.

Signed-off-by: Sagar Khole <sagar.khole@ayanworks.com>
…e sessions and integrate SdJwtVcModule.

Signed-off-by: Sagar Khole <sagar.khole@ayanworks.com>
Signed-off-by: Sagar Khole <sagar.khole@ayanworks.com>
Signed-off-by: Sagar Khole <sagar.khole@ayanworks.com>
Signed-off-by: Sagar Khole <sagar.khole@ayanworks.com>
Signed-off-by: Sagar Khole <sagar.khole@ayanworks.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 30, 2026

📝 Walkthrough

Walkthrough

The PR introduces credential revocation via status lists for OpenID4VC issuance. It adds CLI configuration for status-list server endpoints, extends the issuance controller and service to create and manage status lists, implements a revocation endpoint, and provides utilities for remote status-list operations including JWT signing and concurrent-modification locking.

Changes

Cohort / File(s) Summary
CLI Configuration & Setup
samples/cliConfig.json, src/cli.ts, src/cliAgent.ts
Added status-list server configuration (URL, API key, default size) to CLI argument parsing and AriesRestConfig; registered SdJwtVcModule; environment variables set from config with required statusListServerUrl validation.
Type Definitions
src/controllers/types.ts, src/controllers/openid4vc/types/issuer.types.ts, src/controllers/x509/x509.types.ts
Added CredentialState and CredentialRole enums; extended credential offer types with statusListDetails (listId, index, listSize) and isRevocable flag; replaced imported X509 key usage enums with local definitions.
Issuance Session Controller & Service
src/controllers/openid4vc/issuance-sessions/issuance-sessions.Controller.ts, src/controllers/openid4vc/issuance-sessions/issuance-sessions.service.ts
Added POST revoke endpoint; refactored createCredentialOffer to async with status-list creation logic including server URL injection and metadata storage; added revokeBySessionId method to revoke credentials in status lists.
Status List Utilities
src/utils/statusListService.ts
New utility service for remote status-list management: checkAndCreateStatusList creates/verifies status lists with JWT signing; revokeCredentialInStatusList updates and signs revocation entries with per-listId promise locking to serialize concurrent operations.
Routes & API Documentation
src/routes/routes.ts, src/routes/swagger.json
Registered POST /openid4vc/issuance-sessions/{issuanceSessionId}/revoke endpoint with authentication; updated OpenAPI schemas across all credential offer types and sessions schema to include statusListDetails and isRevocable properties.
Build Configuration
tsconfig.build.json
Updated TypeScript compiler options: changed lib from ES2021.Promise to ESNext/DOM; reformatted types and include arrays to multi-line format; removed trailing newline.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Controller as IssuanceSessionsController
    participant Service as IssuanceSessionsService
    participant StatusListUtil as statusListService
    participant Server as Status List Server
    participant Agent

    rect rgba(135, 206, 250, 0.5)
    Note over Client,Agent: Credential Offer Creation with Status List
    Client->>Controller: POST /issuance-sessions (with statusListDetails)
    Controller->>Service: createCredentialOffer(options, agentReq)
    Service->>Service: Map credentials async
    loop For each credential
        Service->>StatusListUtil: checkAndCreateStatusList(agent, listId, issuerDid)
        StatusListUtil->>Server: GET /status-lists/{listId}
        alt Status List Exists
            Server-->>StatusListUtil: 200 OK
        else Status List Not Found
            Server-->>StatusListUtil: 404 Not Found
            StatusListUtil->>Agent: Resolve issuer DID verification method
            Agent-->>StatusListUtil: Verification method
            StatusListUtil->>StatusListUtil: Sign statuslist+jwt
            StatusListUtil->>Server: POST /status-lists (id, jwt)
            Server-->>StatusListUtil: 201 Created
        end
        Service->>Service: Build status block with uri & idx
        Service->>Service: Store credential in offerStatusInfo
    end
    Service->>Service: Set issuanceMetadata.StatusListInfo
    Service-->>Controller: Offer with status list injection
    Controller-->>Client: 200 OK (credential offer)
    end

    rect rgba(144, 238, 144, 0.5)
    Note over Client,Agent: Credential Revocation
    Client->>Controller: POST /issuance-sessions/{id}/revoke
    Controller->>Service: revokeBySessionId(agentReq, sessionId)
    Service->>Service: Load issuance session record
    Service->>Service: Extract StatusListInfo from metadata
    loop For each revocation entry
        Service->>StatusListUtil: revokeCredentialInStatusList(agent, listId, idx, issuerDid)
        StatusListUtil->>StatusListUtil: Acquire per-listId lock
        StatusListUtil->>Server: GET /status-lists/{listId}
        Server-->>StatusListUtil: Current JWT
        StatusListUtil->>StatusListUtil: Parse JWT → StatusList
        StatusListUtil->>StatusListUtil: Set index status to revoked (1)
        StatusListUtil->>StatusListUtil: Sign updated statuslist+jwt
        StatusListUtil->>Server: PATCH /status-lists/{listId} (new jwt)
        Server-->>StatusListUtil: 200 OK
        StatusListUtil->>StatusListUtil: Release per-listId lock
    end
    Service-->>Controller: success message
    Controller-->>Client: 200 OK (revocation complete)
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • shitrerohit
  • RinkalBhojani

Poem

🐰 A status list's born in the cloud up high,
Credentials revoked as moments pass by,
Locks keep the peace when conflicts might brew,
From offer to revoke, the JWT's true! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.18% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: sd-jwt revocation flow' directly describes the main objective of the PR, which is to implement revocation functionality for SD-JWT credentials across the OpenID4VC issuance flow.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/sd-jwt-revocation-flow

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

❤️ Share

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

@sonarqubecloud
Copy link
Copy Markdown

@ajile-in ajile-in changed the title Feat/sd jwt revocation flow feat: sd-jwt revocation flow Apr 2, 2026
@tipusinghaw
Copy link
Copy Markdown
Contributor

@coderabbitai Please review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 6, 2026

@tipusinghaw Sure! I'll start reviewing the changes in this PR right away.

✅ Actions performed

Review triggered.

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

"apiKey": "supersecret-that-too-16chars",
"updateJwtSecret": false
"updateJwtSecret": false,
"statusListServerUrl": "https://dev-status-list.sovio.id/",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think these details should be stored in the .env file, as it would make updating the credentials easier.

effectiveIssuerDid,
effectiveStatusList.listSize,
)
const listUri = `${getServerUrl()}/status-lists/${effectiveStatusList.listId}`
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think we should move the status-lists into a common constants file so it can be easily modified from a single place without touching the core code.

throw new Error(`No status list information found for session ${sessionId}`)
}

const { revokeCredentialInStatusList } = await import('../../../utils/statusListService')
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Importing this inside a method might cause duplicate imports if revokeCredentialInStatusList is needed in other methods in the future.

function getApiKeyHeaders() {
const key = process.env.STATUS_LIST_API_KEY;
const headers: Record<string, string> = { 'Content-Type': 'application/json' };
if (key) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If the key is not configured, the request will be sent without the header and may fail. We should throw an error if STATUS_LIST_API_KEY is missing.

return headers;
}

async function getKmsKeyIdForDid(agent: any, did: string, verificationMethodId: string) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think we can take agent type for '@credo-ts/core'

async function signStatusList(agent: any, verificationMethodId: string, statusList: StatusList, listId: string, issuerDid: string): Promise<string> {
const payload = new JwtPayload({
iss: issuerDid,
sub: `${getServerUrl()}/status-lists/${listId}`,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same as previous comment, we should use status-lists from common constant

],
"lib": [
"ESNext",
"DOM"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why we are adding DOM here?

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🧹 Nitpick comments (1)
src/controllers/x509/x509.types.ts (1)

103-113: Example values don't match enum type.

The @example shows string names (["digitalSignature", "keyEncipherment", "crlSign"]), but X509KeyUsage is a numeric enum (e.g., DigitalSignature = 1, KeyEncipherment = 4). API consumers may be confused about whether to send enum names or numeric values.

Update the example to reflect the actual expected format, or clarify how the API deserializes string names to enum values.

📝 Suggested example update
 export interface KeyUsageDto {
   /**
-   * `@example` ["digitalSignature", "keyEncipherment", "crlSign"]
+   * `@example` [1, 4, 64]
+   * `@description` Array of X509KeyUsage values: DigitalSignature=1, NonRepudiation=2, KeyEncipherment=4, DataEncipherment=8, KeyAgreement=16, KeyCertSign=32, CrlSign=64, EncipherOnly=128, DecipherOnly=256
   */
   usages: X509KeyUsage[]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/controllers/x509/x509.types.ts` around lines 103 - 113, The example for
KeyUsageDto is misleading because usages is typed as X509KeyUsage (a numeric
enum); update the example to show numeric enum values or clarify conversion:
change the usages example to use the numeric enum values (e.g., [1, 4, 128]) or
add a note that string names are accepted and will be mapped to X509KeyUsage;
modify the KeyUsageDto JSDoc for the usages property and/or add a brief remark
next to markAsCritical to indicate optionality so consumers know the expected
payload format for the usages field.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/controllers/openid4vc/issuance-sessions/issuance-sessions.service.ts`:
- Around line 194-197: The code treats a missing issuanceMetadata.StatusListInfo
as an internal error; instead, update IssuanceSessionsService to return a client
error (4xx) for this input/caller problem by replacing the thrown generic Error
when statusInfo is falsy/empty with a specific HTTP error (e.g., BadRequest or a
400 HttpError) that includes the sessionId and a clear message; locate the check
around statusInfo = record.issuanceMetadata?.StatusListInfo and change the throw
to the appropriate HttpError type used in the codebase so callers receive a 4xx
rather than a 500.
- Around line 53-88: The code currently always injects a top-level SD-JWT-style
status block and doesn't validate that revocability is actually supported for
the chosen signer/format; update the logic around effectiveIssuerDid,
effectiveStatusList, isRevocable, and the payload merge to (1) validate at offer
creation time that isRevocable is only allowed when a revocation mechanism will
actually be attached (e.g., DID signer + SD-JWT/status-list for SD-JWT formats,
or DID signer + W3C-style credentialStatus for W3C VC formats; reject
combinations like isRevocable true with x5c signer or mso_mdoc or when no
effectiveStatusList), (2) compute and attach format-specific status payloads:
for SD-JWT formats use status: { status_list: { uri, idx } }, for W3C VC formats
use credentialStatus with the equivalent uri/index structure, and for mso_mdoc
do not add a JSON status claim (status is handled in CBOR MSO), and (3) keep
using checkAndCreateStatusList and offerStatusInfo but only push into
offerStatusInfo when you actually attach a revocation mechanism; use the
existing symbols effectiveIssuerDid, effectiveStatusList,
checkAndCreateStatusList, offerStatusInfo, supported, statusBlock and replace
the unconditional payload injection with format-aware branches that either
attach the appropriate claim or throw/reject when revocability cannot be
supported.

In `@src/utils/statusListService.ts`:
- Around line 69-71: The fetch calls in statusListService (the call that uses
uri and getApiKeyHeaders) must be routed through the shared HTTP helper that
enforces an abort/timeout and retry policy; replace direct fetch(uri, { headers:
getApiKeyHeaders() }) usages with the helper so requests are bounded by a
timeout/AbortSignal and ensure retries are only applied to safe read operations
(e.g., the status-list GET path) while write paths (issuance/revocation updates)
use a single timed request without retries; update every occurrence (the fetch
at lines using uri/getApiKeyHeaders and the other fetch blocks around 88-92 and
125-149) to use the helper and propagate the AbortSignal or timeout error
handling back to the caller.
- Around line 68-99: The GET→404→POST path for creating status lists is racy for
concurrent callers: wrap the creation logic for a given listId in a per-listId
lock (e.g., acquire/release based on listId) so only one caller performs the
fetch/create flow, or at minimum handle POST conflicts by treating a 409 as
success and re-reading the list after POST; specifically guard the block that
fetches uri, constructs StatusList, resolves issuer DID via agent.dids.resolve,
signs with signStatusList (using keyId), and posts to
`${getServerUrl()}/status-lists` (i.e., the code referencing listId, listSize,
StatusList, signStatusList, postRes) so concurrent Promise.all callers do not
both try to create the same list and fail.
- Around line 37-63: signStatusList currently hardcodes alg: 'EdDSA' and relies
on a non-specific verification method; change it to select the signing key and
algorithm based on the verification relationship (e.g., assertionMethod) and the
actual key type or provided signerOptions rather than array position, and set
the protected header kid to the full DID URL of the selected verification
method. Concretely: update the logic that calls getKmsKeyIdForDid and the
JwsProtectedHeaderOptions (the header variable) to accept/respect a
signerOptions or derive alg from the resolved key type (Ed25519->EdDSA,
P-256->ES256, secp256k1->ES256K), lookup the correct verificationMethod by its
relationship (not index 0), populate protectedHeaderOptions.kid with the DID URL
of that method, and pass the resolved alg and kid into
jwsService.createJwsCompact; apply the same pattern to the other signing sites
flagged (the other blocks using JwsProtectedHeaderOptions and createJwsCompact).

In `@tsconfig.build.json`:
- Line 39: The file ends with a closing brace but lacks a trailing newline;
update the tsconfig.build.json so that after the final closing brace (`}`) there
is a single newline character at EOF (ensure the file ends with a newline to
satisfy linters and POSIX conventions).
- Around line 19-22: The tsconfig.build.json currently includes "DOM" in the
"lib" array which brings browser types into a Node backend and causes conflicts;
remove "DOM" from the "lib" array so it only contains "ESNext" (or replace with
the minimal required ES libs) and rely on the existing "types": ["node"] for
Node typings; update any project docs or a one-line comment if there's a
specific reason to keep DOM types, otherwise delete the "DOM" entry to resolve
the type conflicts.

---

Nitpick comments:
In `@src/controllers/x509/x509.types.ts`:
- Around line 103-113: The example for KeyUsageDto is misleading because usages
is typed as X509KeyUsage (a numeric enum); update the example to show numeric
enum values or clarify conversion: change the usages example to use the numeric
enum values (e.g., [1, 4, 128]) or add a note that string names are accepted and
will be mapped to X509KeyUsage; modify the KeyUsageDto JSDoc for the usages
property and/or add a brief remark next to markAsCritical to indicate
optionality so consumers know the expected payload format for the usages field.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b154db66-7575-4f6c-917e-11e4e5ca9a08

📥 Commits

Reviewing files that changed from the base of the PR and between 741465f and 5e7263b.

📒 Files selected for processing (12)
  • samples/cliConfig.json
  • src/cli.ts
  • src/cliAgent.ts
  • src/controllers/openid4vc/issuance-sessions/issuance-sessions.Controller.ts
  • src/controllers/openid4vc/issuance-sessions/issuance-sessions.service.ts
  • src/controllers/openid4vc/types/issuer.types.ts
  • src/controllers/types.ts
  • src/controllers/x509/x509.types.ts
  • src/routes/routes.ts
  • src/routes/swagger.json
  • src/utils/statusListService.ts
  • tsconfig.build.json
👮 Files not reviewed due to content moderation or server errors (6)
  • src/controllers/openid4vc/types/issuer.types.ts
  • samples/cliConfig.json
  • src/controllers/openid4vc/issuance-sessions/issuance-sessions.Controller.ts
  • src/cliAgent.ts
  • src/cli.ts
  • src/routes/swagger.json

Comment on lines +53 to 88
const effectiveIssuerDid = cred.signerOptions?.method === SignerMethod.Did ? cred.signerOptions.did : undefined
const effectiveStatusList = cred.statusListDetails || options.statusListDetails

let statusBlock = undefined
if (options.isRevocable && effectiveIssuerDid && effectiveStatusList) {
await checkAndCreateStatusList(
agentReq.agent as any,
effectiveStatusList.listId,
effectiveIssuerDid,
effectiveStatusList.listSize,
)
const listUri = `${getServerUrl()}/status-lists/${effectiveStatusList.listId}`

statusBlock = {
status_list: {
uri: listUri,
idx: effectiveStatusList.index
}
}

offerStatusInfo.push({
credentialSupportedId: cred.credentialSupportedId,
listId: effectiveStatusList.listId,
index: effectiveStatusList.index,
issuerDid: effectiveIssuerDid
})
}

const currentVct = cred.payload && 'vct' in cred.payload ? (cred.payload as any).vct : undefined
return {
...cred,
payload: {
...cred.payload,
vct: currentVct ?? (typeof supported.vct === 'string' ? supported.vct : undefined),
...(statusBlock ? { status: statusBlock } : {})
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In OpenID4VCI / VC specs, which credential formats use SD-JWT-style revocation metadata such as a top-level status.status_list object, and how should revocation/status be represented for W3C VC and mso_mdoc credentials?

💡 Result:

Which formats use SD-JWT-style status.status_list metadata?

Only SD-JWT VC credentials (OID4VCI dc+sd-jwt) use the SD-JWT/OAuth Status List style top-level status claim that can contain a status_list object (URI + index) pointing to a Status List Token. (openid.net)

How to represent revocation/status for W3C VC credentials

For W3C VC formats in OID4VCI (jwt_vc_json, jwt_vc_json-ld, ldp_vc), status/revocation is represented using the W3C VC Data Model credentialStatus property (one object or an array), e.g., a BitstringStatusListEntry with fields like statusPurpose, statusListIndex, and statusListCredential. (openid.net)

How to represent revocation/status for mso_mdoc

For mso_mdoc, status is not expressed as an SD-JWT-style JSON status.status_list claim. Instead, ISO mdoc uses a status parameter inside the MSO (Mobile Security Object) that carries the CBOR Status structure defined for the OAuth Status List mechanism. (openid.net)

Sources

  1. OID4VCI draft 16 (format identifiers, incl. dc+sd-jwt, mso_mdoc, W3C VC formats). (openid.net)
  2. IETF SD-JWT VC draft (defines optional top-level status claim for SD-JWT VCs). (datatracker.ietf.org)
  3. IETF OAuth Status List draft (defines status containing status_list for JOSE tokens; and ISO mdoc mapping via MSO status). (datatracker.ietf.org)
  4. W3C VC Data Model v2.0 (credentialStatus, incl. Bitstring status list entry example). (w3.org)

Citations:


🏁 Script executed:

# Get the full context of the issuance-sessions.service.ts file around the cited lines and find format-related logic
head -150 src/controllers/openid4vc/issuance-sessions/issuance-sessions.service.ts | tail -100

Repository: credebl/agent-controller

Length of output: 3404


🏁 Script executed:

# Search for credential format handling and type definitions in this service
rg -A 5 -B 5 "vct|format|payload" src/controllers/openid4vc/issuance-sessions/issuance-sessions.service.ts | head -200

Repository: credebl/agent-controller

Length of output: 1571


🏁 Script executed:

# Look for credential type/format definitions and supported formats
fd -t f "credential" src/controllers/openid4vc/ | head -20

Repository: credebl/agent-controller

Length of output: 129


🏁 Script executed:

# Search for where credential payloads are structured and if there's format-specific handling
rg "credentialSupportedId|supported\." src/controllers/openid4vc/issuance-sessions/ -A 3 -B 2

Repository: credebl/agent-controller

Length of output: 4940


🏁 Script executed:

# Find credential format configuration and supported types
rg "dc\+sd-jwt|jwt_vc_json|mso_mdoc|ldp_vc" src/ --type ts --type js

Repository: credebl/agent-controller

Length of output: 1044


🏁 Script executed:

# Look for format definitions in credential configuration
rg "format.*:" src/controllers/openid4vc/ -A 2 -B 2 | head -100

Repository: credebl/agent-controller

Length of output: 4028


🏁 Script executed:

# Search for where credentialConfigurationsSupported is populated
rg "credentialConfigurationsSupported" src/ -B 5 -A 5

Repository: credebl/agent-controller

Length of output: 8256


🏁 Script executed:

# Check if there's any format-specific payload handling downstream
rg "W3C|credentialStatus|mso_mdoc" src/ --type ts --type js

Repository: credebl/agent-controller

Length of output: 1161


🏁 Script executed:

# Find the issuer module and createCredentialOffer implementation
fd -t f "issuer" src/ | grep -E "\.ts$" | head -20

Repository: credebl/agent-controller

Length of output: 276


🏁 Script executed:

# Search for createCredentialOffer implementation
rg "createCredentialOffer" src/ -B 3 -A 10 | head -150

Repository: credebl/agent-controller

Length of output: 8077


🏁 Script executed:

# Check if there's any payload transformation based on format
rg "format.*payload|payload.*format" src/controllers/openid4vc/ -B 2 -A 2

Repository: credebl/agent-controller

Length of output: 50


🏁 Script executed:

# Look for W3C credential status handling
rg "credentialStatus" src/ -B 2 -A 2

Repository: credebl/agent-controller

Length of output: 1792


Add format-specific status payload structure and validate revocability against actual revocation support.

This code unconditionally injects a top-level status field (SD-JWT/OAuth Status List style) regardless of credential format. Per OpenID4VCI specs:

  • SD-JWT formats (vc+sd-jwt, dc+sd-jwt) use status.status_list with URI and index
  • W3C VC formats (jwt_vc_json, jwt_vc_json-ld, ldp_vc) must use a credentialStatus property instead
  • mso_mdoc uses CBOR-encoded status in the MSO, not a JSON status claim

Additionally, the isRevocable flag is set independently of whether status metadata is actually attached. Status metadata only appears when a DID signer is used; x5c-signed credentials with isRevocable: true will silently have no revocation mechanism. Reject unsupported revocability/signer/format combinations at offer creation time rather than creating sessions that cannot be revoked.

🧰 Tools
🪛 ESLint

[error] 53-53: Insert ··

(prettier/prettier)


[error] 54-54: Replace ······ with ········

(prettier/prettier)


[error] 56-56: Insert ··

(prettier/prettier)


[error] 57-57: Replace ······ with ········

(prettier/prettier)


[error] 58-58: Insert ··

(prettier/prettier)


[error] 59-59: Replace ·········· with ············

(prettier/prettier)


[error] 60-60: Insert ··

(prettier/prettier)


[error] 61-61: Insert ··

(prettier/prettier)


[error] 62-62: Insert ··

(prettier/prettier)


[error] 63-63: Insert ··

(prettier/prettier)


[error] 64-64: Insert ··

(prettier/prettier)


[error] 66-66: Insert ··

(prettier/prettier)


[error] 67-67: Insert ··

(prettier/prettier)


[error] 68-68: Insert ··

(prettier/prettier)


[error] 69-69: Replace idx:·effectiveStatusList.index with ··idx:·effectiveStatusList.index,

(prettier/prettier)


[error] 70-70: Replace ··········} with ············},

(prettier/prettier)


[error] 71-71: Insert ··

(prettier/prettier)


[error] 73-73: Replace ········ with ··········

(prettier/prettier)


[error] 74-74: Insert ··

(prettier/prettier)


[error] 75-75: Replace ·········· with ············

(prettier/prettier)


[error] 76-76: Insert ··

(prettier/prettier)


[error] 77-77: Replace ··········issuerDid:·effectiveIssuerDid with ············issuerDid:·effectiveIssuerDid,

(prettier/prettier)


[error] 78-78: Insert ··

(prettier/prettier)


[error] 79-79: Insert ··

(prettier/prettier)


[error] 81-81: Insert ··

(prettier/prettier)


[error] 82-82: Replace ······ with ········

(prettier/prettier)


[error] 83-83: Insert ··

(prettier/prettier)


[error] 84-84: Replace ········ with ··········

(prettier/prettier)


[error] 85-85: Insert ··

(prettier/prettier)


[error] 86-86: Replace ·········· with ············

(prettier/prettier)


[error] 87-87: Replace ...(statusBlock·?·{·status:·statusBlock·}·:·{}) with ··...(statusBlock·?·{·status:·statusBlock·}·:·{}),

(prettier/prettier)


[error] 88-88: Insert ··

(prettier/prettier)

🪛 GitHub Check: SonarCloud Code Analysis

[warning] 81-81: This assertion is unnecessary since it does not change the type of the expression.

See more on https://sonarcloud.io/project/issues?id=credebl_afj-controller&issues=AZ09pEfQsmaXyNe1z6ly&open=AZ09pEfQsmaXyNe1z6ly&pullRequest=357

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/controllers/openid4vc/issuance-sessions/issuance-sessions.service.ts`
around lines 53 - 88, The code currently always injects a top-level SD-JWT-style
status block and doesn't validate that revocability is actually supported for
the chosen signer/format; update the logic around effectiveIssuerDid,
effectiveStatusList, isRevocable, and the payload merge to (1) validate at offer
creation time that isRevocable is only allowed when a revocation mechanism will
actually be attached (e.g., DID signer + SD-JWT/status-list for SD-JWT formats,
or DID signer + W3C-style credentialStatus for W3C VC formats; reject
combinations like isRevocable true with x5c signer or mso_mdoc or when no
effectiveStatusList), (2) compute and attach format-specific status payloads:
for SD-JWT formats use status: { status_list: { uri, idx } }, for W3C VC formats
use credentialStatus with the equivalent uri/index structure, and for mso_mdoc
do not add a JSON status claim (status is handled in CBOR MSO), and (3) keep
using checkAndCreateStatusList and offerStatusInfo but only push into
offerStatusInfo when you actually attach a revocation mechanism; use the
existing symbols effectiveIssuerDid, effectiveStatusList,
checkAndCreateStatusList, offerStatusInfo, supported, statusBlock and replace
the unconditional payload injection with format-aware branches that either
attach the appropriate claim or throw/reject when revocability cannot be
supported.

Comment on lines +194 to +197
const statusInfo = record.issuanceMetadata?.StatusListInfo as any[]
if (!statusInfo || statusInfo.length === 0) {
throw new Error(`No status list information found for session ${sessionId}`)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Return a 4xx when the session isn't revocable.

Missing StatusListInfo is a caller/input problem, not an internal failure. Throwing a plain Error here will likely surface as a 500.

🩹 Suggested fix
-    const statusInfo = record.issuanceMetadata?.StatusListInfo as any[]
-    if (!statusInfo || statusInfo.length === 0) {
-      throw new Error(`No status list information found for session ${sessionId}`)
+    const statusInfo = record.issuanceMetadata?.StatusListInfo as any[]
+    if (!Array.isArray(statusInfo) || statusInfo.length === 0) {
+      throw new BadRequestError(`Issuance session ${sessionId} is not revocable`)
     }
📝 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
const statusInfo = record.issuanceMetadata?.StatusListInfo as any[]
if (!statusInfo || statusInfo.length === 0) {
throw new Error(`No status list information found for session ${sessionId}`)
}
const statusInfo = record.issuanceMetadata?.StatusListInfo as any[]
if (!Array.isArray(statusInfo) || statusInfo.length === 0) {
throw new BadRequestError(`Issuance session ${sessionId} is not revocable`)
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/controllers/openid4vc/issuance-sessions/issuance-sessions.service.ts`
around lines 194 - 197, The code treats a missing
issuanceMetadata.StatusListInfo as an internal error; instead, update
IssuanceSessionsService to return a client error (4xx) for this input/caller
problem by replacing the thrown generic Error when statusInfo is falsy/empty
with a specific HTTP error (e.g., BadRequest or a 400 HttpError) that includes
the sessionId and a clear message; locate the check around statusInfo =
record.issuanceMetadata?.StatusListInfo and change the throw to the appropriate
HttpError type used in the codebase so callers receive a 4xx rather than a 500.

Comment on lines +37 to +63
async function signStatusList(agent: any, verificationMethodId: string, statusList: StatusList, listId: string, issuerDid: string): Promise<string> {
const payload = new JwtPayload({
iss: issuerDid,
sub: `${getServerUrl()}/status-lists/${listId}`,
iat: Math.floor(Date.now() / 1000),
additionalClaims: {
status_list: {
bits: statusList.getBitsPerStatus(),
lst: statusList.compressStatusList(),
}
}
});

const header: JwsProtectedHeaderOptions = {
alg: 'EdDSA',
typ: 'statuslist+jwt',
};

const jwsService = agent.dependencyManager.resolve(JwsService);
const kmsKeyId = await getKmsKeyIdForDid(agent, issuerDid, verificationMethodId);

return jwsService.createJwsCompact(agent.context, {
keyId: kmsKeyId,
payload,
protectedHeaderOptions: header,
});
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

For JOSE/JWS, is alg: "EdDSA"valid only for Ed25519/Ed448 keys, or can it be used with P-256/secp256k1 keys? For DID-based JWT signing, is selectingverificationMethod[0] from the DID Document a safe way to choose the signing key?

💡 Result:

1) JOSE/JWS: is alg: "EdDSA" only for Ed25519/Ed448?

Yes. In JOSE/JWS, alg: "EdDSA" is the registered JWS algorithm for EdDSA with Ed25519 or Ed448 (the curve is indicated by the key’s crv, e.g., Ed25519 or Ed448). It is not used with NIST/SECG “EC” curves like P-256 or secp256k1. [1]

For those curves, JOSE uses ECDSA-specific algorithm identifiers, e.g.:

  • P-256alg: "ES256" [2]
  • secp256k1alg: "ES256K" [3]

2) DID-based JWT signing: is picking verificationMethod[0] “safe”?

No. Selecting verificationMethod[0] is not a safe/general key-selection rule because DID Documents commonly contain multiple keys with different intended uses, expressed via verification relationships like authentication, assertionMethod, and keyAgreement. For signing “assertions”, you’re expected to use a verification method authorized via assertionMethod (not whatever happens to be first in an array). [4]

A safer pattern is:

  • Put the specific key you used in the JWT protected header as kid, where kid is a DID URL identifying a particular verification method in the DID Document, then verify that this key is authorized for the intended relationship/purpose. (This is also how several DID/JWT profiles require key identification for verification.) [5]

Sources
[1] RFC 8037 (EdDSA for JOSE/JWS) (datatracker.ietf.org)
[2] RFC 7518 (JWA: ES256 = ECDSA P-256 + SHA-256) (datatracker.ietf.org)
[3] RFC 8812 (ES256K for secp256k1 in JOSE) (ietf.org)
[4] W3C DID Core (verification relationships like assertionMethod, authentication, keyAgreement) (w3.org)
[5] OpenID4VCI draft: kid as DID URL identifying a particular key in the DID Document (and required for signature verification in that profile) (openid.net)

Citations:


Stop hardcoding alg: 'EdDSA' and selecting the first verification method.

The algorithm EdDSA is only valid for Ed25519/Ed448 keys. Issuers using P-256 or secp256k1 keys must use ES256 or ES256K respectively; selecting the first verification method and always applying EdDSA will fail or produce invalid signatures for these curves.

Additionally, DID Documents often contain multiple keys with different purposes (authentication, assertion, key agreement). Picking verificationMethod[0] is unsafe and does not follow DID signing patterns. Instead, select the appropriate key based on its verification relationship (e.g., assertionMethod for signing assertions) and identify it via kid as a DID URL in the protected header. The actual signer key and algorithm should come from signerOptions or be derived from the key's type, not hardcoded or taken from array position.

Applies to lines 37–63, 78–87, 138–143.

🧰 Tools
🪛 ESLint

[error] 37-37: Replace agent:·any,·verificationMethodId:·string,·statusList:·StatusList,·listId:·string,·issuerDid:·string with ⏎··agent:·any,⏎··verificationMethodId:·string,⏎··statusList:·StatusList,⏎··listId:·string,⏎··issuerDid:·string,⏎

(prettier/prettier)


[error] 38-38: Delete ··

(prettier/prettier)


[error] 39-39: Replace ········ with ····

(prettier/prettier)


[error] 40-40: Delete ····

(prettier/prettier)


[error] 41-41: Replace ········ with ····

(prettier/prettier)


[error] 42-42: Delete ····

(prettier/prettier)


[error] 43-43: Replace ············ with ······

(prettier/prettier)


[error] 44-44: Replace ················ with ········

(prettier/prettier)


[error] 45-45: Delete ········

(prettier/prettier)


[error] 46-46: Replace ······} with },

(prettier/prettier)


[error] 47-47: Replace ········} with ····},

(prettier/prettier)


[error] 48-48: Replace ····}); with ··})

(prettier/prettier)


[error] 50-50: Delete ··

(prettier/prettier)


[error] 51-51: Delete ····

(prettier/prettier)


[error] 52-52: Delete ····

(prettier/prettier)


[error] 53-53: Replace ····}; with ··}

(prettier/prettier)


[error] 55-55: Replace ····const·jwsService·=·agent.dependencyManager.resolve(JwsService); with ··const·jwsService·=·agent.dependencyManager.resolve(JwsService)

(prettier/prettier)


[error] 56-56: Replace ····const·kmsKeyId·=·await·getKmsKeyIdForDid(agent,·issuerDid,·verificationMethodId); with ··const·kmsKeyId·=·await·getKmsKeyIdForDid(agent,·issuerDid,·verificationMethodId)

(prettier/prettier)


[error] 58-58: Replace ···· with ··

(prettier/prettier)


[error] 59-59: Delete ····

(prettier/prettier)


[error] 60-60: Delete ····

(prettier/prettier)


[error] 61-61: Delete ····

(prettier/prettier)


[error] 62-62: Replace ··}); with })

(prettier/prettier)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/statusListService.ts` around lines 37 - 63, signStatusList
currently hardcodes alg: 'EdDSA' and relies on a non-specific verification
method; change it to select the signing key and algorithm based on the
verification relationship (e.g., assertionMethod) and the actual key type or
provided signerOptions rather than array position, and set the protected header
kid to the full DID URL of the selected verification method. Concretely: update
the logic that calls getKmsKeyIdForDid and the JwsProtectedHeaderOptions (the
header variable) to accept/respect a signerOptions or derive alg from the
resolved key type (Ed25519->EdDSA, P-256->ES256, secp256k1->ES256K), lookup the
correct verificationMethod by its relationship (not index 0), populate
protectedHeaderOptions.kid with the DID URL of that method, and pass the
resolved alg and kid into jwsService.createJwsCompact; apply the same pattern to
the other signing sites flagged (the other blocks using
JwsProtectedHeaderOptions and createJwsCompact).

Comment on lines +68 to +99
try {
const res = await fetch(uri, {
headers: getApiKeyHeaders()
});

if (res.status === 404) {
console.log(`Status list ${listId} not found, creating a new one...`);
const size = listSize || Number(process.env.STATUS_LIST_DEFAULT_SIZE);
const statusList = new StatusList(new Array(size).fill(0), 1);

const didDocument = await agent.dids.resolve(issuerDid);
const verificationMethod = didDocument.didDocument?.verificationMethod?.[0];

if (!verificationMethod) {
throw new Error(`Could not find verification method for DID ${issuerDid}`);
}

const keyId = verificationMethod.id;

const jwt = await signStatusList(agent, keyId, statusList, listId, issuerDid);
const postRes = await fetch(`${getServerUrl()}/status-lists`, {
method: 'POST',
headers: getApiKeyHeaders(),
body: JSON.stringify({ id: listId, jwt }),
});

if (!postRes.ok) {
const errBody = await postRes.text();
throw new Error(`Failed to create list on server: ${postRes.status} ${errBody}`);
}

console.log(`Successfully created and published new status list ${listId}`);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Make list creation idempotent under concurrency.

This is still a GET→404→POST race. Because the caller now fans out with Promise.all(...) on Line 23 of src/controllers/openid4vc/issuance-sessions/issuance-sessions.service.ts, two credentials that target the same fresh listId can both observe 404, and one POST will fail even though the list was created successfully. Guard this path with the same per-listId lock, or at least treat a conflict response as success and re-read the list.

⚙️ Minimal hardening
-            if (!postRes.ok) {
+            if (postRes.status === 409) {
+                return
+            }
+            if (!postRes.ok) {
                 const errBody = await postRes.text();
                 throw new Error(`Failed to create list on server: ${postRes.status} ${errBody}`);
             }
🧰 Tools
🪛 ESLint

[error] 68-68: Delete ··

(prettier/prettier)


[error] 69-69: Delete ····

(prettier/prettier)


[error] 70-70: Replace ············headers:·getApiKeyHeaders() with ······headers:·getApiKeyHeaders(),

(prettier/prettier)


[error] 71-71: Replace ········}); with ····})

(prettier/prettier)


[error] 73-73: Delete ····

(prettier/prettier)


[error] 74-74: Replace ············console.log(Status·list·${listId}·not·found,·creating·a·new·one...); with ······console.log(Status·list·${listId}·not·found,·creating·a·new·one...)

(prettier/prettier)


[error] 74-74: Unexpected console statement.

(no-console)


[error] 75-75: Replace ············const·size·=·listSize·||·Number(process.env.STATUS_LIST_DEFAULT_SIZE); with ······const·size·=·listSize·||·Number(process.env.STATUS_LIST_DEFAULT_SIZE)

(prettier/prettier)


[error] 76-76: Replace ············const·statusList·=·new·StatusList(new·Array(size).fill(0),·1); with ······const·statusList·=·new·StatusList(new·Array(size).fill(0),·1)

(prettier/prettier)


[error] 78-78: Replace ············const·didDocument·=·await·agent.dids.resolve(issuerDid); with ······const·didDocument·=·await·agent.dids.resolve(issuerDid)

(prettier/prettier)


[error] 79-79: Replace ············const·verificationMethod·=·didDocument.didDocument?.verificationMethod?.[0]; with ······const·verificationMethod·=·didDocument.didDocument?.verificationMethod?.[0]

(prettier/prettier)


[error] 81-81: Replace ············ with ······

(prettier/prettier)


[error] 82-82: Replace ················throw·new·Error(Could·not·find·verification·method·for·DID·${issuerDid}); with ········throw·new·Error(Could·not·find·verification·method·for·DID·${issuerDid})

(prettier/prettier)


[error] 83-83: Replace ············ with ······

(prettier/prettier)


[error] 85-85: Replace ············const·keyId·=·verificationMethod.id; with ······const·keyId·=·verificationMethod.id

(prettier/prettier)


[error] 87-87: Replace ············const·jwt·=·await·signStatusList(agent,·keyId,·statusList,·listId,·issuerDid); with ······const·jwt·=·await·signStatusList(agent,·keyId,·statusList,·listId,·issuerDid)

(prettier/prettier)


[error] 88-88: Replace ············ with ······

(prettier/prettier)


[error] 89-89: Delete ········

(prettier/prettier)


[error] 90-90: Delete ········

(prettier/prettier)


[error] 91-91: Replace ················ with ········

(prettier/prettier)


[error] 92-92: Replace ······}); with })

(prettier/prettier)


[error] 94-94: Replace ············ with ······

(prettier/prettier)


[error] 95-95: Replace ················const·errBody·=·await·postRes.text(); with ········const·errBody·=·await·postRes.text()

(prettier/prettier)


[error] 96-96: Replace ················throw·new·Error(Failed·to·create·list·on·server:·${postRes.status}·${errBody}); with ········throw·new·Error(Failed·to·create·list·on·server:·${postRes.status}·${errBody})

(prettier/prettier)


[error] 97-97: Delete ······

(prettier/prettier)


[error] 99-99: Replace ············console.log(Successfully·created·and·published·new·status·list·${listId}); with ······console.log(Successfully·created·and·published·new·status·list·${listId})

(prettier/prettier)


[error] 99-99: Unexpected console statement.

(no-console)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/statusListService.ts` around lines 68 - 99, The GET→404→POST path
for creating status lists is racy for concurrent callers: wrap the creation
logic for a given listId in a per-listId lock (e.g., acquire/release based on
listId) so only one caller performs the fetch/create flow, or at minimum handle
POST conflicts by treating a 409 as success and re-reading the list after POST;
specifically guard the block that fetches uri, constructs StatusList, resolves
issuer DID via agent.dids.resolve, signs with signStatusList (using keyId), and
posts to `${getServerUrl()}/status-lists` (i.e., the code referencing listId,
listSize, StatusList, signStatusList, postRes) so concurrent Promise.all callers
do not both try to create the same list and fail.

Comment on lines +69 to +71
const res = await fetch(uri, {
headers: getApiKeyHeaders()
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Bound the status-list server calls with timeouts.

These fetches run on issuance/revocation request paths with no abort/timeout, so a slow or hung status-list server can stall the API request indefinitely. Please route them through a shared HTTP helper with a bounded timeout, and only retry the safe read path.

Also applies to: 88-92, 125-149

🧰 Tools
🪛 ESLint

[error] 69-69: Delete ····

(prettier/prettier)


[error] 70-70: Replace ············headers:·getApiKeyHeaders() with ······headers:·getApiKeyHeaders(),

(prettier/prettier)


[error] 71-71: Replace ········}); with ····})

(prettier/prettier)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/statusListService.ts` around lines 69 - 71, The fetch calls in
statusListService (the call that uses uri and getApiKeyHeaders) must be routed
through the shared HTTP helper that enforces an abort/timeout and retry policy;
replace direct fetch(uri, { headers: getApiKeyHeaders() }) usages with the
helper so requests are bounded by a timeout/AbortSignal and ensure retries are
only applied to safe read operations (e.g., the status-list GET path) while
write paths (issuance/revocation updates) use a single timed request without
retries; update every occurrence (the fetch at lines using uri/getApiKeyHeaders
and the other fetch blocks around 88-92 and 125-149) to use the helper and
propagate the AbortSignal or timeout error handling back to the caller.

Comment on lines +19 to +22
"lib": [
"ESNext",
"DOM"
]
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Search for usage of common DOM-specific types and APIs that would require the DOM lib.

echo "Checking for DOM-specific type usage..."

# Check for common DOM types in type annotations
rg -n --type=ts --type=tsx -P '\b(HTMLElement|Document|Window|Navigator|Location|Element|NodeList|DOMParser|XMLHttpRequest|FormData|Blob|File)\b' -g '!node_modules' -g '!build' -C 2

echo -e "\n---\nChecking for DOM API usage..."

# Check for DOM API usage
rg -n --type=ts --type=tsx -P '\b(window\.|document\.|navigator\.|localStorage\.|sessionStorage\.)\w+' -g '!node_modules' -g '!build' -C 2

Repository: credebl/agent-controller

Length of output: 215


🏁 Script executed:

# First, let's view the tsconfig.build.json file to understand the full context
cat -n tsconfig.build.json

Repository: credebl/agent-controller

Length of output: 1063


🏁 Script executed:

# Check the package.json to see what dependencies might require DOM types
cat -n package.json | head -50

Repository: credebl/agent-controller

Length of output: 2219


🏁 Script executed:

# Rerun the DOM detection with corrected file type (use 'ts' only and broader search)
rg -n --type=ts '(HTMLElement|Document|Window|Navigator|Location|Element|NodeList|DOMParser|XMLHttpRequest|FormData|Blob|File|window\.|document\.|navigator\.|localStorage\.|sessionStorage\.)' -g '!node_modules' -g '!build' -C 2

Repository: credebl/agent-controller

Length of output: 29626


🏁 Script executed:

# Check specifically for fetch usage
rg -n 'fetch\(' -g '!node_modules' -g '!build'

Repository: credebl/agent-controller

Length of output: 577


Remove "DOM" from the lib array—it introduces unnecessary type conflicts without being used.

Adding "DOM" includes browser-specific type definitions in a Node.js backend, causing potential conflicts with Node.js globals like setTimeout, setInterval, and fetch. A search of the codebase shows:

  • No DOM-specific types (HTMLElement, Document, Window, etc.) are used anywhere
  • fetch() is used in several files, but Node.js 18+ has native fetch support with types provided by @types/node, which is already configured via "types": ["node"]
  • No clear connection to the PR's SD-JWT revocation flow changes

Either remove "DOM" or document specifically why it's required. The current TypeScript configuration with "types": ["node"] is sufficient for this backend project.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tsconfig.build.json` around lines 19 - 22, The tsconfig.build.json currently
includes "DOM" in the "lib" array which brings browser types into a Node backend
and causes conflicts; remove "DOM" from the "lib" array so it only contains
"ESNext" (or replace with the minimal required ES libs) and rely on the existing
"types": ["node"] for Node typings; update any project docs or a one-line
comment if there's a specific reason to keep DOM types, otherwise delete the
"DOM" entry to resolve the type conflicts.

"scripts"
]
}
} No newline at end of file
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add trailing newline.

The file is missing a trailing newline. Most style guides and linters (ESLint, Prettier) prefer files to end with a newline for better POSIX compliance and cleaner diffs.

📝 Suggested fix

Add a newline after the closing brace on line 39.

   ]
 }
+
📝 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
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tsconfig.build.json` at line 39, The file ends with a closing brace but lacks
a trailing newline; update the tsconfig.build.json so that after the final
closing brace (`}`) there is a single newline character at EOF (ensure the file
ends with a newline to satisfy linters and POSIX conventions).

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.

2 participants