mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-06-16 17:48:34 +02:00
refactor: simplify spell parameter resolution to allow $me and $contacts together
Changed how non-parameterized spells with $me and $contacts are applied:
- $me now resolves to target pubkey
- $contacts now resolves to target pubkey's contacts
- Spells can now use both $me and $contacts in the same filter
Breaking change: applySpellParameters now uses context object instead of array
- Old: applySpellParameters(parsed, [pubkey])
- New: applySpellParameters(parsed, { targetPubkey, targetContacts, targetEventId, targetRelay })
- Simplified useParameterizedSpells to allow both $me and $contacts
- Added contact list fetching in ProfileViewer using kind 3 events
- Updated all viewers (Profile, Event, Relay) to use new API
- Updated all 87 tests in spell-conversion.test.ts
- All 1017 tests passing
This commit is contained in:
@@ -129,9 +129,11 @@ function SpellTabContent({
|
||||
if (!parsed || !targetEventId) return null;
|
||||
|
||||
try {
|
||||
const applied = applySpellParameters(parsed, [targetEventId]);
|
||||
const applied = applySpellParameters(parsed, {
|
||||
targetEventId,
|
||||
});
|
||||
console.log(`[EventSpell:${spell.name || spellId}] Applied parameters:`, {
|
||||
input: targetEventId,
|
||||
targetEventId,
|
||||
result: applied,
|
||||
});
|
||||
return applied;
|
||||
|
||||
@@ -70,6 +70,41 @@ function SpellTabContent({
|
||||
targetPubkey,
|
||||
}: SpellTabContentProps) {
|
||||
const { state } = useGrimoire();
|
||||
const eventStore = useEventStore();
|
||||
|
||||
// Fetch target pubkey's contacts (kind 3 contact list)
|
||||
const targetContacts = useMemo(() => {
|
||||
if (!targetPubkey) return [];
|
||||
|
||||
try {
|
||||
const contactListEvent = eventStore.replaceable(
|
||||
kinds.Contacts,
|
||||
targetPubkey,
|
||||
);
|
||||
if (!contactListEvent) return [];
|
||||
|
||||
// Extract pubkeys from p tags
|
||||
const contacts = contactListEvent.tags
|
||||
.filter((tag) => tag[0] === "p" && tag[1])
|
||||
.map((tag) => tag[1]);
|
||||
|
||||
console.log(
|
||||
`[SpellTabContent:${spell.name || spellId}] Target contacts:`,
|
||||
{
|
||||
count: contacts.length,
|
||||
targetPubkey,
|
||||
},
|
||||
);
|
||||
|
||||
return contacts;
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`[SpellTabContent:${spell.name || spellId}] Failed to fetch contacts:`,
|
||||
error,
|
||||
);
|
||||
return [];
|
||||
}
|
||||
}, [targetPubkey, eventStore, spell.name, spellId]);
|
||||
|
||||
// Parse spell and get filter - handle both published (with event) and local (command-only) spells
|
||||
const parsed = useMemo(() => {
|
||||
@@ -149,11 +184,15 @@ function SpellTabContent({
|
||||
if (!parsed || !targetPubkey) return null;
|
||||
|
||||
try {
|
||||
const applied = applySpellParameters(parsed, [targetPubkey]);
|
||||
const applied = applySpellParameters(parsed, {
|
||||
targetPubkey,
|
||||
targetContacts,
|
||||
});
|
||||
console.log(
|
||||
`[SpellTabContent:${spell.name || spellId}] Applied parameters:`,
|
||||
{
|
||||
input: targetPubkey,
|
||||
targetPubkey,
|
||||
targetContactsCount: targetContacts.length,
|
||||
result: applied,
|
||||
},
|
||||
);
|
||||
@@ -165,7 +204,7 @@ function SpellTabContent({
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}, [parsed, targetPubkey, spell.name, spellId]);
|
||||
}, [parsed, targetPubkey, targetContacts, spell.name, spellId]);
|
||||
|
||||
// Resolve relays - use explicit relays from spell, or use NIP-65 outbox selection
|
||||
const fallbackRelays = useMemo(
|
||||
|
||||
@@ -115,9 +115,11 @@ function SpellTabContent({
|
||||
if (!parsed || !targetRelay) return null;
|
||||
|
||||
try {
|
||||
const applied = applySpellParameters(parsed, [targetRelay]);
|
||||
const applied = applySpellParameters(parsed, {
|
||||
targetRelay,
|
||||
});
|
||||
console.log(`[RelaySpell:${spell.name || spellId}] Applied parameters:`, {
|
||||
input: targetRelay,
|
||||
targetRelay,
|
||||
result: applied,
|
||||
});
|
||||
return applied;
|
||||
|
||||
@@ -105,22 +105,10 @@ export function useParameterizedSpells(
|
||||
// Include spells with $me or $contacts that could be parameterized
|
||||
if (!spell.parameterType) {
|
||||
const cmd = spell.command.toLowerCase();
|
||||
// Check if command uses ONLY $me or ONLY $contacts (not both, not mixed with other pubkeys)
|
||||
// Just check if command uses $me or $contacts - we'll resolve them at runtime
|
||||
const hasMeOrContacts =
|
||||
cmd.includes("$me") || cmd.includes("$contacts");
|
||||
if (!hasMeOrContacts) return false;
|
||||
|
||||
// Make sure it doesn't use multiple different variables
|
||||
const hasBothMeAndContacts =
|
||||
cmd.includes("$me") && cmd.includes("$contacts");
|
||||
if (hasBothMeAndContacts) return false; // Can't have both
|
||||
|
||||
// Check if it has hex pubkeys mixed with $me/$contacts
|
||||
// This is a rough check - looking for hex strings in -a or #p tags
|
||||
const hasHexPubkey = /(?:-a|#p)\s+[a-f0-9]{64}/.test(cmd);
|
||||
if (hasHexPubkey) return false; // Mixed pubkeys, not eligible
|
||||
|
||||
return true;
|
||||
return hasMeOrContacts;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -1014,24 +1014,11 @@ describe("Spell Conversion", () => {
|
||||
} as any;
|
||||
|
||||
const hex = "a".repeat(64);
|
||||
const result = applySpellParameters(parsed, [hex]);
|
||||
const result = applySpellParameters(parsed, { targetPubkey: hex });
|
||||
|
||||
expect(result.authors).toEqual([hex]);
|
||||
});
|
||||
|
||||
it("should substitute $pubkey with multiple values", () => {
|
||||
const parsed = {
|
||||
filter: { kinds: [1], authors: ["$pubkey"] },
|
||||
parameter: { type: "$pubkey" as const },
|
||||
} as any;
|
||||
|
||||
const hex1 = "a".repeat(64);
|
||||
const hex2 = "b".repeat(64);
|
||||
const result = applySpellParameters(parsed, [hex1, hex2]);
|
||||
|
||||
expect(result.authors).toEqual([hex1, hex2]);
|
||||
});
|
||||
|
||||
it("should substitute $pubkey in #p tag filters", () => {
|
||||
const parsed = {
|
||||
filter: { kinds: [1], "#p": ["$pubkey"] },
|
||||
@@ -1039,7 +1026,7 @@ describe("Spell Conversion", () => {
|
||||
} as any;
|
||||
|
||||
const hex = "c".repeat(64);
|
||||
const result = applySpellParameters(parsed, [hex]);
|
||||
const result = applySpellParameters(parsed, { targetPubkey: hex });
|
||||
|
||||
expect(result["#p"]).toEqual([hex]);
|
||||
});
|
||||
@@ -1051,7 +1038,7 @@ describe("Spell Conversion", () => {
|
||||
} as any;
|
||||
|
||||
const hex = "d".repeat(64);
|
||||
const result = applySpellParameters(parsed, [hex]);
|
||||
const result = applySpellParameters(parsed, { targetPubkey: hex });
|
||||
|
||||
expect(result["#P"]).toEqual([hex]);
|
||||
});
|
||||
@@ -1064,19 +1051,19 @@ describe("Spell Conversion", () => {
|
||||
parameter: { type: "$pubkey" as const },
|
||||
} as any;
|
||||
|
||||
const result = applySpellParameters(parsed, [hex2]);
|
||||
const result = applySpellParameters(parsed, { targetPubkey: hex2 });
|
||||
|
||||
expect(result.authors).toEqual([hex1, hex2]);
|
||||
});
|
||||
|
||||
it("should use default values when no args provided", () => {
|
||||
it("should use default values when no target provided", () => {
|
||||
const hex = "a".repeat(64);
|
||||
const parsed = {
|
||||
filter: { kinds: [1], authors: ["$pubkey"] },
|
||||
parameter: { type: "$pubkey" as const, default: [hex] },
|
||||
} as any;
|
||||
|
||||
const result = applySpellParameters(parsed, []);
|
||||
const result = applySpellParameters(parsed, {});
|
||||
|
||||
expect(result.authors).toEqual([hex]);
|
||||
});
|
||||
@@ -1090,7 +1077,9 @@ describe("Spell Conversion", () => {
|
||||
} as any;
|
||||
|
||||
const eventId = "abc123def456";
|
||||
const result = applySpellParameters(parsed, [eventId]);
|
||||
const result = applySpellParameters(parsed, {
|
||||
targetEventId: eventId,
|
||||
});
|
||||
|
||||
expect(result["#e"]).toEqual([eventId]);
|
||||
});
|
||||
@@ -1102,7 +1091,7 @@ describe("Spell Conversion", () => {
|
||||
} as any;
|
||||
|
||||
const addr = "30023:pubkey:article";
|
||||
const result = applySpellParameters(parsed, [addr]);
|
||||
const result = applySpellParameters(parsed, { targetEventId: addr });
|
||||
|
||||
expect(result["#a"]).toEqual([addr]);
|
||||
});
|
||||
@@ -1114,23 +1103,12 @@ describe("Spell Conversion", () => {
|
||||
} as any;
|
||||
|
||||
const eventId = "abc123def456";
|
||||
const result = applySpellParameters(parsed, [eventId]);
|
||||
const result = applySpellParameters(parsed, {
|
||||
targetEventId: eventId,
|
||||
});
|
||||
|
||||
expect(result.ids).toEqual([eventId]);
|
||||
});
|
||||
|
||||
it("should substitute $event with multiple values", () => {
|
||||
const parsed = {
|
||||
filter: { kinds: [1], "#e": ["$event"] },
|
||||
parameter: { type: "$event" as const },
|
||||
} as any;
|
||||
|
||||
const event1 = "abc123";
|
||||
const event2 = "def456";
|
||||
const result = applySpellParameters(parsed, [event1, event2]);
|
||||
|
||||
expect(result["#e"]).toEqual([event1, event2]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("$relay parameters", () => {
|
||||
@@ -1141,23 +1119,10 @@ describe("Spell Conversion", () => {
|
||||
} as any;
|
||||
|
||||
const relay = "wss://relay.example.com/";
|
||||
const result = applySpellParameters(parsed, [relay]);
|
||||
const result = applySpellParameters(parsed, { targetRelay: relay });
|
||||
|
||||
expect(result["#r"]).toEqual([relay]);
|
||||
});
|
||||
|
||||
it("should substitute $relay with multiple values", () => {
|
||||
const parsed = {
|
||||
filter: { kinds: [1], "#r": ["$relay"] },
|
||||
parameter: { type: "$relay" as const },
|
||||
} as any;
|
||||
|
||||
const relay1 = "wss://relay1.com/";
|
||||
const relay2 = "wss://relay2.com/";
|
||||
const result = applySpellParameters(parsed, [relay1, relay2]);
|
||||
|
||||
expect(result["#r"]).toEqual([relay1, relay2]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Edge cases", () => {
|
||||
@@ -1166,7 +1131,9 @@ describe("Spell Conversion", () => {
|
||||
filter: { kinds: [1], authors: ["abc123"] },
|
||||
} as any;
|
||||
|
||||
const result = applySpellParameters(parsed, ["def456"]);
|
||||
const result = applySpellParameters(parsed, {
|
||||
targetPubkey: "def456",
|
||||
});
|
||||
|
||||
expect(result).toEqual({ kinds: [1], authors: ["abc123"] });
|
||||
});
|
||||
@@ -1177,8 +1144,8 @@ describe("Spell Conversion", () => {
|
||||
parameter: { type: "$pubkey" as const },
|
||||
} as any;
|
||||
|
||||
expect(() => applySpellParameters(parsed, [])).toThrow(
|
||||
"Parameterized spell requires $pubkey argument(s)",
|
||||
expect(() => applySpellParameters(parsed, {})).toThrow(
|
||||
"Parameterized $pubkey spell requires target pubkey",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1190,7 +1157,7 @@ describe("Spell Conversion", () => {
|
||||
} as any;
|
||||
|
||||
const hex = "a".repeat(64);
|
||||
applySpellParameters(parsed, [hex]);
|
||||
applySpellParameters(parsed, { targetPubkey: hex });
|
||||
|
||||
// Original should be unchanged
|
||||
expect(original.authors).toEqual(["$pubkey"]);
|
||||
@@ -1203,7 +1170,7 @@ describe("Spell Conversion", () => {
|
||||
} as any;
|
||||
|
||||
const hex = "a".repeat(64);
|
||||
const result = applySpellParameters(parsed, [hex]);
|
||||
const result = applySpellParameters(parsed, { targetPubkey: hex });
|
||||
|
||||
expect(result.authors).toEqual([]);
|
||||
});
|
||||
@@ -1215,12 +1182,86 @@ describe("Spell Conversion", () => {
|
||||
} as any;
|
||||
|
||||
const hex = "a".repeat(64);
|
||||
const result = applySpellParameters(parsed, [hex]);
|
||||
const result = applySpellParameters(parsed, { targetPubkey: hex });
|
||||
|
||||
expect(result.authors).toBeUndefined();
|
||||
expect(result.kinds).toEqual([1]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Implicit $me and $contacts resolution", () => {
|
||||
it("should resolve $me to targetPubkey", () => {
|
||||
const parsed = {
|
||||
filter: { kinds: [1], authors: ["$me"] },
|
||||
} as any;
|
||||
|
||||
const targetPubkey = "a".repeat(64);
|
||||
const result = applySpellParameters(parsed, { targetPubkey });
|
||||
|
||||
expect(result.authors).toEqual([targetPubkey]);
|
||||
});
|
||||
|
||||
it("should resolve $contacts to targetContacts array", () => {
|
||||
const parsed = {
|
||||
filter: { kinds: [1], authors: ["$contacts"] },
|
||||
} as any;
|
||||
|
||||
const contact1 = "a".repeat(64);
|
||||
const contact2 = "b".repeat(64);
|
||||
const result = applySpellParameters(parsed, {
|
||||
targetContacts: [contact1, contact2],
|
||||
});
|
||||
|
||||
expect(result.authors).toEqual([contact1, contact2]);
|
||||
});
|
||||
|
||||
it("should resolve both $me and $contacts in same filter", () => {
|
||||
const parsed = {
|
||||
filter: { kinds: [1], authors: ["$me", "$contacts"] },
|
||||
} as any;
|
||||
|
||||
const targetPubkey = "a".repeat(64);
|
||||
const contact1 = "b".repeat(64);
|
||||
const contact2 = "c".repeat(64);
|
||||
const result = applySpellParameters(parsed, {
|
||||
targetPubkey,
|
||||
targetContacts: [contact1, contact2],
|
||||
});
|
||||
|
||||
expect(result.authors).toEqual([targetPubkey, contact1, contact2]);
|
||||
});
|
||||
|
||||
it("should resolve $me and $contacts in #p tags", () => {
|
||||
const parsed = {
|
||||
filter: { kinds: [1], "#p": ["$me", "$contacts"] },
|
||||
} as any;
|
||||
|
||||
const targetPubkey = "a".repeat(64);
|
||||
const contact1 = "b".repeat(64);
|
||||
const result = applySpellParameters(parsed, {
|
||||
targetPubkey,
|
||||
targetContacts: [contact1],
|
||||
});
|
||||
|
||||
expect(result["#p"]).toEqual([targetPubkey, contact1]);
|
||||
});
|
||||
|
||||
it("should preserve other values when resolving $me/$contacts", () => {
|
||||
const otherPubkey = "z".repeat(64);
|
||||
const parsed = {
|
||||
filter: { kinds: [1], authors: [otherPubkey, "$me", "$contacts"] },
|
||||
} as any;
|
||||
|
||||
const targetPubkey = "a".repeat(64);
|
||||
const contact = "b".repeat(64);
|
||||
const result = applySpellParameters(parsed, {
|
||||
targetPubkey,
|
||||
targetContacts: [contact],
|
||||
});
|
||||
|
||||
expect(result.authors).toEqual([otherPubkey, targetPubkey, contact]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Round-trip with parameters", () => {
|
||||
@@ -1284,11 +1325,11 @@ describe("Spell Conversion", () => {
|
||||
// Filter should now have $pubkey placeholder
|
||||
expect(decoded.filter.authors).toEqual(["$pubkey"]);
|
||||
|
||||
// Apply with arguments
|
||||
const filter = applySpellParameters(decoded, [hex1, hex2]);
|
||||
// Apply with single target pubkey
|
||||
const filter = applySpellParameters(decoded, { targetPubkey: hex1 });
|
||||
|
||||
expect(filter.kinds).toEqual([1]);
|
||||
expect(filter.authors).toEqual([hex1, hex2]);
|
||||
expect(filter.authors).toEqual([hex1]);
|
||||
});
|
||||
|
||||
it("should handle complex parameterized spell", () => {
|
||||
@@ -1316,7 +1357,7 @@ describe("Spell Conversion", () => {
|
||||
expect(decoded.filter.authors).toEqual(["$pubkey"]);
|
||||
|
||||
const hex = "c".repeat(64);
|
||||
const filter = applySpellParameters(decoded, [hex]);
|
||||
const filter = applySpellParameters(decoded, { targetPubkey: hex });
|
||||
|
||||
expect(filter.kinds).toEqual([1, 30023]);
|
||||
expect(filter.authors).toEqual([hex]);
|
||||
|
||||
@@ -506,115 +506,137 @@ export function reconstructCommand(
|
||||
* Apply parameter values to a parameterized spell
|
||||
* Substitutes parameter placeholders with actual values
|
||||
*
|
||||
* @param parsed - Parsed spell (must have parameter configuration)
|
||||
* @param args - Arguments to substitute (if empty, uses defaults)
|
||||
* For $pubkey spells:
|
||||
* - $me is replaced with targetPubkey
|
||||
* - $contacts is replaced with targetContacts
|
||||
* - $pubkey is replaced with targetPubkey (for explicitly parameterized spells)
|
||||
*
|
||||
* @param parsed - Parsed spell
|
||||
* @param context - Context for substitution (pubkey, contacts, or event/relay IDs)
|
||||
* @returns Filter with parameters applied
|
||||
*/
|
||||
export function applySpellParameters(
|
||||
parsed: Pick<ParsedSpell, "filter" | "parameter">,
|
||||
args: string[] = [],
|
||||
context: {
|
||||
targetPubkey?: string;
|
||||
targetContacts?: string[];
|
||||
targetEventId?: string;
|
||||
targetRelay?: string;
|
||||
} = {},
|
||||
): NostrFilter {
|
||||
if (!parsed.parameter) {
|
||||
// Not an explicitly parameterized spell
|
||||
// Check if we have args and the filter uses $me or $contacts (implicit parameterization)
|
||||
if (args.length > 0) {
|
||||
const filter: NostrFilter = { ...parsed.filter };
|
||||
|
||||
// Substitute $me and $contacts with provided pubkey(s)
|
||||
if (filter.authors) {
|
||||
filter.authors = filter.authors.flatMap((author) =>
|
||||
author === "$me" || author === "$contacts" ? args : [author],
|
||||
);
|
||||
}
|
||||
|
||||
if (filter["#p"]) {
|
||||
filter["#p"] = filter["#p"].flatMap((p) =>
|
||||
p === "$me" || p === "$contacts" ? args : [p],
|
||||
);
|
||||
}
|
||||
|
||||
if (filter["#P"]) {
|
||||
filter["#P"] = filter["#P"].flatMap((p) =>
|
||||
p === "$me" || p === "$contacts" ? args : [p],
|
||||
);
|
||||
}
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
// No parameter and no args, return filter as-is
|
||||
return parsed.filter;
|
||||
}
|
||||
|
||||
// Use provided args or fall back to defaults
|
||||
const values = args.length > 0 ? args : parsed.parameter.default || [];
|
||||
|
||||
if (values.length === 0) {
|
||||
throw new Error(
|
||||
`Parameterized spell requires ${parsed.parameter.type} argument(s)`,
|
||||
);
|
||||
}
|
||||
const {
|
||||
targetPubkey,
|
||||
targetContacts = [],
|
||||
targetEventId,
|
||||
targetRelay,
|
||||
} = context;
|
||||
|
||||
// Clone the filter
|
||||
const filter: NostrFilter = { ...parsed.filter };
|
||||
|
||||
// Apply substitution based on parameter type
|
||||
switch (parsed.parameter.type) {
|
||||
case "$pubkey":
|
||||
// Substitute in authors array
|
||||
if (filter.authors) {
|
||||
filter.authors = filter.authors.flatMap((author) =>
|
||||
author === "$pubkey" ? values : [author],
|
||||
);
|
||||
// Handle explicitly parameterized spells
|
||||
if (parsed.parameter) {
|
||||
switch (parsed.parameter.type) {
|
||||
case "$pubkey": {
|
||||
const values = targetPubkey
|
||||
? [targetPubkey]
|
||||
: parsed.parameter.default || [];
|
||||
if (values.length === 0) {
|
||||
throw new Error("Parameterized $pubkey spell requires target pubkey");
|
||||
}
|
||||
|
||||
// Substitute $pubkey placeholders
|
||||
if (filter.authors) {
|
||||
filter.authors = filter.authors.flatMap((author) =>
|
||||
author === "$pubkey" ? values : [author],
|
||||
);
|
||||
}
|
||||
if (filter["#p"]) {
|
||||
filter["#p"] = filter["#p"].flatMap((p) =>
|
||||
p === "$pubkey" ? values : [p],
|
||||
);
|
||||
}
|
||||
if (filter["#P"]) {
|
||||
filter["#P"] = filter["#P"].flatMap((p) =>
|
||||
p === "$pubkey" ? values : [p],
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Substitute in #p tag filters
|
||||
if (filter["#p"]) {
|
||||
filter["#p"] = filter["#p"].flatMap((p) =>
|
||||
p === "$pubkey" ? values : [p],
|
||||
);
|
||||
case "$event": {
|
||||
const values = targetEventId
|
||||
? [targetEventId]
|
||||
: parsed.parameter.default || [];
|
||||
if (values.length === 0) {
|
||||
throw new Error(
|
||||
"Parameterized $event spell requires target event ID",
|
||||
);
|
||||
}
|
||||
|
||||
if (filter["#e"]) {
|
||||
filter["#e"] = filter["#e"].flatMap((e) =>
|
||||
e === "$event" ? values : [e],
|
||||
);
|
||||
}
|
||||
if (filter["#a"]) {
|
||||
filter["#a"] = filter["#a"].flatMap((a) =>
|
||||
a === "$event" ? values : [a],
|
||||
);
|
||||
}
|
||||
if (filter.ids) {
|
||||
filter.ids = filter.ids.flatMap((id) =>
|
||||
id === "$event" ? values : [id],
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Substitute in #P tag filters
|
||||
if (filter["#P"]) {
|
||||
filter["#P"] = filter["#P"].flatMap((p) =>
|
||||
p === "$pubkey" ? values : [p],
|
||||
);
|
||||
}
|
||||
break;
|
||||
case "$relay": {
|
||||
const values = targetRelay
|
||||
? [targetRelay]
|
||||
: parsed.parameter.default || [];
|
||||
if (values.length === 0) {
|
||||
throw new Error("Parameterized $relay spell requires target relay");
|
||||
}
|
||||
|
||||
case "$event":
|
||||
// Substitute in #e tag filters
|
||||
if (filter["#e"]) {
|
||||
filter["#e"] = filter["#e"].flatMap((e) =>
|
||||
e === "$event" ? values : [e],
|
||||
);
|
||||
if (filter["#r"]) {
|
||||
filter["#r"] = filter["#r"].flatMap((r) =>
|
||||
r === "$relay" ? values : [r],
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Substitute in #a tag filters
|
||||
if (filter["#a"]) {
|
||||
filter["#a"] = filter["#a"].flatMap((a) =>
|
||||
a === "$event" ? values : [a],
|
||||
);
|
||||
}
|
||||
// Handle implicit parameterization ($me and $contacts)
|
||||
// $me → targetPubkey
|
||||
// $contacts → targetContacts
|
||||
if (targetPubkey || targetContacts.length > 0) {
|
||||
if (filter.authors) {
|
||||
filter.authors = filter.authors.flatMap((author) => {
|
||||
if (author === "$me") return targetPubkey ? [targetPubkey] : [];
|
||||
if (author === "$contacts") return targetContacts;
|
||||
return [author];
|
||||
});
|
||||
}
|
||||
|
||||
// Substitute in ids array
|
||||
if (filter.ids) {
|
||||
filter.ids = filter.ids.flatMap((id) =>
|
||||
id === "$event" ? values : [id],
|
||||
);
|
||||
}
|
||||
break;
|
||||
if (filter["#p"]) {
|
||||
filter["#p"] = filter["#p"].flatMap((p) => {
|
||||
if (p === "$me") return targetPubkey ? [targetPubkey] : [];
|
||||
if (p === "$contacts") return targetContacts;
|
||||
return [p];
|
||||
});
|
||||
}
|
||||
|
||||
case "$relay":
|
||||
// Relay parameters are handled differently
|
||||
// They could affect relay hints or #r tag filters
|
||||
if (filter["#r"]) {
|
||||
filter["#r"] = filter["#r"].flatMap((r) =>
|
||||
r === "$relay" ? values : [r],
|
||||
);
|
||||
}
|
||||
break;
|
||||
if (filter["#P"]) {
|
||||
filter["#P"] = filter["#P"].flatMap((p) => {
|
||||
if (p === "$me") return targetPubkey ? [targetPubkey] : [];
|
||||
if (p === "$contacts") return targetContacts;
|
||||
return [p];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return filter;
|
||||
|
||||
Reference in New Issue
Block a user