Skip to content

error mapping normalization#45

Merged
ntheile merged 4 commits into
masterfrom
error-mapping
Jul 8, 2026
Merged

error mapping normalization#45
ntheile merged 4 commits into
masterfrom
error-mapping

Conversation

@ntheile

@ntheile ntheile commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features
    • Standardized error responses across supported nodes with clearer NWC-style codes plus provider code/status/message details.
    • Operation-aware error reporting for more accurate failure context, including unsupported Bolt12 actions.
  • Bug Fixes
    • Improved payment failure handling (e.g., insufficient balance, unauthorized, payment failed) across varying response formats, including non-JSON.
    • More reliable “not found” behavior for missing invoices/transactions.
  • Tests
    • Expanded coverage for provider error normalization and Strike error mappings.
  • Documentation
    • Added guidance on how provider-specific errors are normalized across integrations.

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fd0770e7-8018-4752-9167-d23defd4e9eb

📥 Commits

Reviewing files that changed from the base of the PR and between d14b1a4 and 6ac74cc.

⛔ Files ignored due to path filters (5)
  • bindings/typescript-arkade/package-lock.json is excluded by !**/package-lock.json
  • bindings/typescript-spark/examples/spark-expo-go/package-lock.json is excluded by !**/package-lock.json
  • bindings/typescript-spark/examples/spark-web/package-lock.json is excluded by !**/package-lock.json
  • bindings/typescript-spark/package-lock.json is excluded by !**/package-lock.json
  • bindings/typescript/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (4)
  • AGENTS.md
  • bindings/typescript-arkade/package.json
  • bindings/typescript-spark/package.json
  • bindings/typescript/package.json
✅ Files skipped from review due to trivial changes (4)
  • bindings/typescript-arkade/package.json
  • AGENTS.md
  • bindings/typescript/package.json
  • bindings/typescript-spark/package.json

📝 Walkthrough

Walkthrough

This PR standardizes provider error handling across the TypeScript bindings and Rust core crate. It adds shared normalization helpers, operation-aware request plumbing, richer NwcError/ApiError::Nwc metadata, updated tests, and versioning/docs refreshes.

Changes

TypeScript bindings error normalization

Layer / File(s) Summary
NwcError fields and normalization utility
bindings/typescript/src/errors.ts, bindings/typescript/src/internal/error-normalization.ts, bindings/typescript/src/__tests__/provider-error-normalization.test.ts
NwcError gains provider metadata fields; shared helpers extract provider info, map codes/messages/statuses, normalize errors, and add coverage for provider-specific and pathological cases.
Blink adapter error mapping
bindings/typescript/src/nodes/blink.ts
GraphQL errors become operation-aware NwcErrors, and invoice, payment, onchain, offer, and lookup call sites pass operation identifiers.
CLN and LND adapter error mapping
bindings/typescript/src/nodes/cln.ts, bindings/typescript/src/nodes/lnd.ts
CLN and LND request helpers accept operations, normalize provider failures, and replace generic not-found or unsupported-path errors with standardized NwcError values.
Phoenixd and Speed adapter error mapping
bindings/typescript/src/nodes/phoenixd.ts, bindings/typescript/src/nodes/speed.ts
Phoenixd and Speed request helpers become operation-aware, payment failures are detected from response payloads, and not-found or unsupported paths now throw standardized NwcErrors.
Strike adapter error mapping and tests
bindings/typescript/src/nodes/strike.ts, bindings/typescript/src/__tests__/strike.test.ts
Strike request helpers normalize provider failures with operation context, and the Strike test suite asserts the normalized codes, metadata, and fallback behavior.
Package versioning and ignore updates
AGENTS.md, bindings/typescript/package.json, bindings/typescript-arkade/package.json, bindings/typescript-spark/package.json, bindings/typescript/.gitignore
TypeScript package version guidance, package versions, peer ranges, and archive ignore rules are updated together.

Rust core crate error normalization

