-
Notifications
You must be signed in to change notification settings - Fork 736
Add window.nostrdb NIP #2229
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Add window.nostrdb NIP #2229
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| # NIP-DB: Browser Nostr Event Database Interface | ||
|
|
||
| ## Abstract | ||
|
|
||
| This NIP defines a standard interface for browser extensions that provide local Nostr event storage capabilities to web applications. The interface allows web applications to interact with Nostr events stored locally in the browser through a standardized `window.nostrdb` API. | ||
|
|
||
| ## Motivation | ||
|
|
||
| Browser extensions can provide valuable local storage and caching capabilities for Nostr events, improving performance and enabling offline functionality. | ||
|
|
||
| This NIP establishes a common interface that browser extensions can implement to provide Nostr event storage services to web applications. | ||
|
|
||
| ## Specification | ||
|
|
||
| ### Interface Definition | ||
|
|
||
| Browser extensions implementing this NIP MUST inject a `window.nostrdb` object that implements the following interface: | ||
|
|
||
| ```typescript | ||
| interface IWindowNostrDB { | ||
| /** Add an event to the database */ | ||
| add(event: NostrEvent): Promise<boolean>; | ||
|
|
||
| /** Get a single event by ID */ | ||
| event(id: string): Promise<NostrEvent | undefined>; | ||
|
|
||
| /** Get the latest version of a replaceable event */ | ||
| replaceable( | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method is not needed. Just call .query
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agree, the .query can check if the filter is only made by one address and send it here if this is more performant. |
||
| kind: number, | ||
| author: string, | ||
| identifier?: string, | ||
| ): Promise<NostrEvent | undefined>; | ||
|
|
||
| /** Count the number of events matching filters */ | ||
| count(filters: Filter | Filter[]): Promise<number>; | ||
|
|
||
| /** Check if the database backend supports features */ | ||
| supports(): Promise<string[]>; | ||
|
|
||
| /** Get events by filters */ | ||
| query(filters: Filter | Filter[]): Promise<NostrEvent[]>; | ||
|
|
||
| /** Subscribe to events in the database based on filters */ | ||
| subscribe(filters: Filter | Filter[]): AsyncGenerator<NostrEvent>; | ||
| } | ||
| ``` | ||
|
|
||
| ### Feature Detection | ||
|
|
||
| The `supports()` method allows web applications to check for optional features: | ||
|
|
||
| - `"search"` - NIP-50 full-text search capabilities | ||
|
|
||
| ### Implementation Requirements | ||
|
|
||
| 1. **Injection**: The interface MUST be injected into every web page via content scripts | ||
| 2. **Availability**: The interface MUST be available as `window.nostrdb` after DOM content is loaded | ||
| 3. **Error Handling**: All methods MUST handle errors gracefully and return appropriate error states | ||
| 4. **Thread Safety**: The interface MUST be safe to use from multiple contexts | ||
|
|
||
| ### Usage Examples | ||
|
|
||
| #### Basic Event Operations | ||
|
|
||
| ```javascript | ||
| // Add an event | ||
| const success = await window.nostrdb.add(nostrEvent); | ||
|
|
||
| // Get a specific event | ||
| const event = await window.nostrdb.event(eventId); | ||
|
|
||
| // Get latest replaceable event | ||
| const profile = await window.nostrdb.replaceable(0, pubkey); | ||
|
|
||
| // Count events | ||
| const count = await window.nostrdb.count({ kinds: [1] }); | ||
| ``` | ||
|
|
||
| #### Getting Events | ||
|
|
||
| ```javascript | ||
| // Get events matching filters | ||
| const events = await window.nostrdb.query([{ kinds: [1] }]); | ||
| console.log("Found events:", events); | ||
| ``` | ||
|
|
||
| #### Subscribing to Events | ||
|
|
||
| ```javascript | ||
| // Subscribe to events using an async iterator | ||
| for await (const event of window.nostrdb.subscribe([{ kinds: [1] }])) { | ||
| console.log("New event:", event); | ||
| } | ||
| ``` | ||
|
|
||
| #### Feature Detection | ||
|
|
||
| ```javascript | ||
| // Get all supported features | ||
| const supportedFeatures = await window.nostrdb.supports(); | ||
|
|
||
| // Check for search support | ||
| if (supportedFeatures.includes("search")) { | ||
|
Comment on lines
+102
to
+103
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not just use objects directly? if (supportedFeatures.search) {
}Then the UI doesn't need to loop through the array of features and compare strings every time it needs to check.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Accessing fields in a hashmap also involves string comparisons, and some hashing too. Who knows what the JIT compiler does with these "objects", but in theory a single-item array is faster than a single-item hashmap.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Only if there are collisions in each hashcode, otherwise, it's an int comparison. The hashcode is cached after the first usage.
Only if you are not accounting for the string comparison. |
||
| // Use search functionality | ||
| const notes = await window.nostrdb.query({ kinds: [1], search: "nostr" }); | ||
| } | ||
| ``` | ||
|
|
||
| ## Reference Implementation | ||
|
|
||
| A reference implementation is available at: [nostr-bucket](https://github.com/hzrd149/nostr-bucket) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method is not needed. Just call .query
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree, the .query can check if the filter is only made by one ID and send it here if this is more performant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it's important this interface is minimal for extension implementers. Client code can always wrap it with convenience methods.