Parse Nostr content into structured tokens.
npm install @konemono/nostr-content-parser
import {
parseContent,
parseContentAsync,
TokenType,
NIP19SubType,
} from "@konemono/nostr-content-parser";
const content = "Hello npub1xyz... Check :custom_emoji: #nostr";
const tags = [["emoji", "custom_emoji", "https://example.com/emoji.png"]];
// Synchronous parsing (recommended for most cases)
const tokens = parseContent(content, tags);
console.log(tokens);
// Asynchronous parsing with URL type detection
const tokensWithUrlTypes = await parseContentAsync(content, tags);
console.log(tokensWithUrlTypes);
TokenType.TEXT
- Plain textTokenType.NIP19
- NIP-19 entities (npub, nprofile, note, nevent, naddr, nsec)TokenType.URL
- URLsTokenType.CUSTOM_EMOJI
- Custom emojisTokenType.HASHTAG
- HashtagsTokenType.LN_ADDRESS
- Lightning addressesTokenType.LN_URL
- Lightning URLsTokenType.LNBC
- Lightning invoicesTokenType.EMAIL
- Email addressesTokenType.BITCOIN_ADDRESS
- Bitcoin addressesTokenType.CASHU_TOKEN
- Cashu tokensTokenType.NIP_IDENTIFIER
- NIP identifiers
NIP19SubType.NPUB
- Public keyNIP19SubType.NPROFILE
- ProfileNIP19SubType.NOTE
- NoteNIP19SubType.NEVENT
- EventNIP19SubType.NADDR
- AddressNIP19SubType.NSEC
- Secret key
Synchronous parsing - Recommended for most use cases.
Parameters:
-
content: string
– Input content to parse. -
tags: string[][]
– Optional tag array (used for custom emoji, etc). -
options: object
– Optional settings:includeNostrPrefixOnly?: boolean
Iftrue
(default), only tokens starting withnostr:
will be included for NIP-19. Iffalse
, plain NIP-19 tokens (without prefix) will also be parsed.hashtagsFromTagsOnly?: boolean
Iftrue
(default), only hashtags that match at
tag will be parsed as hashtags. Iffalse
, all#
-prefixed words are treated as hashtags.
Returns: Token[]
URL type detection is performed based on file extensions only (fast and lightweight).
Asynchronous parsing - Use when you need comprehensive URL type detection.
Parameters:
-
content: string
– Input content to parse. -
tags: string[][]
– Optional tag array (used for custom emoji, etc). -
options: object
– Optional settings:includeNostrPrefixOnly?: boolean
(same as sync version)hashtagsFromTagsOnly?: boolean
(same as sync version)
Returns: Promise<Token[]>
URL type detection includes HTTP HEAD requests to determine content type when file extension is not available.
filterTokens(tokens, types)
- Filter tokens by typefilterTokensBy(tokens, predicate)
- Filter tokens by custom predicate
getNip19Entities(tokens)
- Get all NIP-19 entitiesfilterNip19BySubType(tokens, subType)
- Filter NIP-19 by sub typegetNpubs(tokens)
- Get npub tokensgetNprofiles(tokens)
- Get nprofile tokensgetNotes(tokens)
- Get note tokensgetNevents(tokens)
- Get nevent tokensgetNaddrs(tokens)
- Get naddr tokensgetNsecs(tokens)
- Get nsec tokens
getUrls(tokens)
- Get URLsgetCustomEmojis(tokens)
- Get custom emojisgetHashtags(tokens)
- Get hashtagsgetLightningAddresses(tokens)
- Get Lightning addressesgetLightningUrls(tokens)
- Get Lightning URLsgetLightningInvoices(tokens)
- Get Lightning invoicesgetBitcoinAddresses(tokens)
- Get Bitcoin addressesgetCashuTokens(tokens)
- Get Cashu tokensgetEmails(tokens)
- Get email addressesgetNipIdentifiers(tokens)
- Get NIP identifiers
resetPatterns()
- Reset regex patterns (call if needed)
// Fast, synchronous parsing
const tokens = parseContent("Check out npub1xyz... and #nostr!");
// Returns tokens with NIP19 and HASHTAG types
// Comprehensive URL type detection
const content =
"Check this image: https://example.com/photo and https://example.com/video.mp4";
const tokens = await parseContentAsync(content);
const urls = getUrls(tokens);
urls.forEach((url) => {
console.log(`URL: ${url.content}`);
console.log(`Scheme: ${url.metadata.scheme}`); // "https", "http"
console.log(`Type: ${url.metadata.type}`); // "image", "video", "audio" (if detected)
});
const tokens = parseContent("Check nostr:npub1xyz... and note1abc...", [], {
includeNostrPrefixOnly: false, // Include plain NIP19 tokens
});
const nip19Tokens = getNip19Entities(tokens);
nip19Tokens.forEach((token) => {
console.log(`Type: ${token.metadata.subType}`); // "npub", "note", etc.
console.log(`Has nostr: prefix: ${token.metadata.hasNostrPrefix}`);
console.log(`Plain NIP19: ${token.metadata.plainNip19}`);
});
// Filter specific NIP19 types
const npubs = getNpubs(tokens);
const notes = filterNip19BySubType(tokens, NIP19SubType.NOTE);
const content = "#nostr #dev";
const tags = [["t", "nostr"]];
const tokens = parseContent(content, tags, {
hashtagsFromTagsOnly: true,
});
// Only "#nostr" will be returned as a HASHTAG token
const content = "Hello :custom_emoji:!";
const tags = [["emoji", "custom_emoji", "https://example.com/emoji.png"]];
const tokens = parseContent(content, tags);
const emojis = getCustomEmojis(tokens);
emojis.forEach((emoji) => {
console.log(`Name: ${emoji.metadata.name}`);
console.log(`URL: ${emoji.metadata.url}`);
});
// Fast: Extension-based URL type detection only
const fastTokens = parseContent(content);
// Comprehensive: Includes HTTP requests for unknown URLs
const detailedTokens = await parseContentAsync(content);
Each token has the following structure:
interface Token {
type: TokenType;
content: string;
start: number;
end: number;
metadata: Record<string, unknown>;
}
NIP19 Token:
{
subType: "npub",
hasNostrPrefix: true,
plainNip19: "npub1xyz..."
}
Custom Emoji Token:
{
name: "custom_emoji",
url: "https://example.com/emoji.png"
}
URL Token:
{
scheme: "https",
type: "image" // Only present if detected
}
# Run tests
npm test
# Run tests once
npm run test:run
# Build
npm run build
# Pre-publish check
npm run prepublishOnly
# Publish to npm
npm publish