Layer / File(s) Summary
error_normalization module and crate wiring
crates/lni/error_normalization.rs, crates/lni/lib.rs
New module defines provider info parsing, HTTP and message mapping, normalized response/transport helpers, and tests; lib.rs registers the module.
Blink Rust API error mapping
crates/lni/blink/api.rs
Blink GraphQL execution becomes operation-aware and invoice, payment, onchain, offer, lookup, and listing flows route request and GraphQL errors through normalized nwc_error handling.
CLN and LND Rust API error mapping
crates/lni/cln/api.rs, crates/lni/lnd/api.rs
CLN and LND request paths add shared provider mapping, operation tagging, explicit status checks, and standardized not-found, not-implemented, and payment-failure errors.
Phoenixd and Speed Rust API error mapping
crates/lni/phoenixd/api.rs, crates/lni/speed/api.rs
Phoenixd and Speed request flows normalize transport and provider errors, add explicit payment outcome handling, and convert lookup/list not-found cases into structured nwc_error values.
Strike Rust API error mapping and tests
crates/lni/strike/api.rs
Strike HTTP and transport failures are normalized into ApiError::Nwc, and the Strike test suite validates the resulting codes and messages.

Error normalization documentation

Layer / File(s) Summary
Error normalization reference document
docs/error-normalization.md
Documents canonical codes, fallback rules, and adapter-specific mapping tables and checklists for all six providers.

Estimated code review effort: 4 (Complex) | ~75 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Adapter as Node adapter
  participant Helper as normalizeProviderError
  participant Error as NwcError

  Adapter->>Helper: throwNormalizedProviderError(error, options)
  Helper->>Helper: extract provider info and map code/message/status
  Helper->>Error: construct normalized NwcError
  Error-->>Adapter: throw provider metadata on error
Loading
sequenceDiagram
  participant Api as Provider API module
  participant Core as error_normalization
  participant ApiError as ApiError::Nwc

  Api->>Core: transport_error / provider_error_from_response
  Core->>Core: map provider code, message, and HTTP status
  Core->>ApiError: build normalized ApiError::Nwc
  ApiError-->>Api: return structured error
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title is related to the changes, but it is generic and does not indicate the provider-specific normalization work. Use a more specific title such as "Normalize provider error mapping across node bindings" or similar.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch error-mapping

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.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f44e00950b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/lni/strike/api.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (6)
crates/lni/phoenixd/api.rs (1)

309-313: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Redundant double map_err; first mapping is dead.

The first .map_err(|e| ApiError::Json { ... }) produces a value that the second .map_err(|_| ...) immediately discards, so the Failed to parse pay_invoice response message is never used. Collapse to a single conversion (matching the match style used in pay_offer).

♻️ Proposed simplification
-    let pay_invoice_resp: PhoenixPayInvoiceResp = serde_json::from_str(&response_text)
-        .map_err(|e| ApiError::Json {
-            reason: format!("Failed to parse pay_invoice response: {}", e),
-        })
-        .map_err(|_| phoenixd_error_from_body(None, response_text.clone()))?;
+    let pay_invoice_resp: PhoenixPayInvoiceResp = match serde_json::from_str(&response_text) {
+        Ok(resp) => resp,
+        Err(_) => return Err(phoenixd_error_from_body(None, response_text.clone())),
+    };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/lni/phoenixd/api.rs` around lines 309 - 313, The `pay_invoice_resp`
parsing path has a redundant double `map_err`, where the `ApiError::Json` from
`serde_json::from_str` is immediately discarded by the next `map_err`. In
`phoenixd/api.rs`, simplify the `pay_invoice_resp` conversion to a single
error-handling step, following the same `match`-based pattern used in
`pay_offer`, so the parse failure is handled in one place and no dead error
mapping remains.
crates/lni/strike/api.rs (1)

87-196: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Strike duplicates the shared error_normalization helpers.

Every other adapter in this PR consumes crate::error_normalization (ProviderErrorInfo, provider_info_from_body, map_http_status, provider_error_from_response, transport_error), but Strike re-implements equivalents (StrikeProviderErrorInfo, json_string_field, json_u16_field, map_provider_http_status, strike_nwc_error_from_transport). Consider reusing the shared module with a Strike-specific map_provider_code, matching the CLN/LND/Phoenixd/Speed pattern, to reduce divergence.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/lni/strike/api.rs` around lines 87 - 196, Strike is duplicating the
shared error-normalization logic instead of using crate::error_normalization.
Refactor strike_nwc_error_from_response and strike_nwc_error_from_transport to
build on ProviderErrorInfo, provider_info_from_body, map_http_status,
provider_error_from_response, and transport_error, and keep only a
Strike-specific map_provider_code equivalent for any provider code translation.
Remove the local StrikeProviderErrorInfo/json_* helpers so Strike follows the
same CLN/LND/Phoenixd/Speed pattern and stays consistent with the shared adapter
behavior.
bindings/typescript/src/internal/error-normalization.ts (1)

