feat(extensions): add extension loader infrastructure (#3)
Some checks are pending
Docker Compose Actions Workflow / test (push) Waiting to run
Some checks are pending
Docker Compose Actions Workflow / test (push) Waiting to run
## Summary - Adds a modular extension system for Lightning.Pub enabling third-party plugins - Provides isolated SQLite databases per extension for data safety - Implements ExtensionContext API for accessing Lightning.Pub services (payments, Nostr, storage) - Supports RPC method registration with automatic namespacing - Includes HTTP route handling for protocols like LNURL - Event routing for payment receipts and Nostr events - Comprehensive documentation with architecture overview and working examples ## Key Components - `src/extensions/types.ts` - Core extension interfaces - `src/extensions/loader.ts` - Extension discovery, loading, and lifecycle management - `src/extensions/context.ts` - Bridge between extensions and Lightning.Pub services - `src/extensions/database.ts` - SQLite isolation with WAL mode - `src/extensions/README.md` - Full documentation with examples ## ExtensionContext API | Method | Description | |--------|-------------| | `getApplication()` | Get application info | | `createInvoice()` | Create Lightning invoice | | `payInvoice()` | Pay Lightning invoice | | `getLnurlPayInfo()` | Get LNURL-pay info for a user (enables Lightning Address/zaps) | | `sendEncryptedDM()` | Send Nostr DM (NIP-44) | | `publishNostrEvent()` | Publish Nostr event | | `registerMethod()` | Register RPC method | | `onPaymentReceived()` | Subscribe to payment callbacks | | `onNostrEvent()` | Subscribe to Nostr events | ## Test plan - [x] Review extension loader code for correctness - [x] Verify TypeScript compilation succeeds - [x] Test extension discovery from `src/extensions/` directory - [x] Test RPC method registration and routing - [x] Test database isolation between extensions 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: boufni95 <boufni95@gmail.com> Co-authored-by: Patrick Mulligan <patjmulligan@protonmail.com> Reviewed-on: #3
This commit is contained in:
parent
72c9872b23
commit
77e5772afd
47 changed files with 10187 additions and 4828 deletions
|
|
@ -1431,6 +1431,9 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- __failure_reason__: _string_ *this field is optional
|
||||
- __operation_payment__: _[UserOperation](#UserOperation)_ *this field is optional
|
||||
- __quote__: _[InvoiceSwapQuote](#InvoiceSwapQuote)_
|
||||
- __refund_address__: _string_ *this field is optional
|
||||
- __refund_at_unix__: _number_ *this field is optional
|
||||
- __refund_tx_id__: _string_ *this field is optional
|
||||
|
||||
### InvoiceSwapQuote
|
||||
- __address__: _string_
|
||||
|
|
|
|||
|
|
@ -391,6 +391,9 @@ type InvoiceSwapOperation struct {
|
|||
Failure_reason string `json:"failure_reason"`
|
||||
Operation_payment *UserOperation `json:"operation_payment"`
|
||||
Quote *InvoiceSwapQuote `json:"quote"`
|
||||
Refund_address string `json:"refund_address"`
|
||||
Refund_at_unix int64 `json:"refund_at_unix"`
|
||||
Refund_tx_id string `json:"refund_tx_id"`
|
||||
}
|
||||
type InvoiceSwapQuote struct {
|
||||
Address string `json:"address"`
|
||||
|
|
|
|||
|
|
@ -2270,15 +2270,21 @@ export type InvoiceSwapOperation = {
|
|||
failure_reason?: string
|
||||
operation_payment?: UserOperation
|
||||
quote: InvoiceSwapQuote
|
||||
refund_address?: string
|
||||
refund_at_unix?: number
|
||||
refund_tx_id?: string
|
||||
}
|
||||
export type InvoiceSwapOperationOptionalField = 'completed_at_unix' | 'failure_reason' | 'operation_payment'
|
||||
export const InvoiceSwapOperationOptionalFields: InvoiceSwapOperationOptionalField[] = ['completed_at_unix', 'failure_reason', 'operation_payment']
|
||||
export type InvoiceSwapOperationOptionalField = 'completed_at_unix' | 'failure_reason' | 'operation_payment' | 'refund_address' | 'refund_at_unix' | 'refund_tx_id'
|
||||
export const InvoiceSwapOperationOptionalFields: InvoiceSwapOperationOptionalField[] = ['completed_at_unix', 'failure_reason', 'operation_payment', 'refund_address', 'refund_at_unix', 'refund_tx_id']
|
||||
export type InvoiceSwapOperationOptions = OptionsBaseMessage & {
|
||||
checkOptionalsAreSet?: InvoiceSwapOperationOptionalField[]
|
||||
completed_at_unix_CustomCheck?: (v?: number) => boolean
|
||||
failure_reason_CustomCheck?: (v?: string) => boolean
|
||||
operation_payment_Options?: UserOperationOptions
|
||||
quote_Options?: InvoiceSwapQuoteOptions
|
||||
refund_address_CustomCheck?: (v?: string) => boolean
|
||||
refund_at_unix_CustomCheck?: (v?: number) => boolean
|
||||
refund_tx_id_CustomCheck?: (v?: string) => boolean
|
||||
}
|
||||
export const InvoiceSwapOperationValidate = (o?: InvoiceSwapOperation, opts: InvoiceSwapOperationOptions = {}, path: string = 'InvoiceSwapOperation::root.'): Error | null => {
|
||||
if (opts.checkOptionalsAreSet && opts.allOptionalsAreSet) return new Error(path + ': only one of checkOptionalsAreSet or allOptionalNonDefault can be set for each message')
|
||||
|
|
@ -2300,6 +2306,15 @@ export const InvoiceSwapOperationValidate = (o?: InvoiceSwapOperation, opts: Inv
|
|||
if (quoteErr !== null) return quoteErr
|
||||
|
||||
|
||||
if ((o.refund_address || opts.allOptionalsAreSet || opts.checkOptionalsAreSet?.includes('refund_address')) && typeof o.refund_address !== 'string') return new Error(`${path}.refund_address: is not a string`)
|
||||
if (opts.refund_address_CustomCheck && !opts.refund_address_CustomCheck(o.refund_address)) return new Error(`${path}.refund_address: custom check failed`)
|
||||
|
||||
if ((o.refund_at_unix || opts.allOptionalsAreSet || opts.checkOptionalsAreSet?.includes('refund_at_unix')) && typeof o.refund_at_unix !== 'number') return new Error(`${path}.refund_at_unix: is not a number`)
|
||||
if (opts.refund_at_unix_CustomCheck && !opts.refund_at_unix_CustomCheck(o.refund_at_unix)) return new Error(`${path}.refund_at_unix: custom check failed`)
|
||||
|
||||
if ((o.refund_tx_id || opts.allOptionalsAreSet || opts.checkOptionalsAreSet?.includes('refund_tx_id')) && typeof o.refund_tx_id !== 'string') return new Error(`${path}.refund_tx_id: is not a string`)
|
||||
if (opts.refund_tx_id_CustomCheck && !opts.refund_tx_id_CustomCheck(o.refund_tx_id)) return new Error(`${path}.refund_tx_id: custom check failed`)
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue