mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-04-11 16:07:15 +02:00
feat: add @domain alias for NIP-05 domain directory resolution (#136)
Add support for @domain syntax in req and count commands to query all users from a domain's NIP-05 directory (e.g., @habla.news). Features: - Fetches /.well-known/nostr.json from domain - Extracts all pubkeys from the names object - Works with -a (authors), -p (#p tags), and -P (#P tags) flags - Supports mixed usage with npub, hex, NIP-05, $me, $contacts - 5-minute caching for domain lookups - UI display in ReqViewer query dropdown Implementation: - Added resolveDomainDirectory and resolveDomainDirectoryBatch to nip05.ts - Updated req-parser and count-parser to detect @domain syntax - Updated argParsers in man.ts to resolve domains asynchronously - Updated ReqViewer to display queried domains in dropdown - Added comprehensive tests for domain resolution Examples: - req -k 1 -a @habla.news - req -k 7 -p @nostr.band - count relay.damus.io -k 1 -a @getcurrent.io Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -97,6 +97,8 @@ interface ReqViewerProps {
|
||||
view?: ViewMode;
|
||||
nip05Authors?: string[];
|
||||
nip05PTags?: string[];
|
||||
domainAuthors?: string[];
|
||||
domainPTags?: string[];
|
||||
needsAccount?: boolean;
|
||||
title?: string;
|
||||
}
|
||||
@@ -105,9 +107,16 @@ interface QueryDropdownProps {
|
||||
filter: NostrFilter;
|
||||
nip05Authors?: string[];
|
||||
nip05PTags?: string[];
|
||||
domainAuthors?: string[];
|
||||
domainPTags?: string[];
|
||||
}
|
||||
|
||||
function QueryDropdown({ filter, nip05Authors }: QueryDropdownProps) {
|
||||
function QueryDropdown({
|
||||
filter,
|
||||
nip05Authors,
|
||||
domainAuthors,
|
||||
domainPTags,
|
||||
}: QueryDropdownProps) {
|
||||
const { copy: handleCopy, copied } = useCopy();
|
||||
|
||||
// Expandable lists state
|
||||
@@ -271,6 +280,13 @@ function QueryDropdown({ filter, nip05Authors }: QueryDropdownProps) {
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{domainAuthors && domainAuthors.length > 0 && (
|
||||
<div className="text-xs space-y-0.5 text-muted-foreground">
|
||||
{domainAuthors.map((domain) => (
|
||||
<div key={domain}>→ @{domain}</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
@@ -311,6 +327,13 @@ function QueryDropdown({ filter, nip05Authors }: QueryDropdownProps) {
|
||||
: `Show all ${pTagPubkeys.length}`}
|
||||
</button>
|
||||
)}
|
||||
{domainPTags && domainPTags.length > 0 && (
|
||||
<div className="text-xs space-y-0.5 text-muted-foreground">
|
||||
{domainPTags.map((domain) => (
|
||||
<div key={domain}>→ @{domain}</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
@@ -610,6 +633,8 @@ export default function ReqViewer({
|
||||
view = "list",
|
||||
nip05Authors,
|
||||
nip05PTags,
|
||||
domainAuthors,
|
||||
domainPTags,
|
||||
needsAccount = false,
|
||||
title = "nostr-events",
|
||||
}: ReqViewerProps) {
|
||||
@@ -1203,6 +1228,8 @@ export default function ReqViewer({
|
||||
filter={resolvedFilter}
|
||||
nip05Authors={nip05Authors}
|
||||
nip05PTags={nip05PTags}
|
||||
domainAuthors={domainAuthors}
|
||||
domainPTags={domainPTags}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
@@ -154,6 +154,8 @@ export function WindowRenderer({ window, onClose }: WindowRendererProps) {
|
||||
view={window.props.view}
|
||||
nip05Authors={window.props.nip05Authors}
|
||||
nip05PTags={window.props.nip05PTags}
|
||||
domainAuthors={window.props.domainAuthors}
|
||||
domainPTags={window.props.domainPTags}
|
||||
needsAccount={window.props.needsAccount}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { nip19 } from "nostr-tools";
|
||||
import type { NostrFilter } from "@/types/nostr";
|
||||
import { isNip05 } from "./nip05";
|
||||
import { isNip05, isDomain } from "./nip05";
|
||||
import {
|
||||
isValidHexPubkey,
|
||||
isValidHexEventId,
|
||||
@@ -14,6 +14,9 @@ export interface ParsedCountCommand {
|
||||
nip05Authors?: string[];
|
||||
nip05PTags?: string[];
|
||||
nip05PTagsUppercase?: string[];
|
||||
domainAuthors?: string[];
|
||||
domainPTags?: string[];
|
||||
domainPTagsUppercase?: string[];
|
||||
needsAccount?: boolean;
|
||||
}
|
||||
|
||||
@@ -61,6 +64,9 @@ export function parseCountCommand(args: string[]): ParsedCountCommand {
|
||||
const nip05Authors = new Set<string>();
|
||||
const nip05PTags = new Set<string>();
|
||||
const nip05PTagsUppercase = new Set<string>();
|
||||
const domainAuthors = new Set<string>();
|
||||
const domainPTags = new Set<string>();
|
||||
const domainPTagsUppercase = new Set<string>();
|
||||
|
||||
// Use sets for deduplication during accumulation
|
||||
const kinds = new Set<number>();
|
||||
@@ -133,6 +139,12 @@ export function parseCountCommand(args: string[]): ParsedCountCommand {
|
||||
if (normalized === "$me" || normalized === "$contacts") {
|
||||
authors.add(normalized);
|
||||
addedAny = true;
|
||||
} else if (authorStr.startsWith("@")) {
|
||||
const domain = authorStr.slice(1);
|
||||
if (isDomain(domain)) {
|
||||
domainAuthors.add(domain);
|
||||
addedAny = true;
|
||||
}
|
||||
} else if (isNip05(authorStr)) {
|
||||
nip05Authors.add(authorStr);
|
||||
addedAny = true;
|
||||
@@ -198,6 +210,12 @@ export function parseCountCommand(args: string[]): ParsedCountCommand {
|
||||
if (normalized === "$me" || normalized === "$contacts") {
|
||||
pTags.add(normalized);
|
||||
addedAny = true;
|
||||
} else if (pubkeyStr.startsWith("@")) {
|
||||
const domain = pubkeyStr.slice(1);
|
||||
if (isDomain(domain)) {
|
||||
domainPTags.add(domain);
|
||||
addedAny = true;
|
||||
}
|
||||
} else if (isNip05(pubkeyStr)) {
|
||||
nip05PTags.add(pubkeyStr);
|
||||
addedAny = true;
|
||||
@@ -229,6 +247,12 @@ export function parseCountCommand(args: string[]): ParsedCountCommand {
|
||||
if (normalized === "$me" || normalized === "$contacts") {
|
||||
pTagsUppercase.add(normalized);
|
||||
addedAny = true;
|
||||
} else if (pubkeyStr.startsWith("@")) {
|
||||
const domain = pubkeyStr.slice(1);
|
||||
if (isDomain(domain)) {
|
||||
domainPTagsUppercase.add(domain);
|
||||
addedAny = true;
|
||||
}
|
||||
} else if (isNip05(pubkeyStr)) {
|
||||
nip05PTagsUppercase.add(pubkeyStr);
|
||||
addedAny = true;
|
||||
@@ -377,6 +401,13 @@ export function parseCountCommand(args: string[]): ParsedCountCommand {
|
||||
nip05PTagsUppercase.size > 0
|
||||
? Array.from(nip05PTagsUppercase)
|
||||
: undefined,
|
||||
domainAuthors:
|
||||
domainAuthors.size > 0 ? Array.from(domainAuthors) : undefined,
|
||||
domainPTags: domainPTags.size > 0 ? Array.from(domainPTags) : undefined,
|
||||
domainPTagsUppercase:
|
||||
domainPTagsUppercase.size > 0
|
||||
? Array.from(domainPTagsUppercase)
|
||||
: undefined,
|
||||
needsAccount,
|
||||
};
|
||||
}
|
||||
|
||||
108
src/lib/nip05.ts
108
src/lib/nip05.ts
@@ -98,3 +98,111 @@ export async function resolveNip05Batch(
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Domain Directory Resolution (@domain syntax)
|
||||
* Resolves @domain to all pubkeys in domain's NIP-05 directory
|
||||
*/
|
||||
|
||||
// Cache for domain directory lookups (domain -> {pubkeys, timestamp})
|
||||
const domainDirectoryCache = new Map<
|
||||
string,
|
||||
{ pubkeys: string[]; timestamp: number }
|
||||
>();
|
||||
const DOMAIN_CACHE_TTL = 5 * 60 * 1000; // 5 minutes
|
||||
|
||||
/**
|
||||
* Check if a string looks like a domain (for @domain syntax)
|
||||
*/
|
||||
export function isDomain(value: string): boolean {
|
||||
if (!value) return false;
|
||||
return /^[a-zA-Z0-9][\w.-]+\.[a-zA-Z]{2,}$/.test(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all pubkeys from a domain's NIP-05 directory
|
||||
* @param domain - Domain name (e.g., "habla.news")
|
||||
* @returns Array of hex pubkeys from the domain's nostr.json
|
||||
*/
|
||||
export async function resolveDomainDirectory(
|
||||
domain: string,
|
||||
): Promise<string[]> {
|
||||
// Normalize domain to lowercase
|
||||
const normalizedDomain = domain.toLowerCase();
|
||||
|
||||
// Check cache first
|
||||
const cached = domainDirectoryCache.get(normalizedDomain);
|
||||
if (cached && Date.now() - cached.timestamp < DOMAIN_CACHE_TTL) {
|
||||
console.log(`Domain directory cache hit for @${normalizedDomain}`);
|
||||
return cached.pubkeys;
|
||||
}
|
||||
|
||||
try {
|
||||
const url = `https://${normalizedDomain}/.well-known/nostr.json`;
|
||||
const response = await fetch(url, {
|
||||
headers: { Accept: "application/json" },
|
||||
signal: AbortSignal.timeout(5000), // 5s timeout
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
console.warn(
|
||||
`Domain directory fetch failed for @${normalizedDomain}: ${response.status}`,
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (!data.names || typeof data.names !== "object") {
|
||||
console.warn(`Invalid nostr.json format for @${normalizedDomain}`);
|
||||
return [];
|
||||
}
|
||||
|
||||
// Extract all pubkeys from the names object
|
||||
const pubkeys = Object.values(data.names)
|
||||
.filter((pk): pk is string => typeof pk === "string")
|
||||
.map((pk) => pk.toLowerCase());
|
||||
|
||||
console.log(
|
||||
`Resolved @${normalizedDomain} → ${pubkeys.length} pubkeys`,
|
||||
pubkeys.slice(0, 5),
|
||||
);
|
||||
|
||||
// Cache the result
|
||||
domainDirectoryCache.set(normalizedDomain, {
|
||||
pubkeys,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
|
||||
return pubkeys;
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
`Domain directory resolution failed for @${normalizedDomain}:`,
|
||||
error,
|
||||
);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve multiple domain directories in parallel
|
||||
* @param domains - Array of domain names
|
||||
* @returns Map of domain -> pubkeys array
|
||||
*/
|
||||
export async function resolveDomainDirectoryBatch(
|
||||
domains: string[],
|
||||
): Promise<Map<string, string[]>> {
|
||||
const results = new Map<string, string[]>();
|
||||
|
||||
await Promise.all(
|
||||
domains.map(async (domain) => {
|
||||
const pubkeys = await resolveDomainDirectory(domain);
|
||||
if (pubkeys.length > 0) {
|
||||
// Store with original domain as key
|
||||
results.set(domain, pubkeys);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
@@ -153,6 +153,45 @@ describe("parseReqCommand", () => {
|
||||
const result = parseReqCommand(["-a", "user@domain.com,user@domain.com"]);
|
||||
expect(result.nip05Authors).toEqual(["user@domain.com"]);
|
||||
});
|
||||
|
||||
it("should accumulate @domain syntax for async resolution", () => {
|
||||
const result = parseReqCommand(["-a", "@habla.news"]);
|
||||
expect(result.domainAuthors).toEqual(["habla.news"]);
|
||||
expect(result.filter.authors).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should accumulate multiple @domains for async resolution", () => {
|
||||
const result = parseReqCommand(["-a", "@habla.news,@nostr.com"]);
|
||||
expect(result.domainAuthors).toEqual(["habla.news", "nostr.com"]);
|
||||
expect(result.filter.authors).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should handle mixed hex, NIP-05, and @domain", () => {
|
||||
const hex = "a".repeat(64);
|
||||
const result = parseReqCommand([
|
||||
"-a",
|
||||
`${hex},user@domain.com,@habla.news`,
|
||||
]);
|
||||
expect(result.filter.authors).toEqual([hex]);
|
||||
expect(result.nip05Authors).toEqual(["user@domain.com"]);
|
||||
expect(result.domainAuthors).toEqual(["habla.news"]);
|
||||
});
|
||||
|
||||
it("should deduplicate @domain identifiers", () => {
|
||||
const result = parseReqCommand(["-a", "@habla.news,@habla.news"]);
|
||||
expect(result.domainAuthors).toEqual(["habla.news"]);
|
||||
});
|
||||
|
||||
it("should preserve @domain case (normalization happens in resolution)", () => {
|
||||
const result = parseReqCommand(["-a", "@Habla.News"]);
|
||||
expect(result.domainAuthors).toEqual(["Habla.News"]);
|
||||
});
|
||||
|
||||
it("should reject invalid @domain formats", () => {
|
||||
const result = parseReqCommand(["-a", "@invalid"]);
|
||||
expect(result.domainAuthors).toBeUndefined();
|
||||
expect(result.filter.authors).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("event ID flag (-e)", () => {
|
||||
@@ -577,6 +616,23 @@ describe("parseReqCommand", () => {
|
||||
const result = parseReqCommand(["-p", `${hex},${hex}`]);
|
||||
expect(result.filter["#p"]).toEqual([hex]);
|
||||
});
|
||||
|
||||
it("should accumulate @domain syntax for #p tags", () => {
|
||||
const result = parseReqCommand(["-p", "@habla.news"]);
|
||||
expect(result.domainPTags).toEqual(["habla.news"]);
|
||||
expect(result.filter["#p"]).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should handle mixed hex, NIP-05, and @domain for #p tags", () => {
|
||||
const hex = "a".repeat(64);
|
||||
const result = parseReqCommand([
|
||||
"-p",
|
||||
`${hex},user@domain.com,@habla.news`,
|
||||
]);
|
||||
expect(result.filter["#p"]).toEqual([hex]);
|
||||
expect(result.nip05PTags).toEqual(["user@domain.com"]);
|
||||
expect(result.domainPTags).toEqual(["habla.news"]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("uppercase P tag flag (-P)", () => {
|
||||
@@ -641,6 +697,23 @@ describe("parseReqCommand", () => {
|
||||
expect(result.filter["#P"]).toEqual([hex]);
|
||||
});
|
||||
|
||||
it("should accumulate @domain syntax for #P tags", () => {
|
||||
const result = parseReqCommand(["-P", "@habla.news"]);
|
||||
expect(result.domainPTagsUppercase).toEqual(["habla.news"]);
|
||||
expect(result.filter["#P"]).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should handle mixed hex, NIP-05, and @domain for #P tags", () => {
|
||||
const hex = "a".repeat(64);
|
||||
const result = parseReqCommand([
|
||||
"-P",
|
||||
`${hex},user@domain.com,@habla.news`,
|
||||
]);
|
||||
expect(result.filter["#P"]).toEqual([hex]);
|
||||
expect(result.nip05PTagsUppercase).toEqual(["user@domain.com"]);
|
||||
expect(result.domainPTagsUppercase).toEqual(["habla.news"]);
|
||||
});
|
||||
|
||||
it("should handle $me alias in #P tags", () => {
|
||||
const result = parseReqCommand(["-P", "$me"]);
|
||||
expect(result.filter["#P"]).toContain("$me");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { nip19 } from "nostr-tools";
|
||||
import type { NostrFilter } from "@/types/nostr";
|
||||
import { isNip05 } from "./nip05";
|
||||
import { isNip05, isDomain } from "./nip05";
|
||||
import {
|
||||
isValidHexPubkey,
|
||||
isValidHexEventId,
|
||||
@@ -18,6 +18,9 @@ export interface ParsedReqCommand {
|
||||
nip05Authors?: string[]; // NIP-05 identifiers that need async resolution
|
||||
nip05PTags?: string[]; // NIP-05 identifiers for #p tags that need async resolution
|
||||
nip05PTagsUppercase?: string[]; // NIP-05 identifiers for #P tags that need async resolution
|
||||
domainAuthors?: string[]; // @domain aliases for authors that need async resolution
|
||||
domainPTags?: string[]; // @domain aliases for #p tags that need async resolution
|
||||
domainPTagsUppercase?: string[]; // @domain aliases for #P tags that need async resolution
|
||||
needsAccount?: boolean; // True if filter contains $me or $contacts aliases
|
||||
}
|
||||
|
||||
@@ -60,6 +63,9 @@ export function parseReqCommand(args: string[]): ParsedReqCommand {
|
||||
const nip05Authors = new Set<string>();
|
||||
const nip05PTags = new Set<string>();
|
||||
const nip05PTagsUppercase = new Set<string>();
|
||||
const domainAuthors = new Set<string>();
|
||||
const domainPTags = new Set<string>();
|
||||
const domainPTagsUppercase = new Set<string>();
|
||||
|
||||
// Use sets for deduplication during accumulation
|
||||
const kinds = new Set<number>();
|
||||
@@ -124,7 +130,7 @@ export function parseReqCommand(args: string[]): ParsedReqCommand {
|
||||
|
||||
case "-a":
|
||||
case "--author": {
|
||||
// Support comma-separated authors: -a npub1...,npub2...,user@domain.com,$me,$contacts
|
||||
// Support comma-separated authors: -a npub1...,npub2...,user@domain.com,@domain.com,$me,$contacts
|
||||
if (!nextArg) {
|
||||
i++;
|
||||
break;
|
||||
@@ -138,6 +144,13 @@ export function parseReqCommand(args: string[]): ParsedReqCommand {
|
||||
if (normalized === "$me" || normalized === "$contacts") {
|
||||
authors.add(normalized);
|
||||
addedAny = true;
|
||||
} else if (authorStr.startsWith("@")) {
|
||||
// Check for @domain syntax
|
||||
const domain = authorStr.slice(1);
|
||||
if (isDomain(domain)) {
|
||||
domainAuthors.add(domain);
|
||||
addedAny = true;
|
||||
}
|
||||
} else if (isNip05(authorStr)) {
|
||||
// Check if it's a NIP-05 identifier
|
||||
nip05Authors.add(authorStr);
|
||||
@@ -208,7 +221,7 @@ export function parseReqCommand(args: string[]): ParsedReqCommand {
|
||||
}
|
||||
|
||||
case "-p": {
|
||||
// Support comma-separated pubkeys: -p npub1...,npub2...,user@domain.com,$me,$contacts
|
||||
// Support comma-separated pubkeys: -p npub1...,npub2...,user@domain.com,@domain.com,$me,$contacts
|
||||
if (!nextArg) {
|
||||
i++;
|
||||
break;
|
||||
@@ -222,6 +235,13 @@ export function parseReqCommand(args: string[]): ParsedReqCommand {
|
||||
if (normalized === "$me" || normalized === "$contacts") {
|
||||
pTags.add(normalized);
|
||||
addedAny = true;
|
||||
} else if (pubkeyStr.startsWith("@")) {
|
||||
// Check for @domain syntax
|
||||
const domain = pubkeyStr.slice(1);
|
||||
if (isDomain(domain)) {
|
||||
domainPTags.add(domain);
|
||||
addedAny = true;
|
||||
}
|
||||
} else if (isNip05(pubkeyStr)) {
|
||||
// Check if it's a NIP-05 identifier
|
||||
nip05PTags.add(pubkeyStr);
|
||||
@@ -244,7 +264,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...,$me,$contacts
|
||||
// Support comma-separated pubkeys: -P npub1...,npub2...,@domain.com,$me,$contacts
|
||||
if (!nextArg) {
|
||||
i++;
|
||||
break;
|
||||
@@ -258,6 +278,13 @@ export function parseReqCommand(args: string[]): ParsedReqCommand {
|
||||
if (normalized === "$me" || normalized === "$contacts") {
|
||||
pTagsUppercase.add(normalized);
|
||||
addedAny = true;
|
||||
} else if (pubkeyStr.startsWith("@")) {
|
||||
// Check for @domain syntax
|
||||
const domain = pubkeyStr.slice(1);
|
||||
if (isDomain(domain)) {
|
||||
domainPTagsUppercase.add(domain);
|
||||
addedAny = true;
|
||||
}
|
||||
} else if (isNip05(pubkeyStr)) {
|
||||
// Check if it's a NIP-05 identifier
|
||||
nip05PTagsUppercase.add(pubkeyStr);
|
||||
@@ -439,6 +466,13 @@ export function parseReqCommand(args: string[]): ParsedReqCommand {
|
||||
nip05PTagsUppercase.size > 0
|
||||
? Array.from(nip05PTagsUppercase)
|
||||
: undefined,
|
||||
domainAuthors:
|
||||
domainAuthors.size > 0 ? Array.from(domainAuthors) : undefined,
|
||||
domainPTags: domainPTags.size > 0 ? Array.from(domainPTags) : undefined,
|
||||
domainPTagsUppercase:
|
||||
domainPTagsUppercase.size > 0
|
||||
? Array.from(domainPTagsUppercase)
|
||||
: undefined,
|
||||
needsAccount,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { AppId } from "./app";
|
||||
import { parseOpenCommand } from "@/lib/open-parser";
|
||||
import { parseProfileCommand } from "@/lib/profile-parser";
|
||||
import { parseRelayCommand } from "@/lib/relay-parser";
|
||||
import { resolveNip05Batch } from "@/lib/nip05";
|
||||
import { resolveNip05Batch, resolveDomainDirectoryBatch } from "@/lib/nip05";
|
||||
import { parseChatCommand } from "@/lib/chat-parser";
|
||||
import { parseBlossomCommand } from "@/lib/blossom-parser";
|
||||
|
||||
@@ -318,6 +318,50 @@ export const manPages: Record<string, ManPageEntry> = {
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve domain directories if present
|
||||
const allDomains = [
|
||||
...(parsed.domainAuthors || []),
|
||||
...(parsed.domainPTags || []),
|
||||
...(parsed.domainPTagsUppercase || []),
|
||||
];
|
||||
|
||||
if (allDomains.length > 0) {
|
||||
const resolved = await resolveDomainDirectoryBatch(allDomains);
|
||||
|
||||
// Add resolved authors to filter
|
||||
if (parsed.domainAuthors) {
|
||||
for (const domain of parsed.domainAuthors) {
|
||||
const pubkeys = resolved.get(domain);
|
||||
if (pubkeys) {
|
||||
if (!parsed.filter.authors) parsed.filter.authors = [];
|
||||
parsed.filter.authors.push(...pubkeys);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add resolved #p tags to filter
|
||||
if (parsed.domainPTags) {
|
||||
for (const domain of parsed.domainPTags) {
|
||||
const pubkeys = resolved.get(domain);
|
||||
if (pubkeys) {
|
||||
if (!parsed.filter["#p"]) parsed.filter["#p"] = [];
|
||||
parsed.filter["#p"].push(...pubkeys);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add resolved #P tags to filter
|
||||
if (parsed.domainPTagsUppercase) {
|
||||
for (const domain of parsed.domainPTagsUppercase) {
|
||||
const pubkeys = resolved.get(domain);
|
||||
if (pubkeys) {
|
||||
if (!parsed.filter["#P"]) parsed.filter["#P"] = [];
|
||||
parsed.filter["#P"].push(...pubkeys);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parsed;
|
||||
},
|
||||
defaultProps: { filter: { kinds: [1], limit: 50 } },
|
||||
@@ -443,6 +487,47 @@ export const manPages: Record<string, ManPageEntry> = {
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve domain directories if present
|
||||
const allDomains = [
|
||||
...(parsed.domainAuthors || []),
|
||||
...(parsed.domainPTags || []),
|
||||
...(parsed.domainPTagsUppercase || []),
|
||||
];
|
||||
|
||||
if (allDomains.length > 0) {
|
||||
const resolved = await resolveDomainDirectoryBatch(allDomains);
|
||||
|
||||
if (parsed.domainAuthors) {
|
||||
for (const domain of parsed.domainAuthors) {
|
||||
const pubkeys = resolved.get(domain);
|
||||
if (pubkeys) {
|
||||
if (!parsed.filter.authors) parsed.filter.authors = [];
|
||||
parsed.filter.authors.push(...pubkeys);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (parsed.domainPTags) {
|
||||
for (const domain of parsed.domainPTags) {
|
||||
const pubkeys = resolved.get(domain);
|
||||
if (pubkeys) {
|
||||
if (!parsed.filter["#p"]) parsed.filter["#p"] = [];
|
||||
parsed.filter["#p"].push(...pubkeys);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (parsed.domainPTagsUppercase) {
|
||||
for (const domain of parsed.domainPTagsUppercase) {
|
||||
const pubkeys = resolved.get(domain);
|
||||
if (pubkeys) {
|
||||
if (!parsed.filter["#P"]) parsed.filter["#P"] = [];
|
||||
parsed.filter["#P"].push(...pubkeys);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parsed;
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"root":["./vite.config.ts"],"errors":true,"version":"5.9.3"}
|
||||
{"root":["./vite.config.ts"],"version":"5.6.3"}
|
||||
Reference in New Issue
Block a user