188-223: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Rewrapping an already-normalized NwcError is a bit wasteful.

When error instanceof NwcError (Line 192), a brand-new NwcError is constructed with cause: error, purely to potentially override operation/provider. Since all other fields are copied verbatim, in the common case where options.operation/options.provider match the existing values this just adds an extra wrapping layer/allocation. Not incorrect, just avoidable churn.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@bindings/typescript/src/internal/error-normalization.ts` around lines 188 -
223, In normalizeProviderError, avoid rewrapping an input that is already an
NwcError unless you actually need to change operation or provider. Check the
existing error’s operation/provider against options.operation and
options.provider, and return the original error unchanged when they already
match; only construct a new NwcError in the NwcError branch when those fields
need overriding, keeping the rest of the copied fields and cause behavior as-is.
bindings/typescript/src/nodes/blink.ts (2)

421-431: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Consider NOT_FOUND instead of OTHER for missing BTC wallet.

getBtcWallet throws blinkNwcError('OTHER', ...) when no BTC wallet exists in the account. NOT_FOUND would better convey the failure mode to NWC clients and is consistent with how other adapters signal missing resources.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@bindings/typescript/src/nodes/blink.ts` around lines 421 - 431, The
getBtcWallet method currently throws blinkNwcError with OTHER when no BTC wallet
is found; change that error to use NOT_FOUND so the missing-resource failure is
signaled correctly to NWC clients. Update the throw in getBtcWallet while
keeping the existing message and context, and make sure the BlinkWallet lookup
logic and cachedWalletId assignment remain unchanged.

256-262: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Only the first GraphQL error's code is used for classification.

