87fde102f9f1557efa2bd0be3826f459e0cd6ddca6aa92249795c4f4ee13bdd4.json raw
1 {"ast":null,"code":"import _asyncToGenerator from \"/home/mleku/src/orly.dev/next/signer/node_modules/@babel/runtime/helpers/esm/asyncToGenerator.js\";\nimport { inject } from '@angular/core';\nimport { SimplePool } from 'nostr-tools/pool';\nimport { FALLBACK_PROFILE_RELAYS } from '../../constants/fallback-relays';\nimport { LoggerService } from '../logger/logger.service';\nimport * as i0 from \"@angular/core\";\nconst CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours\nconst FETCH_TIMEOUT_MS = 10000; // 10 seconds\nconst STORAGE_KEY = 'profileMetadataCache';\nexport let ProfileMetadataService = /*#__PURE__*/(() => {\n class ProfileMetadataService {\n #logger = inject(LoggerService);\n #cache = {};\n #pool = null;\n #fetchPromises = new Map();\n #initialized = false;\n #initPromise = null;\n /**\n * Initialize the service by loading cache from session storage\n */\n initialize() {\n var _this = this;\n return _asyncToGenerator(function* () {\n if (_this.#initialized) {\n return;\n }\n if (_this.#initPromise) {\n return _this.#initPromise;\n }\n _this.#initPromise = _this.#loadCacheFromStorage();\n yield _this.#initPromise;\n _this.#initialized = true;\n })();\n }\n /**\n * Load cache from browser session storage\n */\n #loadCacheFromStorage() {\n var _this2 = this;\n return _asyncToGenerator(function* () {\n try {\n // Use chrome API (works in both Chrome and Firefox with polyfill)\n if (typeof chrome !== 'undefined' && chrome.storage?.session) {\n const result = yield chrome.storage.session.get(STORAGE_KEY);\n if (result[STORAGE_KEY]) {\n _this2.#cache = result[STORAGE_KEY];\n // Clean up stale entries\n _this2.#pruneStaleCache();\n }\n }\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : 'Unknown error';\n _this2.#logger.logStorageError('load profile cache', errorMsg);\n }\n })();\n }\n /**\n * Save cache to browser session storage\n */\n #saveCacheToStorage() {\n var _this3 = this;\n return _asyncToGenerator(function* () {\n try {\n if (typeof chrome !== 'undefined' && chrome.storage?.session) {\n yield chrome.storage.session.set({\n [STORAGE_KEY]: _this3.#cache\n });\n }\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : 'Unknown error';\n _this3.#logger.logStorageError('save profile cache', errorMsg);\n }\n })();\n }\n /**\n * Remove stale entries from cache\n */\n #pruneStaleCache() {\n const now = Date.now();\n for (const pubkey of Object.keys(this.#cache)) {\n if (now - this.#cache[pubkey].fetchedAt > CACHE_TTL_MS) {\n delete this.#cache[pubkey];\n }\n }\n }\n /**\n * Get the SimplePool instance, creating it if necessary\n */\n #getPool() {\n if (!this.#pool) {\n this.#pool = new SimplePool();\n }\n return this.#pool;\n }\n /**\n * Get cached profile metadata for a pubkey\n */\n getCachedProfile(pubkey) {\n const cached = this.#cache[pubkey];\n if (!cached) {\n return null;\n }\n // Check if cache is still valid\n if (Date.now() - cached.fetchedAt > CACHE_TTL_MS) {\n delete this.#cache[pubkey];\n return null;\n }\n return cached;\n }\n /**\n * Fetch profile metadata for a single pubkey\n */\n fetchProfile(pubkey) {\n var _this4 = this;\n return _asyncToGenerator(function* () {\n // Ensure initialized\n yield _this4.initialize();\n // Check cache first\n const cached = _this4.getCachedProfile(pubkey);\n if (cached) {\n return cached;\n }\n // Check if already fetching\n const existingPromise = _this4.#fetchPromises.get(pubkey);\n if (existingPromise) {\n return existingPromise;\n }\n // Start new fetch\n const fetchPromise = _this4.#doFetchProfile(pubkey);\n _this4.#fetchPromises.set(pubkey, fetchPromise);\n try {\n const result = yield fetchPromise;\n return result;\n } finally {\n _this4.#fetchPromises.delete(pubkey);\n }\n })();\n }\n /**\n * Fetch profiles for multiple pubkeys in parallel\n */\n fetchProfiles(pubkeys) {\n var _this5 = this;\n return _asyncToGenerator(function* () {\n // Ensure initialized\n yield _this5.initialize();\n const results = new Map();\n // Filter out pubkeys we already have cached\n const uncachedPubkeys = [];\n for (const pubkey of pubkeys) {\n const cached = _this5.getCachedProfile(pubkey);\n if (cached) {\n results.set(pubkey, cached);\n } else {\n uncachedPubkeys.push(pubkey);\n }\n }\n if (uncachedPubkeys.length === 0) {\n return results;\n }\n // Fetch all uncached profiles\n const pool = _this5.#getPool();\n try {\n const events = yield _this5.#queryWithTimeout(pool, FALLBACK_PROFILE_RELAYS, [{\n kinds: [0],\n authors: uncachedPubkeys\n }], FETCH_TIMEOUT_MS);\n // Process events - keep only the most recent event per pubkey\n const latestEvents = new Map();\n for (const event of events) {\n const existing = latestEvents.get(event.pubkey);\n if (!existing || event.created_at > existing.created_at) {\n latestEvents.set(event.pubkey, {\n created_at: event.created_at,\n content: event.content\n });\n }\n }\n // Parse and cache the profiles\n for (const [pubkey, eventData] of latestEvents) {\n try {\n const content = JSON.parse(eventData.content);\n const profile = {\n pubkey,\n name: content.name,\n display_name: content.display_name,\n displayName: content.displayName,\n picture: content.picture,\n banner: content.banner,\n about: content.about,\n website: content.website,\n nip05: content.nip05,\n lud06: content.lud06,\n lud16: content.lud16,\n fetchedAt: Date.now()\n };\n _this5.#cache[pubkey] = profile;\n results.set(pubkey, profile);\n } catch {\n _this5.#logger.logProfileParseError(pubkey);\n results.set(pubkey, null);\n }\n }\n // Set null for pubkeys we didn't find\n for (const pubkey of uncachedPubkeys) {\n if (!results.has(pubkey)) {\n results.set(pubkey, null);\n }\n }\n // Save updated cache to storage\n yield _this5.#saveCacheToStorage();\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : 'Unknown error';\n _this5.#logger.logProfileFetchError('multiple', errorMsg);\n // Set null for all unfetched pubkeys on error\n for (const pubkey of uncachedPubkeys) {\n if (!results.has(pubkey)) {\n results.set(pubkey, null);\n }\n }\n }\n return results;\n })();\n }\n /**\n * Internal method to fetch a single profile\n */\n #doFetchProfile(pubkey) {\n var _this6 = this;\n return _asyncToGenerator(function* () {\n const pool = _this6.#getPool();\n try {\n const events = yield _this6.#queryWithTimeout(pool, FALLBACK_PROFILE_RELAYS, [{\n kinds: [0],\n authors: [pubkey]\n }], FETCH_TIMEOUT_MS);\n if (events.length === 0) {\n return null;\n }\n // Get the most recent event\n const latestEvent = events.reduce((latest, event) => event.created_at > latest.created_at ? event : latest);\n try {\n const content = JSON.parse(latestEvent.content);\n const profile = {\n pubkey,\n name: content.name,\n display_name: content.display_name,\n displayName: content.displayName,\n picture: content.picture,\n banner: content.banner,\n about: content.about,\n website: content.website,\n nip05: content.nip05,\n lud06: content.lud06,\n lud16: content.lud16,\n fetchedAt: Date.now()\n };\n _this6.#cache[pubkey] = profile;\n // Save updated cache to storage\n yield _this6.#saveCacheToStorage();\n return profile;\n } catch {\n _this6.#logger.logProfileParseError(pubkey);\n return null;\n }\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : 'Unknown error';\n _this6.#logger.logProfileFetchError(pubkey, errorMsg);\n return null;\n }\n })();\n }\n /**\n * Query relays with a timeout\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n #queryWithTimeout(pool, relays, filters, timeoutMs) {\n return _asyncToGenerator(function* () {\n return new Promise(resolve => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const events = [];\n let settled = false;\n const timeout = setTimeout(() => {\n if (!settled) {\n settled = true;\n resolve(events);\n }\n }, timeoutMs);\n const sub = pool.subscribeMany(relays, filters, {\n onevent(event) {\n events.push(event);\n },\n oneose() {\n if (!settled) {\n settled = true;\n clearTimeout(timeout);\n sub.close();\n resolve(events);\n }\n }\n });\n });\n })();\n }\n /**\n * Clear the cache\n */\n clearCache() {\n var _this7 = this;\n return _asyncToGenerator(function* () {\n _this7.#cache = {};\n yield _this7.#saveCacheToStorage();\n })();\n }\n /**\n * Clear cache for a specific pubkey\n */\n clearCacheForPubkey(pubkey) {\n var _this8 = this;\n return _asyncToGenerator(function* () {\n delete _this8.#cache[pubkey];\n yield _this8.#saveCacheToStorage();\n })();\n }\n /**\n * Get the display name for a profile (prioritizes display_name over name)\n */\n getDisplayName(profile) {\n if (!profile) return undefined;\n return profile.display_name || profile.displayName || profile.name;\n }\n /**\n * Get the username for a profile (prioritizes name over display_name)\n */\n getUsername(profile) {\n if (!profile) return undefined;\n return profile.name || profile.display_name || profile.displayName;\n }\n static ɵfac = function ProfileMetadataService_Factory(__ngFactoryType__) {\n return new (__ngFactoryType__ || ProfileMetadataService)();\n };\n static ɵprov = /*@__PURE__*/i0.ɵɵdefineInjectable({\n token: ProfileMetadataService,\n factory: ProfileMetadataService.ɵfac,\n providedIn: 'root'\n });\n }\n return ProfileMetadataService;\n})();","map":null,"metadata":{},"sourceType":"module","externalDependencies":[]}