Skip to content

DX overhaul, gacela 1.19 / router 0.13 upgrade, CORS + LUD-06 fix, tooling#25

Merged
Chemaclass merged 4 commits into
mainfrom
feat/dx-improvements-and-gacela-1.19
Jul 23, 2026
Merged

DX overhaul, gacela 1.19 / router 0.13 upgrade, CORS + LUD-06 fix, tooling#25
Chemaclass merged 4 commits into
mainfrom
feat/dx-improvements-and-gacela-1.19

Conversation

@Chemaclass

Copy link
Copy Markdown
Contributor

Summary

A developer-experience pass over the library, plus the gacela stack upgrade that the new gacela-project/router 0.13 release unblocks, a LUD-06 wire-format fix, router middleware, and modernized dev tooling.

Reviewed commit-by-commit for a clean history.

1. DX overhaul (485e954)

  • Metadata correctness: build LNURL-pay metadata via a shared LnurlPayMetadata value object using json_encode instead of string concatenation — a quote in the description no longer produces invalid JSON. Removes duplication between CallbackUrl and InvoiceGenerator.
  • HTTP errors: HttpApi now catches Symfony transport/HTTP errors + JsonException and returns null, so the backend surfaces a clean "unreachable" error instead of leaking a stack trace (that graceful path was previously dead code).
  • Remove dump($e) from the controller; normalize errors to the LNURL {status, reason} shape.
  • Add a BackendType enum and clear errors in addBackendsFile (missing file, invalid JSON, missing/unknown type); add addBackend() to register backends programmatically without a JSON file.
  • Centralize app-config keys in ConfigKey so the config writer/reader can't drift; fix the nonsensical REQUEST_URI receiver default.
  • Add return-shape docblocks to the facade/generator for IDE autocomplete.
  • Rename the misleadingly named nostr.json backends file to backends.json.
  • Rewrite the README (install, config, HTTP API, programmatic use, reference table).

2. LUD-06 pr key fix (d5fc11e)

LUD-06 mandates the callback response carry the bolt11 invoice under a field named pr. The response keyed it bolt11, so spec-compliant wallets saw the invoice as missing. Renamed the output key to pr (the internal InvoiceTransfer property and the LNbits API field stay bolt11).

3. gacela 1.19 / router 0.13 upgrade (485e954)

gacela 1.9.1 -> 1.19, router 0.12.1 -> 0.13, container 0.6 -> 0.10, phpstan-extension 0.3 -> 0.4. Required fixes (caught by a live server smoke test, not the unit suite):

  • SendableRange::__set_state for gacela's var_export'd merged-config file cache.
  • Generic @extends params for the now-templated gacela base classes.
  • DocBlockResolverAwareTrait -> ServiceResolverAwareTrait (deprecated).

4. CORS middleware + global exception handler (f2bca2d)

Uses router 0.13's new middleware support:

  • CorsMiddleware sets Access-Control-Allow-Origin: * on every response and answers OPTIONS preflight directly (route registered for GET + OPTIONS).
  • InvoiceExceptionHandler registered as the router's global Exception handler, rendering any uncaught error as the LNURL error object — the controller no longer needs a try/catch.

5. Tooling modernization (66e8cdd)

PHPUnit 9.6 -> 10.5 (config migrated to the 10.x schema), PHPStan 1.12 -> 2.2 (clean at level max), Rector 1.2 -> 2.0 (applied its new suggestions). Removed now-unused symfony/var-dumper.

Testing

  • composer test-all green: psalm (98.9% type coverage), phpstan 2 level max, PHPUnit 10 (26 tests), rector 2 — all clean.
  • Live server smoke-tested end-to-end: LNURL pay params, pr-keyed invoice, Backend "LnBits" unreachable path, CORS header on GET, OPTIONS preflight (200), and the global error handler — 0 fatals.

Notes

  • gacela-project/gacela constraint is ^1.19; the upgrade depended on gacela-project/router 0.13 (which requires container ^0.10).
  • Breaking for downstream consumers: the nostr.json -> backends.json rename and the bolt11 -> pr response key.

Developer-experience pass across the library plus the framework upgrade
that the new gacela-router 0.13 release unblocks.

DX / correctness:
- Build LNURL-pay metadata via a shared LnurlPayMetadata value object using
  json_encode instead of string concatenation (a quote in the description no
  longer produces invalid JSON); removes duplication between CallbackUrl and
  InvoiceGenerator.
- Wire HttpApi to catch Symfony transport/HTTP errors and JsonException and
  return null, so the backend surfaces a clean "unreachable" error instead of
  leaking a stack trace (the graceful path was previously dead code).
- Remove dump($e) from the controller; normalize error responses to the LNURL
  {status, reason} shape.
- Add a BackendType enum and clear errors in LightningConfig::addBackendsFile
  (missing file, invalid JSON, missing/unknown type); add addBackend() to
  register backends programmatically without a JSON file.
- Centralize app-config keys in ConfigKey so the config writer/reader cannot
  drift; fix the nonsensical REQUEST_URI receiver default.
- Add return-shape docblocks to the facade/generator for IDE autocomplete.
- Rename the misleadingly named nostr.json backends file to backends.json.
- Rewrite the README (install, config, HTTP API, programmatic use, reference).

Gacela upgrade (gacela 1.19, router 0.13, container 0.10, phpstan-ext 0.4):
- Add SendableRange::__set_state for gacela's var_export'd merged-config cache.
- Add generic @extends params for the now-templated gacela base classes.
- Swap deprecated DocBlockResolverAwareTrait for ServiceResolverAwareTrait.

Tests: add coverage for metadata escaping, the backend-type enum, programmatic
addBackend, and the hardened backends-file error paths.
LNURL-pay (LUD-06) mandates the callback response carry the bolt11 invoice
in a field named 'pr'. The response keyed it 'bolt11', so spec-compliant
wallets saw the invoice as missing. Rename the output key to 'pr' (the
internal InvoiceTransfer property and the LNbits API field stay 'bolt11').
Use gacela-router 0.13's new middleware support:
- CorsMiddleware sets Access-Control-Allow-Origin on every response and
  answers OPTIONS preflight directly, so browser-based wallets can call the
  LNURL endpoints. The route is registered for GET and OPTIONS so preflight
  reaches the middleware.
- InvoiceExceptionHandler is registered as the router's global handler for
  Exception, rendering any uncaught error as the LNURL {status, reason}
  object. The controller no longer needs its own try/catch.

Tests: unit-cover the middleware's preflight short-circuit / delegation and
the handler's error rendering (process-isolated for the header() calls).
- PHPUnit 9.6 -> 10.5: migrated phpunit.xml to the 10.x schema (coverage
  <source>, deprecated attributes removed, cache directory added). Tests
  needed no code changes.
- PHPStan 1.12 -> 2.2: clean at level max, no code changes required.
- Rector 1.2 -> 2.0: applied its new suggestions (narrow a test fake's
  return type, sort named arguments to constructor-declaration order).
- Remove symfony/var-dumper: it only backed the dump() call removed earlier.
@Chemaclass Chemaclass self-assigned this Jul 23, 2026
@Chemaclass
Chemaclass merged commit cbeedb2 into main Jul 23, 2026
8 of 9 checks passed
@Chemaclass
Chemaclass deleted the feat/dx-improvements-and-gacela-1.19 branch July 23, 2026 11:57
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