blinkProviderInfoFromErrors joins all error messages but only reads code from errors[0]. If Blink returns multiple errors with different codes (e.g., a benign validation error first and an UNAUTHORIZED error second), the more actionable code could be missed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@bindings/typescript/src/nodes/blink.ts` around lines 256 - 262, The error
aggregation in blinkProviderInfoFromErrors only classifies by the first
GraphQLError, so it can miss a more relevant code when Blink returns multiple
errors. Update blinkProviderInfoFromErrors in blink.ts to inspect all errors and
derive the ProviderErrorInfo.code from the most actionable error rather than
errors[0], while still joining all messages for ProviderErrorInfo.message.
bindings/typescript/src/nodes/strike.ts (1)

531-534: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Bolt12 "not implemented" paths still use generic LniError, unlike Blink's equivalent.

createInvoice's Bolt12 guard and createOffer/getOffer/listOffers/payOffer still throw new LniError('Api', 'Bolt12 is not implemented for StrikeNode.'), whereas the equivalent Blink methods in this same PR were migrated to blinkNwcError('NOT_IMPLEMENTED', ...) with proper operation tagging. Worth aligning for consistency across adapters, though low urgency since the feature itself is unimplemented either way.

Also applies to: 762-776

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@bindings/typescript/src/nodes/strike.ts` around lines 531 - 534, The Bolt12
unsupported paths in StrikeNode still throw the generic LniError, unlike the
Blink adapter’s NOT_IMPLEMENTED handling. Update StrikeNode’s createInvoice and
the related Bolt12 methods createOffer, getOffer, listOffers, and payOffer to
use the same not-implemented error helper/pattern as Blink, including the
correct operation tagging and a consistent NOT_IMPLEMENTED-style error shape
instead of LniError.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@bindings/typescript/src/internal/error-normalization.ts`:
- Around line 34-105: The recursive search in findStringProperty and
findNumberProperty can walk arbitrarily deep provider JSON and overflow the
stack. Add a bounded traversal to both helpers, either by threading a
max-depth/step limit through providerInfoFromJsonErrorBody or by switching to an
iterative queue-based search with a visited/bound cap. Keep
normalizeProviderError calling the guarded helpers so malformed or
pathologically nested provider payloads still normalize into NwcError instead of
throwing.

In `@bindings/typescript/src/nodes/lnd.ts`:
- Around line 358-361: The FAILED branch in lnd.ts is mapping missing or
unrecognized failure_reason values through mapLndFailureReason, which can
incorrectly label a definite payment failure as OTHER. Update the
wrapped.result.status === 'FAILED' handling in the pay_invoice flow to default
to PAYMENT_FAILED when the status is FAILED, and only use mapLndFailureReason
when a specific known failure reason is present. Keep the existing lndNwcError
call and reason handling, but ensure the status-to-NWC error mapping in this
branch always surfaces PAYMENT_FAILED for failed payments.

In `@bindings/typescript/src/nodes/strike.ts`:
- Around line 225-319: The Strike error parsing code is duplicating shared
normalization logic and has drifted from
`bindings/typescript/src/internal/error-normalization.ts`. Update
`findNumberProperty`/`strikeProviderErrorInfo` in `strike.ts` to reuse the
shared helpers instead of reimplementing them, so numeric-string status values
are handled consistently and parse failures preserve the raw body text as the
message. Align the JSON parsing and deep-search flow with
`providerInfoFromJsonErrorBody`, and keep `throwStrikeError` wired through
`throwNormalizedProviderError` with the shared extraction behavior.

In `@crates/lni/blink/api.rs`:
- Line 164: The response body read in api.rs should not panic via
response.text().await.unwrap(); instead, route the body read through the same
structured error handling used in the surrounding code. Update the logic around
the response_text assignment in the relevant response-processing function to
capture the text read failure and convert it into the existing error type with
map_err or equivalent, so the request path fails gracefully rather than
unwinding on a mid-stream disconnect.

---

Nitpick comments:
In `@bindings/typescript/src/internal/error-normalization.ts`:
- Around line 188-223: In normalizeProviderError, avoid rewrapping an input that
is already an NwcError unless you actually need to change operation or provider.
Check the existing error’s operation/provider against options.operation and
options.provider, and return the original error unchanged when they already
match; only construct a new NwcError in the NwcError branch when those fields
need overriding, keeping the rest of the copied fields and cause behavior as-is.

In `@bindings/typescript/src/nodes/blink.ts`:
- Around line 421-431: The getBtcWallet method currently throws blinkNwcError
with OTHER when no BTC wallet is found; change that error to use NOT_FOUND so
the missing-resource failure is signaled correctly to NWC clients. Update the
throw in getBtcWallet while keeping the existing message and context, and make
sure the BlinkWallet lookup logic and cachedWalletId assignment remain
unchanged.
- Around line 256-262: The error aggregation in blinkProviderInfoFromErrors only
classifies by the first GraphQLError, so it can miss a more relevant code when
Blink returns multiple errors. Update blinkProviderInfoFromErrors in blink.ts to
inspect all errors and derive the ProviderErrorInfo.code from the most
actionable error rather than errors[0], while still joining all messages for
ProviderErrorInfo.message.

In `@bindings/typescript/src/nodes/strike.ts`:
- Around line 531-534: The Bolt12 unsupported paths in StrikeNode still throw
the generic LniError, unlike the Blink adapter’s NOT_IMPLEMENTED handling.
Update StrikeNode’s createInvoice and the related Bolt12 methods createOffer,
getOffer, listOffers, and payOffer to use the same not-implemented error
helper/pattern as Blink, including the correct operation tagging and a
consistent NOT_IMPLEMENTED-style error shape instead of LniError.

In `@crates/lni/phoenixd/api.rs`:
- Around line 309-313: The `pay_invoice_resp` parsing path has a redundant
double `map_err`, where the `ApiError::Json` from `serde_json::from_str` is
immediately discarded by the next `map_err`. In `phoenixd/api.rs`, simplify the
`pay_invoice_resp` conversion to a single error-handling step, following the
same `match`-based pattern used in `pay_offer`, so the parse failure is handled
in one place and no dead error mapping remains.

In `@crates/lni/strike/api.rs`:
- Around line 87-196: Strike is duplicating the shared error-normalization logic
instead of using crate::error_normalization. Refactor
strike_nwc_error_from_response and strike_nwc_error_from_transport to build on
ProviderErrorInfo, provider_info_from_body, map_http_status,
provider_error_from_response, and transport_error, and keep only a
Strike-specific map_provider_code equivalent for any provider code translation.
Remove the local StrikeProviderErrorInfo/json_* helpers so Strike follows the
same CLN/LND/Phoenixd/Speed pattern and stays consistent with the shared adapter
behavior.
🪄 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: a55aa942-9d9c-44fd-9635-b08645937e96

📥 Commits

Reviewing files that changed from the base of the PR and between 1afd87c and f44e009.

📒 Files selected for processing (20)
  • bindings/typescript/src/__tests__/provider-error-normalization.test.ts
  • bindings/typescript/src/__tests__/strike.test.ts
  • bindings/typescript/src/errors.ts
  • bindings/typescript/src/internal/error-normalization.ts
  • bindings/typescript/src/nodes/blink.ts
  • bindings/typescript/src/nodes/cln.ts
  • bindings/typescript/src/nodes/lnd.ts
  • bindings/typescript/src/nodes/phoenixd.ts
  • bindings/typescript/src/nodes/speed.ts
  • bindings/typescript/src/nodes/strike.ts
  • bindings/typescript/sunnyln-lni-0.2.13.tgz
  • crates/lni/blink/api.rs
  • crates/lni/cln/api.rs
  • crates/lni/error_normalization.rs
  • crates/lni/lib.rs
  • crates/lni/lnd/api.rs
  • crates/lni/phoenixd/api.rs
  • crates/lni/speed/api.rs
  • crates/lni/strike/api.rs
  • docs/error-normalization.md

Comment thread bindings/typescript/src/internal/error-normalization.ts
Comment thread bindings/typescript/src/nodes/lnd.ts
Comment thread bindings/typescript/src/nodes/strike.ts Outdated
Comment thread crates/lni/blink/api.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@bindings/typescript/src/__tests__/provider-error-normalization.test.ts`:
- Around line 22-47: The pathological payload test in normalizeProviderError is
not actually exercising the bounded traversal because providerInfo is never
extracted from the JSON body. Update the test to pass extractProviderError using
providerInfoFromJsonErrorBody so the deep body is parsed and findProperty
performs the BFS traversal capped by MAX_PROVIDER_ERROR_SEARCH_STEPS; keep the
existing assertions on the NwcError result.
🪄 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: e9408996-3c68-4a5a-9efe-47d0ddc60863

