Update util docs
This commit is contained in:
+156
-136
@@ -1,185 +1,205 @@
|
||||
# Nostr Events
|
||||
|
||||
The Events module provides comprehensive type definitions and utilities for working with Nostr events, including helper functions for event creation, validation, and manipulation.
|
||||
The Events module provides type definitions and utilities for working with Nostr events, including creation, validation, and manipulation functions.
|
||||
|
||||
## Event Types Hierarchy
|
||||
## API
|
||||
|
||||
### Event Types
|
||||
|
||||
```typescript
|
||||
// Base event with content and tags
|
||||
interface EventContent {
|
||||
tags: string[][]
|
||||
content: string
|
||||
}
|
||||
// Base event content structure
|
||||
export type EventContent = {
|
||||
tags: string[][];
|
||||
content: string;
|
||||
};
|
||||
|
||||
// Base event with kind
|
||||
interface EventTemplate extends EventContent {
|
||||
kind: number
|
||||
}
|
||||
// Event template with kind
|
||||
export type EventTemplate = EventContent & {
|
||||
kind: number;
|
||||
};
|
||||
|
||||
// Event with timestamp
|
||||
interface StampedEvent extends EventTemplate {
|
||||
created_at: number
|
||||
}
|
||||
export type StampedEvent = EventTemplate & {
|
||||
created_at: number;
|
||||
};
|
||||
|
||||
// Event with author
|
||||
interface OwnedEvent extends StampedEvent {
|
||||
pubkey: string
|
||||
}
|
||||
export type OwnedEvent = StampedEvent & {
|
||||
pubkey: string;
|
||||
};
|
||||
|
||||
// Event with ID
|
||||
interface HashedEvent extends OwnedEvent {
|
||||
id: string
|
||||
}
|
||||
export type HashedEvent = OwnedEvent & {
|
||||
id: string;
|
||||
};
|
||||
|
||||
// Event with signature
|
||||
interface SignedEvent extends HashedEvent {
|
||||
sig: string
|
||||
[verifiedSymbol]?: boolean
|
||||
}
|
||||
// Signed event
|
||||
export type SignedEvent = HashedEvent & {
|
||||
sig: string;
|
||||
};
|
||||
|
||||
// Event with wrapped content
|
||||
interface UnwrappedEvent extends HashedEvent {
|
||||
wrap: SignedEvent
|
||||
}
|
||||
// Wrapped event (NIP-59)
|
||||
export type UnwrappedEvent = HashedEvent & {
|
||||
wrap: SignedEvent;
|
||||
};
|
||||
|
||||
// Event that can be either signed or wrapped
|
||||
type TrustedEvent = HashedEvent & {
|
||||
sig?: string
|
||||
wrap?: SignedEvent
|
||||
[verifiedSymbol]?: boolean
|
||||
}
|
||||
export type TrustedEvent = HashedEvent & {
|
||||
sig?: string;
|
||||
wrap?: SignedEvent;
|
||||
};
|
||||
```
|
||||
|
||||
## Event Creation
|
||||
|
||||
### Create Basic Event
|
||||
```typescript
|
||||
import { createEvent } from '@welshman/util'
|
||||
|
||||
const event = createEvent(
|
||||
1, // kind
|
||||
{
|
||||
content: "Hello Nostr!",
|
||||
tags: [["t", "nostr"]],
|
||||
created_at: now() // Optional, defaults to current time
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
## Type Guards
|
||||
### Event Creation
|
||||
|
||||
```typescript
|
||||
// Check event types
|
||||
isEventTemplate(event): boolean
|
||||
isStampedEvent(event): boolean
|
||||
isOwnedEvent(event): boolean
|
||||
isHashedEvent(event): boolean
|
||||
isSignedEvent(event): boolean
|
||||
isUnwrappedEvent(event): boolean
|
||||
isTrustedEvent(event): boolean
|
||||
// Options for creating events
|
||||
export type MakeEventOpts = {
|
||||
content?: string;
|
||||
tags?: string[][];
|
||||
created_at?: number;
|
||||
};
|
||||
|
||||
// Creates a stamped event template
|
||||
export declare const makeEvent: (kind: number, opts?: MakeEventOpts) => StampedEvent;
|
||||
```
|
||||
|
||||
## Event Type Conversion
|
||||
### Type Guards
|
||||
|
||||
```typescript
|
||||
// Convert to specific event types
|
||||
asEventTemplate(event): EventTemplate
|
||||
asStampedEvent(event): StampedEvent
|
||||
asOwnedEvent(event): OwnedEvent
|
||||
asHashedEvent(event): HashedEvent
|
||||
asSignedEvent(event): SignedEvent
|
||||
asUnwrappedEvent(event): UnwrappedEvent
|
||||
asTrustedEvent(event): TrustedEvent
|
||||
export declare const isEventTemplate: (e: EventTemplate) => e is EventTemplate;
|
||||
export declare const isStampedEvent: (e: StampedEvent) => e is StampedEvent;
|
||||
export declare const isOwnedEvent: (e: OwnedEvent) => e is OwnedEvent;
|
||||
export declare const isHashedEvent: (e: HashedEvent) => e is HashedEvent;
|
||||
export declare const isSignedEvent: (e: TrustedEvent) => e is SignedEvent;
|
||||
export declare const isUnwrappedEvent: (e: TrustedEvent) => e is UnwrappedEvent;
|
||||
export declare const isTrustedEvent: (e: TrustedEvent) => e is TrustedEvent;
|
||||
```
|
||||
|
||||
## Event Utilities
|
||||
### Event Utilities
|
||||
|
||||
### Event Validation
|
||||
```typescript
|
||||
// Check if event has valid signature
|
||||
hasValidSignature(event: SignedEvent): boolean
|
||||
// Event validation and signatures
|
||||
export declare const verifyEvent: (event: TrustedEvent) => boolean;
|
||||
|
||||
// Get event identifier (d tag)
|
||||
getIdentifier(event: EventTemplate): string | undefined
|
||||
// Event properties
|
||||
export declare const getIdentifier: (e: EventTemplate) => string | undefined;
|
||||
export declare const getIdOrAddress: (e: HashedEvent) => string;
|
||||
export declare const getIdAndAddress: (e: HashedEvent) => string[];
|
||||
|
||||
// Event type checking
|
||||
export declare const isEphemeral: (e: EventTemplate) => boolean;
|
||||
export declare const isReplaceable: (e: EventTemplate) => boolean;
|
||||
export declare const isPlainReplaceable: (e: EventTemplate) => boolean;
|
||||
export declare const isParameterizedReplaceable: (e: EventTemplate) => boolean;
|
||||
|
||||
// Thread and reply handling
|
||||
// Note: getAncestors handles comments (kind 1111) differently from regular notes
|
||||
export declare const getAncestors: (event: EventTemplate) => { roots: string[]; replies: string[] };
|
||||
export declare const getParentIdsAndAddrs: (event: EventTemplate) => string[];
|
||||
export declare const getParentIdOrAddr: (event: EventTemplate) => string | undefined;
|
||||
export declare const getParentId: (event: EventTemplate) => string | undefined;
|
||||
export declare const getParentAddr: (event: EventTemplate) => string | undefined;
|
||||
export declare const isChildOf: (child: EventTemplate, parent: HashedEvent) => boolean;
|
||||
```
|
||||
|
||||
### Event References
|
||||
```typescript
|
||||
// Get event ID or address
|
||||
getIdOrAddress(event: HashedEvent): string
|
||||
## Threading Protocols
|
||||
|
||||
// Get both ID and address (if replaceable)
|
||||
getIdAndAddress(event: HashedEvent): string[]
|
||||
```
|
||||
The `getAncestors` function handles two different threading protocols:
|
||||
|
||||
### Event Type Checking
|
||||
```typescript
|
||||
// Check event properties
|
||||
isEphemeral(event: EventTemplate): boolean
|
||||
isReplaceable(event: EventTemplate): boolean
|
||||
isPlainReplaceable(event: EventTemplate): boolean
|
||||
isParameterizedReplaceable(event: EventTemplate): boolean
|
||||
```
|
||||
### Regular Notes (NIP-10)
|
||||
For regular notes and most event kinds, threading follows [NIP-10](https://github.com/nostr-protocol/nips/blob/master/10.md):
|
||||
- Uses `e` and `a` tags with optional markers (`root`, `reply`, `mention`)
|
||||
- Positional rules apply when markers are absent:
|
||||
- First `e`/`a` tag = root
|
||||
- Last `e`/`a` tag = reply target
|
||||
- Middle tags = mentions
|
||||
|
||||
### Thread & Reply Handling
|
||||
```typescript
|
||||
// Get thread information
|
||||
getAncestors(event: EventTemplate): { roots: string[], replies: string[] }
|
||||
### Comments (NIP-22)
|
||||
For comments (kind 1111), threading follows [NIP-22](https://github.com/nostr-protocol/nips/blob/master/22.md):
|
||||
- Uses uppercase tags (`E`, `A`, `P`, `K`) for root references
|
||||
- Uses lowercase tags (`e`, `a`, `p`, `k`) for reply references
|
||||
- No positional rules - explicit tag types determine relationship
|
||||
|
||||
// Get parent references
|
||||
getParentIdsAndAddrs(event: EventTemplate): string[]
|
||||
getParentIdOrAddr(event: EventTemplate): string | undefined
|
||||
getParentId(event: EventTemplate): string | undefined
|
||||
getParentAddr(event: EventTemplate): string | undefined
|
||||
|
||||
// Check reply relationship
|
||||
isChildOf(child: EventTemplate, parent: HashedEvent): boolean
|
||||
```
|
||||
All `getParent*` functions and `isChildOf` include this logic, automatically handling both protocols based on event kind.
|
||||
|
||||
## Examples
|
||||
|
||||
### Creating and Processing Events
|
||||
### Creating Events
|
||||
|
||||
```typescript
|
||||
// Create new event
|
||||
const event = createEvent(1, {
|
||||
content: "Hello world!",
|
||||
tags: [["t", "greeting"]]
|
||||
})
|
||||
import { makeEvent, NOTE, LONG_FORM } from '@welshman/util';
|
||||
|
||||
// Process based on type
|
||||
if (isSignedEvent(event)) {
|
||||
// Handle signed event
|
||||
if (hasValidSignature(event)) {
|
||||
processValidEvent(event)
|
||||
}
|
||||
} else if (isUnwrappedEvent(event)) {
|
||||
// Handle wrapped event
|
||||
processWrappedEvent(event)
|
||||
}
|
||||
// Create a basic note
|
||||
const note = makeEvent(NOTE, {
|
||||
content: "Hello Nostr!",
|
||||
tags: [["t", "nostr"]]
|
||||
});
|
||||
|
||||
// Create a long-form article with custom timestamp
|
||||
const article = makeEvent(LONG_FORM, {
|
||||
content: "# My Article\n\nThis is my article content...",
|
||||
tags: [["d", "my-article"], ["title", "My Article"]],
|
||||
created_at: 1234567890
|
||||
});
|
||||
```
|
||||
|
||||
### Event Properties
|
||||
|
||||
```typescript
|
||||
import { getIdentifier, getIdOrAddress, LONG_FORM } from '@welshman/util';
|
||||
|
||||
const article = makeEvent(LONG_FORM, {
|
||||
content: "Article content...",
|
||||
tags: [["d", "my-unique-id"]]
|
||||
});
|
||||
|
||||
// Get the identifier (d tag value)
|
||||
const identifier = getIdentifier(article); // "my-unique-id"
|
||||
|
||||
// For a hashed event, get ID or address
|
||||
const reference = getIdOrAddress(hashedArticle);
|
||||
// Returns address for replaceable events, ID for others
|
||||
```
|
||||
|
||||
### Working with Threads
|
||||
|
||||
```typescript
|
||||
// Get thread context
|
||||
const ancestors = getAncestors(event)
|
||||
const rootId = ancestors.roots[0]
|
||||
const replyTo = ancestors.replies[0]
|
||||
import { getAncestors, isChildOf, NOTE, COMMENT } from '@welshman/util';
|
||||
|
||||
// Check threading
|
||||
if (isChildOf(event, parentEvent)) {
|
||||
// Handle reply
|
||||
// Regular note reply (NIP-10)
|
||||
const noteReply = makeEvent(NOTE, {
|
||||
content: "This is a reply to a note",
|
||||
tags: [
|
||||
["e", "root-event-id", "", "root"],
|
||||
["e", "parent-event-id", "", "reply"]
|
||||
]
|
||||
});
|
||||
|
||||
// Comment reply (NIP-22)
|
||||
const commentReply = makeEvent(COMMENT, {
|
||||
content: "This is a reply comment",
|
||||
tags: [
|
||||
["E", "root-event-id"], // uppercase = root reference
|
||||
["e", "parent-event-id"] // lowercase = reply reference
|
||||
]
|
||||
});
|
||||
|
||||
// Both work the same way
|
||||
const noteAncestors = getAncestors(noteReply);
|
||||
const commentAncestors = getAncestors(commentReply);
|
||||
|
||||
console.log('Note roots:', noteAncestors.roots); // ["root-event-id"]
|
||||
console.log('Note replies:', noteAncestors.replies); // ["parent-event-id"]
|
||||
|
||||
console.log('Comment roots:', commentAncestors.roots); // ["root-event-id"]
|
||||
console.log('Comment replies:', commentAncestors.replies); // ["parent-event-id"]
|
||||
|
||||
// Parent checking works for both protocols
|
||||
if (isChildOf(noteReply, parentEvent)) {
|
||||
console.log('Note is a reply');
|
||||
}
|
||||
if (isChildOf(commentReply, parentEvent)) {
|
||||
console.log('Comment is a reply');
|
||||
}
|
||||
```
|
||||
|
||||
### Type Conversion
|
||||
|
||||
```typescript
|
||||
// Convert to needed type
|
||||
const template = asEventTemplate(event)
|
||||
const stamped = asStampedEvent(event)
|
||||
const owned = asOwnedEvent(event)
|
||||
const hashed = asHashedEvent(event)
|
||||
const signed = asSignedEvent(event)
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user