From 68dd33a8aaad572a11ad3cc45d6a9a45c28bf1f7 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 23 Jan 2026 09:02:19 +0000 Subject: [PATCH] fix: preserve $pubkey/$event/$relay variable literals in req-parser When creating parameterized spells, variable literals like $pubkey, $event, and $relay were being silently dropped because the parser only checked for $me and $contacts, then tried to parse other values as npub/hex. Changes: - Add $pubkey to -a/--author, -p, and -P flag parsing - Add $event to -e flag parsing - Add variable literal preservation to -d, -t, and --tag/-T flags - All three variables ($pubkey, $event, $relay) now preserved across all applicable filter types This ensures spell events properly encode the variable placeholders in their tags, allowing them to be substituted at runtime. --- src/lib/req-parser.ts | 84 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 68 insertions(+), 16 deletions(-) diff --git a/src/lib/req-parser.ts b/src/lib/req-parser.ts index d5ca744..8056985 100644 --- a/src/lib/req-parser.ts +++ b/src/lib/req-parser.ts @@ -130,7 +130,7 @@ export function parseReqCommand(args: string[]): ParsedReqCommand { case "-a": case "--author": { - // Support comma-separated authors: -a npub1...,npub2...,user@domain.com,@domain.com,$me,$contacts + // Support comma-separated authors: -a npub1...,npub2...,user@domain.com,@domain.com,$me,$contacts,$pubkey if (!nextArg) { i++; break; @@ -139,9 +139,13 @@ export function parseReqCommand(args: string[]): ParsedReqCommand { const values = nextArg.split(",").map((a) => a.trim()); for (const authorStr of values) { if (!authorStr) continue; - // Check for $me and $contacts aliases (case-insensitive) + // Check for variable literals (case-insensitive) const normalized = authorStr.toLowerCase(); - if (normalized === "$me" || normalized === "$contacts") { + if ( + normalized === "$me" || + normalized === "$contacts" || + normalized === "$pubkey" + ) { authors.add(normalized); addedAny = true; } else if (authorStr.startsWith("@")) { @@ -215,8 +219,7 @@ export function parseReqCommand(args: string[]): ParsedReqCommand { } case "-e": { - // Tag-based filtering: -e note1...,nevent1...,naddr1...,hex - // Events go to #e tag, addresses go to #a tag + // Support comma-separated event identifiers: -e note1...,nevent1...,naddr1...,hex,$event if (!nextArg) { i++; break; @@ -228,6 +231,14 @@ export function parseReqCommand(args: string[]): ParsedReqCommand { for (const val of values) { if (!val) continue; + // Check for $event variable literal (case-insensitive) + const normalized = val.toLowerCase(); + if (normalized === "$event") { + eventIds.add(normalized); + addedAny = true; + continue; + } + const parsed = parseEventIdentifier(val); if (parsed) { // Route to appropriate tag filter based on type @@ -251,7 +262,7 @@ export function parseReqCommand(args: string[]): ParsedReqCommand { } case "-p": { - // Support comma-separated pubkeys: -p npub1...,npub2...,user@domain.com,@domain.com,$me,$contacts + // Support comma-separated pubkeys: -p npub1...,npub2...,user@domain.com,@domain.com,$me,$contacts,$pubkey if (!nextArg) { i++; break; @@ -260,9 +271,13 @@ export function parseReqCommand(args: string[]): ParsedReqCommand { const values = nextArg.split(",").map((p) => p.trim()); for (const pubkeyStr of values) { if (!pubkeyStr) continue; - // Check for $me and $contacts aliases (case-insensitive) + // Check for variable literals (case-insensitive) const normalized = pubkeyStr.toLowerCase(); - if (normalized === "$me" || normalized === "$contacts") { + if ( + normalized === "$me" || + normalized === "$contacts" || + normalized === "$pubkey" + ) { pTags.add(normalized); addedAny = true; } else if (pubkeyStr.startsWith("@")) { @@ -294,7 +309,7 @@ export function parseReqCommand(args: string[]): ParsedReqCommand { case "-P": { // Uppercase P tag (e.g., zap sender in kind 9735) - // Support comma-separated pubkeys: -P npub1...,npub2...,@domain.com,$me,$contacts + // Support comma-separated pubkeys: -P npub1...,npub2...,@domain.com,$me,$contacts,$pubkey if (!nextArg) { i++; break; @@ -303,9 +318,13 @@ export function parseReqCommand(args: string[]): ParsedReqCommand { const values = nextArg.split(",").map((p) => p.trim()); for (const pubkeyStr of values) { if (!pubkeyStr) continue; - // Check for $me and $contacts aliases (case-insensitive) + // Check for variable literals (case-insensitive) const normalized = pubkeyStr.toLowerCase(); - if (normalized === "$me" || normalized === "$contacts") { + if ( + normalized === "$me" || + normalized === "$contacts" || + normalized === "$pubkey" + ) { pTagsUppercase.add(normalized); addedAny = true; } else if (pubkeyStr.startsWith("@")) { @@ -340,7 +359,18 @@ export function parseReqCommand(args: string[]): ParsedReqCommand { if (nextArg) { const addedAny = parseCommaSeparated( nextArg, - (v) => v, // hashtags are already strings + (v) => { + // Preserve variable literals + const normalized = v.toLowerCase(); + if ( + normalized === "$relay" || + normalized === "$event" || + normalized === "$pubkey" + ) { + return normalized; + } + return v; // hashtags are already strings + }, tTags, ); i += addedAny ? 2 : 1; @@ -351,11 +381,22 @@ export function parseReqCommand(args: string[]): ParsedReqCommand { } case "-d": { - // Support comma-separated d-tags: -d article1,article2,article3 + // Support comma-separated d-tags: -d article1,article2,article3,$relay if (nextArg) { const addedAny = parseCommaSeparated( nextArg, - (v) => v, // d-tags are already strings + (v) => { + // Preserve variable literals like $relay + const normalized = v.toLowerCase(); + if ( + normalized === "$relay" || + normalized === "$event" || + normalized === "$pubkey" + ) { + return normalized; + } + return v; // d-tags are already strings + }, dTags, ); i += addedAny ? 2 : 1; @@ -417,7 +458,7 @@ export function parseReqCommand(args: string[]): ParsedReqCommand { case "-T": case "--tag": { // Generic tag filter: --tag - // Supports comma-separated values: --tag a val1,val2 + // Supports comma-separated values: --tag a val1,val2,$relay,$event,$pubkey if (!nextArg) { i++; break; @@ -443,7 +484,18 @@ export function parseReqCommand(args: string[]): ParsedReqCommand { // Parse comma-separated values const addedAny = parseCommaSeparated( valueArg, - (v) => v, // tag values are already strings + (v) => { + // Preserve variable literals + const normalized = v.toLowerCase(); + if ( + normalized === "$relay" || + normalized === "$event" || + normalized === "$pubkey" + ) { + return normalized; + } + return v; // tag values are already strings + }, tagSet, );