📥 Commits

Reviewing files that changed from the base of the PR and between f44e009 and d14b1a4.

📒 Files selected for processing (11)
  • bindings/typescript/.gitignore
  • bindings/typescript/src/__tests__/provider-error-normalization.test.ts
  • bindings/typescript/src/__tests__/strike.test.ts
  • bindings/typescript/src/internal/error-normalization.ts
  • bindings/typescript/src/nodes/blink.ts
  • bindings/typescript/src/nodes/lnd.ts
  • bindings/typescript/src/nodes/strike.ts
  • crates/lni/blink/api.rs
  • crates/lni/error_normalization.rs
  • crates/lni/phoenixd/api.rs
  • crates/lni/strike/api.rs
✅ Files skipped from review due to trivial changes (1)
  • bindings/typescript/.gitignore
🚧 Files skipped from review as they are similar to previous changes (7)
  • bindings/typescript/src/tests/strike.test.ts
  • crates/lni/error_normalization.rs
  • bindings/typescript/src/internal/error-normalization.ts
  • crates/lni/phoenixd/api.rs
  • bindings/typescript/src/nodes/lnd.ts
  • crates/lni/blink/api.rs
  • bindings/typescript/src/nodes/blink.ts

@ntheile
ntheile merged commit 9dae6a8 into master Jul 8, 2026
3 checks passed
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.

1 participant