diff --git a/src/lib/components/publications/PublicationFeed.svelte b/src/lib/components/publications/PublicationFeed.svelte
index 674eb5a..8156cfe 100644
--- a/src/lib/components/publications/PublicationFeed.svelte
+++ b/src/lib/components/publications/PublicationFeed.svelte
@@ -290,9 +290,9 @@
};
// Debounced search function
- const debouncedSearch = debounce(async (query: string) => {
+ const debouncedSearch = debounce((query: string | undefined) => {
console.debug("[PublicationFeed] Search query changed:", query);
- if (query.trim()) {
+ if (query && query.trim()) {
const filtered = filterEventsBySearch(allIndexEvents);
eventsInView = filtered.slice(0, 30);
endOfFeed = filtered.length <= 30;
@@ -303,10 +303,6 @@
}, 300);
$effect(() => {
- console.debug(
- "[PublicationFeed] Search query effect triggered:",
- props.searchQuery,
- );
debouncedSearch(props.searchQuery);
});
diff --git a/src/lib/navigator/EventNetwork/NodeTooltip.svelte b/src/lib/navigator/EventNetwork/NodeTooltip.svelte
index 8066d4c..8e95b6e 100644
--- a/src/lib/navigator/EventNetwork/NodeTooltip.svelte
+++ b/src/lib/navigator/EventNetwork/NodeTooltip.svelte
@@ -145,7 +145,7 @@
diff --git a/src/lib/utils.ts b/src/lib/utils.ts
index bc2a2ab..00576d5 100644
--- a/src/lib/utils.ts
+++ b/src/lib/utils.ts
@@ -1,7 +1,21 @@
import type { NDKEvent } from "@nostr-dev-kit/ndk";
import { nip19 } from "nostr-tools";
import { getMatchingTags } from "./utils/nostrUtils.ts";
-import { AddressPointer, EventPointer } from "nostr-tools/nip19";
+import type { AddressPointer, EventPointer } from "nostr-tools/nip19";
+
+export class DecodeError extends Error {
+ constructor(message: string) {
+ super(message);
+ this.name = "DecodeError";
+ }
+}
+
+export class InvalidKindError extends DecodeError {
+ constructor(message: string) {
+ super(message);
+ this.name = "InvalidKindError";
+ }
+}
export function neventEncode(event: NDKEvent, relays: string[]) {
return nip19.neventEncode({
@@ -31,39 +45,41 @@ export function nprofileEncode(pubkey: string, relays: string[]) {
}
/**
- * Decodes an naddr identifier and returns the decoded data
+ * Decodes a nostr identifier (naddr, nevent) and returns the decoded data.
+ * @param identifier The nostr identifier to decode.
+ * @param expectedType The expected type of the decoded data ('naddr' or 'nevent').
+ * @returns The decoded data.
*/
-export function naddrDecode(naddr: string): AddressPointer {
+function decodeNostrIdentifier
(
+ identifier: string,
+ expectedType: "naddr" | "nevent",
+): T {
try {
- if (!naddr.startsWith('naddr')) {
- throw new Error('Invalid naddr format');
+ if (!identifier.startsWith(expectedType)) {
+ throw new InvalidKindError(`Invalid ${expectedType} format`);
}
- const decoded = nip19.decode(naddr);
- if (decoded.type !== 'naddr') {
- throw new Error('Decoded result is not an naddr');
+ const decoded = nip19.decode(identifier);
+ if (decoded.type !== expectedType) {
+ throw new InvalidKindError(`Decoded result is not an ${expectedType}`);
}
- return decoded.data;
+ return decoded.data as T;
} catch (error) {
- throw new Error(`Failed to decode naddr: ${error}`);
+ throw new DecodeError(`Failed to decode ${expectedType}: ${error}`);
}
}
+/**
+ * Decodes an naddr identifier and returns the decoded data
+ */
+export function naddrDecode(naddr: string): AddressPointer {
+ return decodeNostrIdentifier(naddr, "naddr");
+}
+
/**
* Decodes an nevent identifier and returns the decoded data
*/
export function neventDecode(nevent: string): EventPointer {
- try {
- if (!nevent.startsWith('nevent')) {
- throw new Error('Invalid nevent format');
- }
- const decoded = nip19.decode(nevent);
- if (decoded.type !== 'nevent') {
- throw new Error('Decoded result is not an nevent');
- }
- return decoded.data;
- } catch (error) {
- throw new Error(`Failed to decode nevent: ${error}`);
- }
+ return decodeNostrIdentifier(nevent, "nevent");
}
export function formatDate(unixtimestamp: number) {
@@ -206,7 +222,8 @@ Array.prototype.findIndexAsync = function (
* @param wait The number of milliseconds to delay
* @returns A debounced version of the function
*/
-export function debounce unknown>(
+// deno-lint-ignore no-explicit-any
+export function debounce any>(
func: T,
wait: number,
): (...args: Parameters) => void {
diff --git a/src/lib/utils/event_search.ts b/src/lib/utils/event_search.ts
index 25319c0..5330ebb 100644
--- a/src/lib/utils/event_search.ts
+++ b/src/lib/utils/event_search.ts
@@ -1,7 +1,8 @@
import { ndkInstance } from "../ndk.ts";
import { fetchEventWithFallback } from "./nostrUtils.ts";
import { nip19 } from "nostr-tools";
-import { NDKEvent, NDKFilter } from "@nostr-dev-kit/ndk";
+import type { NDKFilter } from "@nostr-dev-kit/ndk";
+import { NDKEvent } from "@nostr-dev-kit/ndk";
import { get } from "svelte/store";
import { wellKnownUrl, isValidNip05Address } from "./search_utils.ts";
import { TIMEOUTS, VALIDATION } from "./search_constants.ts";
diff --git a/src/lib/utils/markup/advancedAsciidoctorPostProcessor.ts b/src/lib/utils/markup/advancedAsciidoctorPostProcessor.ts
index 10ec1a7..41e4df9 100644
--- a/src/lib/utils/markup/advancedAsciidoctorPostProcessor.ts
+++ b/src/lib/utils/markup/advancedAsciidoctorPostProcessor.ts
@@ -32,9 +32,11 @@ export async function postProcessAdvancedAsciidoctorHtml(
}
if (
typeof globalThis !== "undefined" &&
- typeof globalThis.MathJax?.typesetPromise === "function"
+ // deno-lint-ignore no-explicit-any
+ typeof (globalThis as any).MathJax?.typesetPromise === "function"
) {
- setTimeout(() => globalThis.MathJax.typesetPromise(), 0);
+ // deno-lint-ignore no-explicit-any
+ setTimeout(() => (globalThis as any).MathJax.typesetPromise(), 0);
}
return processedHtml;
} catch (error) {
diff --git a/src/lib/utils/network_detection.ts b/src/lib/utils/network_detection.ts
index 40bb568..e69543a 100644
--- a/src/lib/utils/network_detection.ts
+++ b/src/lib/utils/network_detection.ts
@@ -153,10 +153,11 @@ export function getRelaySetForNetworkCondition(
*/
export function startNetworkMonitoring(
onNetworkChange: (condition: NetworkCondition) => void,
- checkInterval: number = 60000 // Increased to 60 seconds to reduce spam
+ checkInterval: number = 60000, // Increased to 60 seconds to reduce spam
): () => void {
let lastCondition: NetworkCondition | null = null;
- let intervalId: number | null = null;
+ // deno-lint-ignore no-explicit-any
+ let intervalId: any = null;
const checkNetwork = async () => {
try {
diff --git a/src/lib/utils/nostrUtils.ts b/src/lib/utils/nostrUtils.ts
index 91d3309..813f1e5 100644
--- a/src/lib/utils/nostrUtils.ts
+++ b/src/lib/utils/nostrUtils.ts
@@ -12,6 +12,8 @@ import { schnorr } from "@noble/curves/secp256k1";
import { bytesToHex } from "@noble/hashes/utils";
import { wellKnownUrl } from "./search_utility.ts";
import { VALIDATION } from "./search_constants.ts";
+import { error } from "@sveltejs/kit";
+import { naddrDecode, neventDecode } from "../utils.ts";
const badgeCheckSvg =
'';
@@ -668,3 +670,84 @@ export function prefixNostrAddresses(content: string): string {
return `nostr:${match}`;
});
}
+
+// Added functions for fetching events by various identifiers
+
+/**
+ * Fetches an event by hex ID, throwing a SvelteKit 404 error if not found.
+ */
+export async function fetchEventById(ndk: NDK, id: string): Promise {
+ try {
+ const event = await fetchEventWithFallback(ndk, id);
+ if (!event) {
+ throw error(404, `Event not found for ID: ${id}`);
+ }
+ return event;
+ } catch (err) {
+ if (err && typeof err === "object" && "status" in err) {
+ throw err;
+ }
+ throw error(404, `Failed to fetch event by ID: ${err}`);
+ }
+}
+
+/**
+ * Fetches an event by d tag, throwing a 404 if not found.
+ */
+export async function fetchEventByDTag(ndk: NDK, dTag: string): Promise {
+ try {
+ const event = await fetchEventWithFallback(ndk, { "#d": [dTag], limit: 1 });
+ if (!event) {
+ throw error(404, `Event not found for d-tag: ${dTag}`);
+ }
+ return event;
+ } catch (err) {
+ if (err && typeof err === "object" && "status" in err) {
+ throw err;
+ }
+ throw error(404, `Failed to fetch event by d-tag: ${err}`);
+ }
+}
+
+/**
+ * Fetches an event by naddr identifier.
+ */
+export async function fetchEventByNaddr(ndk: NDK, naddr: string): Promise {
+ try {
+ const decoded = naddrDecode(naddr);
+ const filter = {
+ kinds: [decoded.kind],
+ authors: [decoded.pubkey],
+ "#d": [decoded.identifier],
+ };
+ const event = await fetchEventWithFallback(ndk, filter);
+ if (!event) {
+ throw error(404, `Event not found for naddr: ${naddr}`);
+ }
+ return event;
+ } catch (err) {
+ if (err && typeof err === "object" && "status" in err) {
+ throw err;
+ }
+ throw error(404, `Failed to fetch event by naddr: ${err}`);
+ }
+}
+
+/**
+ * Fetches an event by nevent identifier.
+ */
+export async function fetchEventByNevent(ndk: NDK, nevent: string): Promise {
+ try {
+ const decoded = neventDecode(nevent);
+ const event = await fetchEventWithFallback(ndk, decoded.id);
+ if (!event) {
+ throw error(404, `Event not found for nevent: ${nevent}`);
+ }
+ return event;
+ } catch (err) {
+ if (err && typeof err === "object" && "status" in err) {
+ throw err;
+ }
+ throw error(404, `Failed to fetch event by nevent: ${err}`);
+ }
+}
diff --git a/src/lib/utils/search_types.ts b/src/lib/utils/search_types.ts
index 134ceff..a537edb 100644
--- a/src/lib/utils/search_types.ts
+++ b/src/lib/utils/search_types.ts
@@ -1,4 +1,4 @@
-import { NDKEvent, NDKFilter, NDKSubscription } from "@nostr-dev-kit/ndk";
+import type { NDKEvent, NDKFilter, NDKSubscription } from "@nostr-dev-kit/ndk";
/**
* Extended NostrProfile interface for search results
diff --git a/src/routes/+layout.ts b/src/routes/+layout.ts
index 7ac4f69..59bc393 100644
--- a/src/routes/+layout.ts
+++ b/src/routes/+layout.ts
@@ -8,12 +8,13 @@ import { loginMethodStorageKey } from "../lib/stores/userStore.ts";
import Pharos, { pharosInstance } from "../lib/parser.ts";
import type { LayoutLoad } from "./$types";
import { get } from "svelte/store";
+import { browser } from "$app/environment";
-export const load: LayoutLoad = () => {
- // Initialize NDK with new relay management system
- const ndk = initNdk();
- ndkInstance.set(ndk);
-
+/**
+ * Attempts to restore the user's authentication session from localStorage.
+ * Handles extension, Amber (NIP-46), and npub login methods.
+ */
+async function restoreAuthSession() {
try {
const pubkey = getPersistedLogin();
const loginMethod = localStorage.getItem(loginMethodStorageKey);
@@ -111,9 +112,19 @@ export const load: LayoutLoad = () => {
`Failed to restore login: ${e}\n\nContinuing with anonymous session.`,
);
}
+}
+
+export const load: LayoutLoad = () => {
+ // Initialize NDK with new relay management system
+ const ndk = initNdk();
+ ndkInstance.set(ndk);
+
+ if (browser) {
+ restoreAuthSession();
+ }
const parser = new Pharos(ndk);
- pharosInstance.set(parser);
+ pharosInstance.set(parser);
return {
ndk,
diff --git a/src/routes/publication/+page.server.ts b/src/routes/publication/+page.server.ts
index 29fc5a6..1b66af2 100644
--- a/src/routes/publication/+page.server.ts
+++ b/src/routes/publication/+page.server.ts
@@ -1,6 +1,22 @@
import { redirect } from "@sveltejs/kit";
import type { PageServerLoad } from "./$types";
+// Route pattern constants
+const ROUTES = {
+ PUBLICATION_BASE: "/publication",
+ NADDR: "/publication/naddr",
+ NEVENT: "/publication/nevent",
+ ID: "/publication/id",
+ D_TAG: "/publication/d",
+ START: "/start",
+} as const;
+
+// Identifier prefixes
+const IDENTIFIER_PREFIXES = {
+ NADDR: "naddr",
+ NEVENT: "nevent",
+} as const;
+
export const load: PageServerLoad = ({ url }) => {
const id = url.searchParams.get("id");
const dTag = url.searchParams.get("d");
@@ -8,19 +24,18 @@ export const load: PageServerLoad = ({ url }) => {
// Handle backward compatibility for old query-based routes
if (id) {
// Check if id is an naddr or nevent
- if (id.startsWith("naddr")) {
- throw redirect(301, `/publication/naddr/${id}`);
- } else if (id.startsWith("nevent")) {
- throw redirect(301, `/publication/nevent/${id}`);
+ if (id.startsWith(IDENTIFIER_PREFIXES.NADDR)) {
+ throw redirect(301, `${ROUTES.NADDR}/${id}`);
+ } else if (id.startsWith(IDENTIFIER_PREFIXES.NEVENT)) {
+ throw redirect(301, `${ROUTES.NEVENT}/${id}`);
} else {
// Assume it's a hex ID
- throw redirect(301, `/publication/id/${id}`);
+ throw redirect(301, `${ROUTES.ID}/${id}`);
}
} else if (dTag) {
- throw redirect(301, `/publication/d/${dTag}`);
+ throw redirect(301, `${ROUTES.D_TAG}/${dTag}`);
}
- // If no query parameters, redirect to the start page or show publication feed\
- // AI-TODO: Redirect to a "not found" page.
- throw redirect(301, "/start");
+ // If no query parameters, redirect to the start page
+ throw redirect(301, ROUTES.START);
};
\ No newline at end of file
diff --git a/src/routes/publication/[type]/[identifier]/+layout.server.ts b/src/routes/publication/[type]/[identifier]/+layout.server.ts
index a9ddd3c..1209f7b 100644
--- a/src/routes/publication/[type]/[identifier]/+layout.server.ts
+++ b/src/routes/publication/[type]/[identifier]/+layout.server.ts
@@ -1,95 +1,12 @@
import { error } from "@sveltejs/kit";
import type { LayoutServerLoad } from "./$types";
import type { NDKEvent } from "@nostr-dev-kit/ndk";
-import { getActiveRelaySetAsNDKRelaySet } from "../../../../lib/ndk.ts";
-import { getMatchingTags } from "../../../../lib/utils/nostrUtils.ts";
-import { naddrDecode, neventDecode } from "../../../../lib/utils.ts";
-import type NDK from "@nostr-dev-kit/ndk";
-
-// AI-TODO: Use `fetchEventWithFallback` from `nostrUtils.ts` to retrieve events in this file.
-
-/**
- * Fetches an event by hex ID
- */
-async function fetchEventById(ndk: NDK, id: string): Promise {
- try {
- const event = await ndk.fetchEvent(id);
- if (!event) {
- throw new Error(`Event not found for ID: ${id}`);
- }
- return event;
- } catch (err) {
- throw error(404, `Failed to fetch publication root event.\n${err}`);
- }
-}
-
-/**
- * Fetches an event by d tag
- */
-async function fetchEventByDTag(ndk: NDK, dTag: string): Promise {
- try {
- const relaySet = await getActiveRelaySetAsNDKRelaySet(ndk, true);
- const events = await ndk.fetchEvents(
- { "#d": [dTag] },
- { closeOnEose: false },
- relaySet,
- );
-
- if (!events || events.size === 0) {
- throw new Error(`Event not found for d tag: ${dTag}`);
- }
-
- // Choose the event with the latest created_at timestamp when multiple events share the same d tag
- const sortedEvents = Array.from(events).sort((a, b) => (b.created_at || 0) - (a.created_at || 0));
- return sortedEvents[0];
- } catch (err) {
- throw error(404, `Failed to fetch publication root event.\n${err}`);
- }
-}
-
-/**
- * Fetches an event by naddr identifier
- */
-async function fetchEventByNaddr(ndk: NDK, naddr: string): Promise {
- try {
- const decoded = naddrDecode(naddr);
- const relaySet = await getActiveRelaySetAsNDKRelaySet(ndk, true);
-
- const filter = {
- kinds: [decoded.kind],
- authors: [decoded.pubkey],
- "#d": [decoded.identifier],
- };
-
- const event = await ndk.fetchEvent(filter, { closeOnEose: false }, relaySet);
- if (!event) {
- throw new Error(`Event not found for naddr: ${naddr}`);
- }
- return event;
- } catch (err) {
- throw error(404, `Failed to fetch publication root event.\n${err}`);
- }
-}
-
-/**
- * Fetches an event by nevent identifier
- */
-async function fetchEventByNevent(ndk: NDK, nevent: string): Promise {
- try {
- const decoded = neventDecode(nevent);
- const event = await ndk.fetchEvent(decoded.id);
- if (!event) {
- throw new Error(`Event not found for nevent: ${nevent}`);
- }
- return event;
- } catch (err) {
- throw error(404, `Failed to fetch publication root event.\n${err}`);
- }
-}
+import { getMatchingTags, fetchEventById, fetchEventByDTag, fetchEventByNaddr, fetchEventByNevent } from "../../../../lib/utils/nostrUtils.ts";
export const load: LayoutServerLoad = async ({ params, parent, url }) => {
const { type, identifier } = params;
- const { ndk } = await parent();
+ // deno-lint-ignore no-explicit-any
+ const { ndk } = (await parent()) as any;
if (!ndk) {
throw error(500, "NDK not available");
diff --git a/src/routes/publication/[type]/[identifier]/+page.server.ts b/src/routes/publication/[type]/[identifier]/+page.server.ts
index 3c033c7..95b58fe 100644
--- a/src/routes/publication/[type]/[identifier]/+page.server.ts
+++ b/src/routes/publication/[type]/[identifier]/+page.server.ts
@@ -1,95 +1,12 @@
import { error } from "@sveltejs/kit";
import type { PageServerLoad } from "./$types";
import type { NDKEvent } from "@nostr-dev-kit/ndk";
-import { getActiveRelaySetAsNDKRelaySet } from "../../../../lib/ndk.ts";
-import { getMatchingTags } from "../../../../lib/utils/nostrUtils.ts";
-import { naddrDecode, neventDecode } from "../../../../lib/utils.ts";
-import type NDK from "@nostr-dev-kit/ndk";
-
-// AI-TODO: Use `fetchEventWithFallback` from `nostrUtils.ts` to retrieve events in this file.
-
-/**
- * Fetches an event by hex ID
- */
-async function fetchEventById(ndk: NDK, id: string): Promise {
- try {
- const event = await ndk.fetchEvent(id);
- if (!event) {
- throw new Error(`Event not found for ID: ${id}`);
- }
- return event;
- } catch (err) {
- throw error(404, `Failed to fetch publication root event.\n${err}`);
- }
-}
-
-/**
- * Fetches an event by d tag
- */
-async function fetchEventByDTag(ndk: NDK, dTag: string): Promise {
- try {
- const relaySet = await getActiveRelaySetAsNDKRelaySet(ndk, true); // true for inbox relays
- const events = await ndk.fetchEvents(
- { "#d": [dTag] },
- { closeOnEose: false },
- relaySet,
- );
-
- if (!events || events.size === 0) {
- throw new Error(`Event not found for d tag: ${dTag}`);
- }
-
- // AI-NOTE: Choose the event with the latest created_at timestamp when multiple events share the same d tag
- const sortedEvents = Array.from(events).sort((a, b) => (b.created_at || 0) - (a.created_at || 0));
- return sortedEvents[0];
- } catch (err) {
- throw error(404, `Failed to fetch publication root event.\n${err}`);
- }
-}
-
-/**
- * Fetches an event by naddr identifier
- */
-async function fetchEventByNaddr(ndk: NDK, naddr: string): Promise {
- try {
- const decoded = naddrDecode(naddr);
- const relaySet = await getActiveRelaySetAsNDKRelaySet(ndk, true);
-
- const filter = {
- kinds: [decoded.kind],
- authors: [decoded.pubkey],
- "#d": [decoded.identifier],
- };
-
- const event = await ndk.fetchEvent(filter, { closeOnEose: false }, relaySet);
- if (!event) {
- throw new Error(`Event not found for naddr: ${naddr}`);
- }
- return event;
- } catch (err) {
- throw error(404, `Failed to fetch publication root event.\n${err}`);
- }
-}
-
-/**
- * Fetches an event by nevent identifier
- */
-async function fetchEventByNevent(ndk: NDK, nevent: string): Promise {
- try {
- const decoded = neventDecode(nevent);
- const event = await ndk.fetchEvent(decoded.id);
- if (!event) {
- throw new Error(`Event not found for nevent: ${nevent}`);
- }
- return event;
- } catch (err) {
- throw error(404, `Failed to fetch publication root event.\n${err}`);
- }
-}
+import { getMatchingTags, fetchEventById, fetchEventByDTag, fetchEventByNaddr, fetchEventByNevent } from "../../../../lib/utils/nostrUtils.ts";
export const load: PageServerLoad = async ({ params, parent }) => {
const { type, identifier } = params;
- const { ndk } = await parent();
+ // deno-lint-ignore no-explicit-any
+ const { ndk } = (await parent()) as any;
if (!ndk) {
throw error(500, "NDK not available");