Fix NIP conformance in domain kinds; add domain docs/skill
tests / tests (push) Failing after 5m15s

This commit is contained in:
2026-06-20 14:55:21 +00:00
committed by Jon Staab
parent e2a6ef21cd
commit ed17dcc412
33 changed files with 1406 additions and 658 deletions
-91
View File
@@ -1,91 +0,0 @@
# Encryptable
The Encryptable module provides utilities for handling encrypted Nostr events, allowing you to merge plaintext updates into events and encrypt them before publishing.
## API
```typescript
// Encryption function type
export type Encrypt = (x: string) => Promise<string>;
// Partial event content for updates
export type EncryptableUpdates = Partial<EventContent>;
// Event with attached plaintext data
export type DecryptedEvent = TrustedEvent & {
plaintext: EncryptableUpdates;
};
// Creates a DecryptedEvent by attaching plaintext to an event
export declare const asDecryptedEvent: (
event: TrustedEvent,
plaintext?: EncryptableUpdates
) => DecryptedEvent;
// Encryptable class for handling encrypted events
export declare class Encryptable<T extends EventTemplate> {
constructor(
event: Partial<T>,
updates: EncryptableUpdates
);
// Encrypts updates and merges them into the event
reconcile(encrypt: Encrypt): Promise<T>;
}
```
## Examples
### Basic Usage
```typescript
import { Encryptable } from '@welshman/util';
// Create encryptable with plaintext updates
const encryptable = new Encryptable(
{ kind: 10000 }, // Base event template
{ content: "secret mute list data" } // Plaintext content to encrypt
);
// Encrypt and get final event
const encryptFn = async (text: string) => {
// Your encryption logic here
return await encrypt(text);
};
const event = await encryptable.reconcile(encryptFn);
// event.content is now encrypted
```
### Encrypting Tags
```typescript
import { Encryptable } from '@welshman/util';
// Encrypt both content and tag values
const encryptable = new Encryptable(
{ kind: 10000, tags: [] },
{
content: JSON.stringify(['pubkey1', 'pubkey2']),
tags: [['p', 'sensitive-pubkey'], ['e', 'sensitive-event-id']]
}
);
// The reconcile method encrypts tag values at index 1
const event = await encryptable.reconcile(encryptFn);
// event.tags[0] = ['p', 'encrypted-pubkey']
// event.tags[1] = ['e', 'encrypted-event-id']
```
### Working with Decrypted Events
```typescript
import { asDecryptedEvent } from '@welshman/util';
// Add plaintext data to an event for reference
const event = { kind: 10000, content: "encrypted...", tags: [] };
const plaintext = { content: "original content", tags: [['p', 'pubkey']] };
const decryptedEvent = asDecryptedEvent(event, plaintext);
console.log(decryptedEvent.plaintext.content); // "original content"
```
-134
View File
@@ -1,134 +0,0 @@
# Handlers (NIP-89)
The Handlers module provides functionality for working with handler recommendations and information (NIP-89).
Handlers are events that describe which kinds a given application can display.
This module provides utilities for transforming these events into structured handler objects that applications can easily process.
## Types
### Handler Definition
```typescript
type Handler = {
kind: number // Event kind this handler can process
name: string // Display name of the handler
about: string // Description
image: string // Icon or image URL
identifier: string // Unique identifier (d-tag)
event: TrustedEvent // Original handler event
website?: string // Optional website URL
lud16?: string // Optional Lightning address
nip05?: string // Optional NIP-05 identifier
}
```
## Core Functions
### Reading Handlers
```typescript
function readHandlers(event: TrustedEvent): Handler[]
// Example
const handlers = readHandlers(handlerEvent)
handlers.forEach(handler => {
console.log(`Handler for kind ${handler.kind}: ${handler.name}`)
})
```
### Handler Identification
```typescript
function getHandlerKey(handler: Handler): string
// Returns "kind:address" format
function getHandlerAddress(event: TrustedEvent): string | undefined
// Gets handler address from event tags
```
### Display Formatting
```typescript
function displayHandler(
handler?: Handler,
fallback = ""
): string
```
## Usage Examples
### Reading Handler Information
```typescript
const event = {
kind: 31990, // Handler Information kind
content: JSON.stringify({
name: "Note Viewer",
about: "Displays text notes with formatting",
image: "https://example.com/icon.png"
}),
tags: [
['k', '1'], // Handles kind 1 (text notes)
['d', 'note-viewer']
]
}
const handlers = readHandlers(event)
// Returns array of handlers defined in the event
```
### Working with Handlers
```typescript
// Get unique handler identifier
const key = getHandlerKey(handler)
// => "1:31990:pubkey:identifier" (handler-kind:address)
// where address is the "kind:pubkey:identifier" of the handler event
// Display handler name
const name = displayHandler(handler, "Unknown Handler")
// => "Note Viewer" or fallback if handler undefined
// Get handler address
const address = getHandlerAddress(event)
// Returns address from tags with 'web' marker or first address
```
## Complete Example
```typescript
// Process handler information event
function processHandlerEvent(event: TrustedEvent) {
// Read all handlers from event
const handlers = readHandlers(event)
// Process each handler
handlers.forEach(handler => {
// Generate unique key
const key = getHandlerKey(handler)
// Store handler information
handlerRegistry.set(key, {
name: handler.name,
kind: handler.kind,
about: handler.about,
image: handler.image,
website: handler.website,
address: getHandlerAddress(handler.event)
})
})
}
// Find handler for event kind
function findHandler(kind: number): Handler | undefined {
return Array.from(handlerRegistry.values())
.find(h => h.kind === kind)
}
// Display handler information
function renderHandler(handler: Handler) {
return {
title: displayHandler(handler, "Unknown"),
description: handler.about,
icon: handler.image,
website: handler.website || null
}
}
```
+3 -3
View File
@@ -2,20 +2,20 @@
[![version](https://badgen.net/npm/v/@welshman/util)](https://npmjs.com/package/@welshman/util)
A utility package for Nostr application development, providing essential tools and types for working with Nostr events, addresses, profiles, and more.
A utility package for Nostr application development, providing essential tools and types for working with Nostr events, addresses, filters, tags, and more.
## What's Included
- **Event Management**: Create, validate, and process Nostr events
- **Repository**: In-memory event storage with querying and indexing
- **Filters**: Advanced event filtering and subscription management
- **Profiles**: User profile handling and formatting
- **Lists**: Public and private list management
- **Zaps**: Lightning Network payment integration
- **Tags**: Comprehensive tag parsing and manipulation
- **Addresses**: NIP-19 address handling
- **Relays**: Relay URL handling, event dispatching and in-memory storage
> Note: profiles, lists, handlers, rooms, and event Reader/Builder helpers now live in [@welshman/domain](/domain/).
## Installation
```
-148
View File
@@ -1,148 +0,0 @@
# Lists
The Lists module provides utilities for working with Nostr lists, including both public and private lists (like bookmarks, mute lists, etc.). It handles list creation, encryption, and manipulation.
## Core Types
### List Parameters
```typescript
interface ListParams {
kind: number // List kind (e.g., 10000 for mutes)
}
```
### List Structure
```typescript
interface List extends ListParams {
publicTags: string[][] // Publicly visible tags
privateTags: string[][] // Encrypted tags
event?: DecryptedEvent // Original event if list exists
}
```
### Published List
```typescript
interface PublishedList extends List {
event: DecryptedEvent // Required event for published lists
}
```
## List Creation
### Create New List
```typescript
function makeList(list: ListParams & Partial<List>): List
// Example
const muteList = makeList({
kind: 10000,
publicTags: [['d', 'mutes']],
privateTags: [['p', 'pubkey1'], ['p', 'pubkey2']]
})
```
### Read Existing List
```typescript
function readList(event: DecryptedEvent): PublishedList
// Example
const list = readList(decryptedEvent)
```
## List Operations
### Get All Tags
```typescript
function getListTags(list: List | undefined): string[][]
// Example
const allTags = getListTags(list) // Combines public and private tags
```
### Remove Items
```typescript
// Remove by predicate
function removeFromListByPredicate(
list: List,
pred: (t: string[]) => boolean
): Encryptable
// Remove by value
function removeFromList(
list: List,
value: string
): Encryptable
```
### Add Items
```typescript
// Add public items
function addToListPublicly(
list: List,
...tags: string[][]
): Encryptable
// Add private items
function addToListPrivately(
list: List,
...tags: string[][]
): Encryptable
// Update list with new tags
function updateList(
list: List,
options: {publicTags?: string[][], privateTags?: string[][]}
): Encryptable
```
## Usage Examples
### Creating a Private List
```typescript
// Create new mute list
const muteList = makeList({
kind: 10000,
publicTags: [
['d', 'mutes'],
['name', 'My Mute List']
]
})
// Add items privately
const updated = addToListPrivately(
muteList,
['p', 'pubkey1'],
['p', 'pubkey2']
)
// Add new items publicly
const addItems = addToListPublicly(
list,
['p', 'pubkey3'],
['p', 'pubkey4']
)
// Encrypt and publish
const encrypted = await updated.reconcile(encrypt)
```
### Reading and Updating Lists
```typescript
// Read existing list
const list = readList(decryptedEvent)
// Remove item
const removeItem = removeFromList(list, 'pubkey1')
// Remove by predicate
const noMentions = removeFromListByPredicate(
list,
tag => tag[0] === 'p'
)
```
### Working with Tags
```typescript
// Get all list tags
const tags = getListTags(list)
```
-115
View File
@@ -1,115 +0,0 @@
# Profile
The Profile module provides utilities for handling Nostr user profiles (kind 0 events), including profile creation, reading, and display formatting.
## Core Types
### Profile Structure
```typescript
interface Profile {
name?: string // Display name
nip05?: string // NIP-05 verification
lud06?: string // Legacy Lightning address
lud16?: string // Lightning address
lnurl?: string // Lightning URL
about?: string // Bio/description
banner?: string // Banner image URL
picture?: string // Profile picture URL
website?: string // Website URL
display_name?: string // Alternative display name
event?: TrustedEvent // Original profile event
}
```
### Published Profile
```typescript
interface PublishedProfile extends Omit<Profile, "event"> {
event: TrustedEvent // Required event for published profiles
}
```
## Core Functions
### Profile Creation & Reading
```typescript
// Create new profile
function makeProfile(profile: Partial<Profile>): Profile
// Read profile from event
function readProfile(event: TrustedEvent): PublishedProfile
// Create profile event
function createProfile(profile: Profile): EventTemplate
// Edit existing profile
function editProfile(profile: PublishedProfile): EventTemplate
```
### Display Formatting
```typescript
// Format pubkey for display
function displayPubkey(pubkey: string): string
// Format profile name for display
function displayProfile(
profile?: Profile,
fallback = ""
): string
// Check if profile has name
function profileHasName(profile?: Profile): boolean
```
## Usage Examples
### Creating New Profile
```typescript
// Create basic profile
const profile = makeProfile({
name: "Alice",
about: "Nostr user",
picture: "https://example.com/avatar.jpg",
lud16: "alice@getalby.com"
})
// Create profile event
const profileEvent = createProfile(profile)
```
### Reading Profile
```typescript
// Read profile from event
const profile = readProfile(profileEvent)
// Access profile data
console.log(profile.name)
console.log(profile.about)
console.log(profile.lnurl) // Auto-generated from lud16/lud06
```
### Displaying Profile
```typescript
// Display profile name
const name = displayProfile(profile, "Anonymous")
// Display pubkey
const shortPubkey = displayPubkey(profile.event.pubkey)
// => "npub1abc...xyz"
// Check for name
if (profileHasName(profile)) {
showName(profile)
} else {
showPubkey(profile)
}
```
### Updating Profile
```typescript
// Edit existing profile
const profileEvent = editProfile({
...existingProfile,
name: "New Name",
about: "Updated bio"
})
```