Skip to content

Content Parser

The content parser system in @welshman/content provides a powerful way to parse Nostr content into structured elements. It handles various types of content including Nostr entities, links, code blocks, and special formats.

Content Types

Basic Types

typescript
enum ParsedType {
  Text = "text",         // Plain text
  Newline = "newline",   // Line breaks
  Topic = "topic",       // Hashtags (#nostr)
  Code = "code",         // Code blocks (inline and multi-line)
  Link = "link",         // URLs
  LinkGrid = "link-grid" // Grid of media links
}

Nostr-specific Types

typescript
enum ParsedType {
  Event = "event",       // Nostr events (note1/nevent1)
  Profile = "profile",   // Profiles (npub1/nprofile1)
  Address = "address",   // Addresses (naddr1)
}

Special Format Types

typescript
enum ParsedType {
  Cashu = "cashu",       // Cashu tokens
  Invoice = "invoice",    // Lightning invoices
  Ellipsis = "ellipsis"  // Truncation marker
}

Parsing Content

Main Parser

typescript
const parse = ({
  content = "",
  tags = []
}: {
  content?: string
  tags?: string[][]
}) => Parsed[]

// Example
const parsed = parse({
  content: "Hello #nostr, check nostr:npub1...",
  tags: [["p", "pubkey123"]]
})

Available Parsers

The system includes specialized parsers for each content type:

typescript
// Nostr Entities
parseAddress(text: string, context: ParseContext): ParsedAddress | void
parseEvent(text: string, context: ParseContext): ParsedEvent | void
parseProfile(text: string, context: ParseContext): ParsedProfile | void

// Code Blocks
parseCodeBlock(text: string, context: ParseContext): ParsedCode | void
parseCodeInline(text: string, context: ParseContext): ParsedCode | void

// Special Formats
parseCashu(text: string, context: ParseContext): ParsedCashu | void
parseInvoice(text: string, context: ParseContext): ParsedInvoice | void

// Basic Content
parseLink(text: string, context: ParseContext): ParsedLink | void
parseNewline(text: string, context: ParseContext): ParsedNewline | void
parseTopic(text: string, context: ParseContext): ParsedTopic | void

Content Processing

Truncation

typescript
type TruncateOpts = {
  minLength?: number    // Minimum content length (default: 500)
  maxLength?: number    // Maximum content length (default: 700)
  mediaLength?: number  // Length value for media items (default: 200)
  entityLength?: number // Length value for entities (default: 30)
}

const truncate = (
  content: Parsed[],
  options?: TruncateOpts
) => Parsed[]

// Example
const truncated = truncate(parsed, {
  maxLength: 1000,
  mediaLength: 150
})
typescript
// Consolidate consecutive image links into grids
const reduceLinks = (content: Parsed[]) => Parsed[]

// Example
const processed = reduceLinks(parsed)

Type Guards

typescript
// Basic content
isText(parsed: Parsed): parsed is ParsedText
isNewline(parsed: Parsed): parsed is ParsedNewline
isCode(parsed: Parsed): parsed is ParsedCode
isTopic(parsed: Parsed): parsed is ParsedTopic

// Links and media
isLink(parsed: Parsed): parsed is ParsedLink
isImage(parsed: Parsed): parsed is ParsedLink
isLinkGrid(parsed: Parsed): parsed is ParsedLinkGrid

// Nostr entities
isEvent(parsed: Parsed): parsed is ParsedEvent
isProfile(parsed: Parsed): parsed is ParsedProfile
isAddress(parsed: Parsed): parsed is ParsedAddress

// Special formats
isCashu(parsed: Parsed): parsed is ParsedCashu
isInvoice(parsed: Parsed): parsed is ParsedInvoice
isEllipsis(parsed: Parsed): parsed is ParsedEllipsis

Complete Example

typescript
// Parse content with tags
const parsed = parse({
  content: `
    Hello #nostr!

    Check out this note: nostr:note1...
    And this profile: nostr:npub1...

    Some code: \`console.log("hello")\`

    https://example.com/image.jpg
    https://example.com/image2.jpg
  `,
  tags: [
    ["p", "pubkey123"],
    ["e", "event456"]
  ]
})

// Process the content
const processed = reduceLinks(parsed)

// Truncate if needed
const final = truncate(processed, {
  maxLength: 500,
  mediaLength: 150
})

// Check types and handle accordingly
final.forEach(item => {
  if (isImage(item)) {
    // Handle image
  } else if (isProfile(item)) {
    // Handle profile reference
  } else if (isCode(item)) {
    // Handle code block
  }
})

This parser system provides a robust foundation for handling Nostr content, with support for various content types and processing needs. The type-safe approach ensures reliable content handling while maintaining flexibility for different use cases.