65 changed files with 613 additions and 1919 deletions
@ -1,29 +0,0 @@ |
|||||||
/** |
|
||||||
* Activity tracker - tracks last activity per pubkey |
|
||||||
*/ |
|
||||||
import { eventStore } from '../nostr/event-store.js'; |
|
||||||
/** |
|
||||||
* Get last activity timestamp for a pubkey |
|
||||||
*/ |
|
||||||
export function getLastActivity(pubkey) { |
|
||||||
return eventStore.getLastActivity(pubkey); |
|
||||||
} |
|
||||||
/** |
|
||||||
* Get activity status color |
|
||||||
* Red: ≥168 hours (7 days) |
|
||||||
* Yellow: ≥48 hours (2 days) but <168 hours |
|
||||||
* Green: <48 hours |
|
||||||
*/ |
|
||||||
export function getActivityStatus(pubkey) { |
|
||||||
const lastActivity = getLastActivity(pubkey); |
|
||||||
if (!lastActivity) |
|
||||||
return null; |
|
||||||
const now = Math.floor(Date.now() / 1000); |
|
||||||
const hoursSince = (now - lastActivity) / 3600; |
|
||||||
if (hoursSince >= 168) |
|
||||||
return 'red'; |
|
||||||
if (hoursSince >= 48) |
|
||||||
return 'yellow'; |
|
||||||
return 'green'; |
|
||||||
} |
|
||||||
//# sourceMappingURL=activity-tracker.js.map
|
|
||||||
@ -1 +0,0 @@ |
|||||||
{"version":3,"file":"activity-tracker.js","sourceRoot":"","sources":["activity-tracker.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAErD;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,MAAc;IAC5C,OAAO,UAAU,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;AAC5C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAc;IAC9C,MAAM,YAAY,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IAC7C,IAAI,CAAC,YAAY;QAAE,OAAO,IAAI,CAAC;IAE/B,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IAC1C,MAAM,UAAU,GAAG,CAAC,GAAG,GAAG,YAAY,CAAC,GAAG,IAAI,CAAC;IAE/C,IAAI,UAAU,IAAI,GAAG;QAAE,OAAO,KAAK,CAAC;IACpC,IAAI,UAAU,IAAI,EAAE;QAAE,OAAO,QAAQ,CAAC;IACtC,OAAO,OAAO,CAAC;AACjB,CAAC"} |
|
||||||
@ -1,45 +0,0 @@ |
|||||||
/** |
|
||||||
* Anonymous signer (generated keys, NIP-49 encrypted) |
|
||||||
*/ |
|
||||||
import { generatePrivateKey } from '../security/key-management.js'; |
|
||||||
import { storeAnonymousKey, getAnonymousKey } from '../cache/anonymous-key-store.js'; |
|
||||||
import { getPublicKeyFromNsec } from './nsec-signer.js'; |
|
||||||
import { signEventWithNsec } from './nsec-signer.js'; |
|
||||||
/** |
|
||||||
* Generate and store anonymous key |
|
||||||
*/ |
|
||||||
export async function generateAnonymousKey(password) { |
|
||||||
const nsec = generatePrivateKey(); |
|
||||||
const pubkey = await getPublicKeyFromNsec(nsec); |
|
||||||
// Store encrypted
|
|
||||||
await storeAnonymousKey(nsec, password, pubkey); |
|
||||||
return { pubkey, nsec }; |
|
||||||
} |
|
||||||
/** |
|
||||||
* Get stored anonymous key |
|
||||||
*/ |
|
||||||
export async function getStoredAnonymousKey(pubkey, password) { |
|
||||||
return getAnonymousKey(pubkey, password); |
|
||||||
} |
|
||||||
/** |
|
||||||
* Sign event with anonymous key |
|
||||||
*/ |
|
||||||
export async function signEventWithAnonymous(event, pubkey, password) { |
|
||||||
const nsec = await getStoredAnonymousKey(pubkey, password); |
|
||||||
if (!nsec) { |
|
||||||
throw new Error('Anonymous key not found'); |
|
||||||
} |
|
||||||
// For anonymous keys, we need the ncryptsec format
|
|
||||||
// This is simplified - in practice we'd store ncryptsec and decrypt it
|
|
||||||
// For now, assume we have the plain nsec after decryption
|
|
||||||
return signEventWithNsec(event, nsec, password); |
|
||||||
} |
|
||||||
/** |
|
||||||
* Generate anonymous handle |
|
||||||
*/ |
|
||||||
export function generateAnonymousHandle(pubkey) { |
|
||||||
// Use last 6 characters of pubkey for uniqueness
|
|
||||||
const suffix = pubkey.slice(-6); |
|
||||||
return `Aitherite${suffix}`; |
|
||||||
} |
|
||||||
//# sourceMappingURL=anonymous-signer.js.map
|
|
||||||
@ -1 +0,0 @@ |
|||||||
{"version":3,"file":"anonymous-signer.js","sourceRoot":"","sources":["anonymous-signer.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AACrF,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAGrD;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,QAAgB;IAIzD,MAAM,IAAI,GAAG,kBAAkB,EAAE,CAAC;IAClC,MAAM,MAAM,GAAG,MAAM,oBAAoB,CAAC,IAAI,CAAC,CAAC;IAEhD,kBAAkB;IAClB,MAAM,iBAAiB,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;IAEhD,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AAC1B,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,MAAc,EACd,QAAgB;IAEhB,OAAO,eAAe,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;AAC3C,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,KAAqC,EACrC,MAAc,EACd,QAAgB;IAEhB,MAAM,IAAI,GAAG,MAAM,qBAAqB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC3D,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IAED,mDAAmD;IACnD,uEAAuE;IACvE,0DAA0D;IAC1D,OAAO,iBAAiB,CAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;AAClD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,uBAAuB,CAAC,MAAc;IACpD,iDAAiD;IACjD,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAChC,OAAO,YAAY,MAAM,EAAE,CAAC;AAC9B,CAAC"} |
|
||||||
@ -1,31 +0,0 @@ |
|||||||
/** |
|
||||||
* NIP-46 Bunker signer (remote signer) |
|
||||||
*/ |
|
||||||
/** |
|
||||||
* Connect to bunker signer |
|
||||||
*/ |
|
||||||
export async function connectBunker(bunkerUri) { |
|
||||||
// Parse bunker:// URI
|
|
||||||
// Format: bunker://<pubkey>@<relay>?token=<token>
|
|
||||||
const match = bunkerUri.match(/^bunker:\/\/([^@]+)@([^?]+)(?:\?token=([^&]+))?$/); |
|
||||||
if (!match) { |
|
||||||
throw new Error('Invalid bunker URI'); |
|
||||||
} |
|
||||||
const [, pubkey, relay, token] = match; |
|
||||||
return { |
|
||||||
bunkerUrl: relay, |
|
||||||
pubkey, |
|
||||||
token |
|
||||||
}; |
|
||||||
} |
|
||||||
/** |
|
||||||
* Sign event with bunker |
|
||||||
*/ |
|
||||||
export async function signEventWithBunker(event, connection) { |
|
||||||
// Placeholder - would:
|
|
||||||
// 1. Send NIP-46 request to bunker
|
|
||||||
// 2. Wait for response
|
|
||||||
// 3. Return signed event
|
|
||||||
throw new Error('Bunker signing not yet implemented'); |
|
||||||
} |
|
||||||
//# sourceMappingURL=bunker-signer.js.map
|
|
||||||
@ -1 +0,0 @@ |
|||||||
{"version":3,"file":"bunker-signer.js","sourceRoot":"","sources":["bunker-signer.ts"],"names":[],"mappings":"AAAA;;GAEG;AAUH;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,SAAiB;IACnD,sBAAsB;IACtB,kDAAkD;IAClD,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,kDAAkD,CAAC,CAAC;IAClF,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC;IACxC,CAAC;IAED,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,KAAK,CAAC;IAEvC,OAAO;QACL,SAAS,EAAE,KAAK;QAChB,MAAM;QACN,KAAK;KACN,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,KAAqC,EACrC,UAA4B;IAE5B,uBAAuB;IACvB,mCAAmC;IACnC,uBAAuB;IACvB,yBAAyB;IAEzB,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;AACxD,CAAC"} |
|
||||||
@ -1,39 +0,0 @@ |
|||||||
/** |
|
||||||
* NIP-07 signer (browser extension) |
|
||||||
*/ |
|
||||||
/** |
|
||||||
* Check if NIP-07 is available |
|
||||||
*/ |
|
||||||
export function isNIP07Available() { |
|
||||||
return typeof window !== 'undefined' && 'nostr' in window; |
|
||||||
} |
|
||||||
/** |
|
||||||
* Get NIP-07 signer |
|
||||||
*/ |
|
||||||
export function getNIP07Signer() { |
|
||||||
if (!isNIP07Available()) |
|
||||||
return null; |
|
||||||
const nostr = window.nostr; |
|
||||||
return nostr || null; |
|
||||||
} |
|
||||||
/** |
|
||||||
* Sign event with NIP-07 |
|
||||||
*/ |
|
||||||
export async function signEventWithNIP07(event) { |
|
||||||
const signer = getNIP07Signer(); |
|
||||||
if (!signer) { |
|
||||||
throw new Error('NIP-07 not available'); |
|
||||||
} |
|
||||||
return signer.signEvent(event); |
|
||||||
} |
|
||||||
/** |
|
||||||
* Get public key with NIP-07 |
|
||||||
*/ |
|
||||||
export async function getPublicKeyWithNIP07() { |
|
||||||
const signer = getNIP07Signer(); |
|
||||||
if (!signer) { |
|
||||||
throw new Error('NIP-07 not available'); |
|
||||||
} |
|
||||||
return signer.getPublicKey(); |
|
||||||
} |
|
||||||
//# sourceMappingURL=nip07-signer.js.map
|
|
||||||
@ -1 +0,0 @@ |
|||||||
{"version":3,"file":"nip07-signer.js","sourceRoot":"","sources":["nip07-signer.ts"],"names":[],"mappings":"AAAA;;GAEG;AASH;;GAEG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO,OAAO,MAAM,KAAK,WAAW,IAAI,OAAO,IAAI,MAAM,CAAC;AAC5D,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc;IAC5B,IAAI,CAAC,gBAAgB,EAAE;QAAE,OAAO,IAAI,CAAC;IAErC,MAAM,KAAK,GAAI,MAAkC,CAAC,KAAK,CAAC;IACxD,OAAO,KAAK,IAAI,IAAI,CAAC;AACvB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,KAAqC;IAErC,MAAM,MAAM,GAAG,cAAc,EAAE,CAAC;IAChC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;IAC1C,CAAC;IAED,OAAO,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;AACjC,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB;IACzC,MAAM,MAAM,GAAG,cAAc,EAAE,CAAC;IAChC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;IAC1C,CAAC;IAED,OAAO,MAAM,CAAC,YAAY,EAAE,CAAC;AAC/B,CAAC"} |
|
||||||
@ -1,62 +0,0 @@ |
|||||||
/** |
|
||||||
* Nsec signer (direct private key, NIP-49 encrypted) |
|
||||||
*/ |
|
||||||
import { decryptPrivateKey } from '../security/key-management.js'; |
|
||||||
/** |
|
||||||
* Sign event with nsec (private key) |
|
||||||
* This is a placeholder - full implementation requires: |
|
||||||
* - secp256k1 cryptography library |
|
||||||
* - Event ID computation (SHA256) |
|
||||||
* - Signature computation |
|
||||||
*/ |
|
||||||
export async function signEventWithNsec(event, ncryptsec, password) { |
|
||||||
// Decrypt private key
|
|
||||||
const nsec = await decryptPrivateKey(ncryptsec, password); |
|
||||||
// Compute event ID (SHA256 of serialized event)
|
|
||||||
const serialized = JSON.stringify([ |
|
||||||
0, |
|
||||||
event.pubkey, |
|
||||||
event.created_at, |
|
||||||
event.kind, |
|
||||||
event.tags, |
|
||||||
event.content |
|
||||||
]); |
|
||||||
const encoder = new TextEncoder(); |
|
||||||
const data = encoder.encode(serialized); |
|
||||||
const hashBuffer = await crypto.subtle.digest('SHA-256', data); |
|
||||||
const hashArray = Array.from(new Uint8Array(hashBuffer)); |
|
||||||
const id = hashArray.map((b) => b.toString(16).padStart(2, '0')).join(''); |
|
||||||
// TEMPORARY: Generate a deterministic signature-like string
|
|
||||||
// This is NOT a valid secp256k1 signature but has the correct length
|
|
||||||
// Production code MUST compute actual secp256k1 signature
|
|
||||||
const sigData = encoder.encode(nsec + id); |
|
||||||
const sigHashBuffer = await crypto.subtle.digest('SHA-256', sigData); |
|
||||||
const sigHashArray = Array.from(new Uint8Array(sigHashBuffer)); |
|
||||||
const sigHash = sigHashArray.map((b) => b.toString(16).padStart(2, '0')).join(''); |
|
||||||
// Double the hash to get 128 chars (64 * 2)
|
|
||||||
const sig = (sigHash + sigHash).slice(0, 128); |
|
||||||
return { |
|
||||||
...event, |
|
||||||
id, |
|
||||||
sig |
|
||||||
}; |
|
||||||
} |
|
||||||
/** |
|
||||||
* Get public key from private key |
|
||||||
* |
|
||||||
* TEMPORARY: Uses SHA256 hash of private key to generate a deterministic pubkey. |
|
||||||
* This is NOT a valid secp256k1 public key derivation but provides unique pubkeys. |
|
||||||
* Production code MUST use proper secp256k1 point multiplication. |
|
||||||
*/ |
|
||||||
export async function getPublicKeyFromNsec(nsec) { |
|
||||||
// TEMPORARY: Generate deterministic pubkey from private key hash
|
|
||||||
// This ensures each private key gets a unique (but not cryptographically valid) pubkey
|
|
||||||
const encoder = new TextEncoder(); |
|
||||||
const data = encoder.encode(nsec); |
|
||||||
const hashBuffer = await crypto.subtle.digest('SHA-256', data); |
|
||||||
const hashArray = Array.from(new Uint8Array(hashBuffer)); |
|
||||||
const hash = hashArray.map((b) => b.toString(16).padStart(2, '0')).join(''); |
|
||||||
// Double the hash to get 64 chars (32 * 2)
|
|
||||||
return (hash + hash).slice(0, 64); |
|
||||||
} |
|
||||||
//# sourceMappingURL=nsec-signer.js.map
|
|
||||||
@ -1 +0,0 @@ |
|||||||
{"version":3,"file":"nsec-signer.js","sourceRoot":"","sources":["nsec-signer.ts"],"names":[],"mappings":"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"} |
|
||||||
@ -1,98 +0,0 @@ |
|||||||
/** |
|
||||||
* Profile fetcher (kind 0 events) |
|
||||||
*/ |
|
||||||
import { nostrClient } from '../nostr/applesauce-client.js'; |
|
||||||
import { cacheProfile, getProfile, getProfiles } from '../cache/profile-cache.js'; |
|
||||||
import { config } from '../nostr/config.js'; |
|
||||||
/** |
|
||||||
* Parse profile from kind 0 event |
|
||||||
*/ |
|
||||||
export function parseProfile(event) { |
|
||||||
const profile = {}; |
|
||||||
// Try to parse from tags first (preferred)
|
|
||||||
const nameTag = event.tags.find((t) => t[0] === 'name'); |
|
||||||
if (nameTag && nameTag[1]) |
|
||||||
profile.name = nameTag[1]; |
|
||||||
const aboutTag = event.tags.find((t) => t[0] === 'about'); |
|
||||||
if (aboutTag && aboutTag[1]) |
|
||||||
profile.about = aboutTag[1]; |
|
||||||
const pictureTag = event.tags.find((t) => t[0] === 'picture'); |
|
||||||
if (pictureTag && pictureTag[1]) |
|
||||||
profile.picture = pictureTag[1]; |
|
||||||
// Multiple tags for website, nip05, lud16
|
|
||||||
profile.website = event.tags.filter((t) => t[0] === 'website').map((t) => t[1]).filter(Boolean); |
|
||||||
profile.nip05 = event.tags.filter((t) => t[0] === 'nip05').map((t) => t[1]).filter(Boolean); |
|
||||||
profile.lud16 = event.tags.filter((t) => t[0] === 'lud16').map((t) => t[1]).filter(Boolean); |
|
||||||
// Fallback to JSON content if tags not found
|
|
||||||
if (!profile.name || !profile.about) { |
|
||||||
try { |
|
||||||
const json = JSON.parse(event.content); |
|
||||||
if (json.name && !profile.name) |
|
||||||
profile.name = json.name; |
|
||||||
if (json.about && !profile.about) |
|
||||||
profile.about = json.about; |
|
||||||
if (json.picture && !profile.picture) |
|
||||||
profile.picture = json.picture; |
|
||||||
if (json.website && profile.website.length === 0) { |
|
||||||
profile.website = Array.isArray(json.website) ? json.website : [json.website]; |
|
||||||
} |
|
||||||
if (json.nip05 && profile.nip05.length === 0) { |
|
||||||
profile.nip05 = Array.isArray(json.nip05) ? json.nip05 : [json.nip05]; |
|
||||||
} |
|
||||||
if (json.lud16 && profile.lud16.length === 0) { |
|
||||||
profile.lud16 = Array.isArray(json.lud16) ? json.lud16 : [json.lud16]; |
|
||||||
} |
|
||||||
} |
|
||||||
catch { |
|
||||||
// Invalid JSON, ignore
|
|
||||||
} |
|
||||||
} |
|
||||||
return profile; |
|
||||||
} |
|
||||||
/** |
|
||||||
* Fetch profile for a pubkey |
|
||||||
*/ |
|
||||||
export async function fetchProfile(pubkey, relays) { |
|
||||||
// Try cache first
|
|
||||||
const cached = await getProfile(pubkey); |
|
||||||
if (cached) { |
|
||||||
return parseProfile(cached.event); |
|
||||||
} |
|
||||||
// Fetch from relays
|
|
||||||
const relayList = relays || [ |
|
||||||
...config.defaultRelays, |
|
||||||
...config.profileRelays |
|
||||||
]; |
|
||||||
const events = await nostrClient.fetchEvents([{ kinds: [0], authors: [pubkey], limit: 1 }], relayList, { useCache: true, cacheResults: true }); |
|
||||||
if (events.length === 0) |
|
||||||
return null; |
|
||||||
const event = events[0]; |
|
||||||
await cacheProfile(event); |
|
||||||
return parseProfile(event); |
|
||||||
} |
|
||||||
/** |
|
||||||
* Fetch multiple profiles |
|
||||||
*/ |
|
||||||
export async function fetchProfiles(pubkeys, relays) { |
|
||||||
const profiles = new Map(); |
|
||||||
// Check cache first
|
|
||||||
const cached = await getProfiles(pubkeys); |
|
||||||
for (const [pubkey, cachedProfile] of cached.entries()) { |
|
||||||
profiles.set(pubkey, parseProfile(cachedProfile.event)); |
|
||||||
} |
|
||||||
// Fetch missing profiles
|
|
||||||
const missing = pubkeys.filter((p) => !profiles.has(p)); |
|
||||||
if (missing.length === 0) |
|
||||||
return profiles; |
|
||||||
const relayList = relays || [ |
|
||||||
...config.defaultRelays, |
|
||||||
...config.profileRelays |
|
||||||
]; |
|
||||||
const events = await nostrClient.fetchEvents([{ kinds: [0], authors: missing, limit: 1 }], relayList, { useCache: true, cacheResults: true }); |
|
||||||
for (const event of events) { |
|
||||||
await cacheProfile(event); |
|
||||||
profiles.set(event.pubkey, parseProfile(event)); |
|
||||||
} |
|
||||||
return profiles; |
|
||||||
} |
|
||||||
//# sourceMappingURL=profile-fetcher.js.map
|
|
||||||
@ -1 +0,0 @@ |
|||||||
{"version":3,"file":"profile-fetcher.js","sourceRoot":"","sources":["profile-fetcher.ts"],"names":[],"mappings":"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"} |
|
||||||
@ -1,65 +0,0 @@ |
|||||||
/** |
|
||||||
* Relay list fetcher (kind 10002 and 10432) |
|
||||||
*/ |
|
||||||
import { nostrClient } from '../nostr/applesauce-client.js'; |
|
||||||
import { config } from '../nostr/config.js'; |
|
||||||
/** |
|
||||||
* Parse relay list from event |
|
||||||
*/ |
|
||||||
export function parseRelayList(event) { |
|
||||||
const relays = []; |
|
||||||
for (const tag of event.tags) { |
|
||||||
if (tag[0] === 'r' && tag[1]) { |
|
||||||
const url = tag[1]; |
|
||||||
const markers = tag.slice(2); |
|
||||||
// If no markers, relay is both read and write
|
|
||||||
if (markers.length === 0) { |
|
||||||
relays.push({ url, read: true, write: true }); |
|
||||||
continue; |
|
||||||
} |
|
||||||
// Check for explicit markers
|
|
||||||
const hasRead = markers.includes('read'); |
|
||||||
const hasWrite = markers.includes('write'); |
|
||||||
// If only 'read' marker: read=true, write=false
|
|
||||||
// If only 'write' marker: read=false, write=true
|
|
||||||
// If both or neither explicitly: both true (default behavior)
|
|
||||||
const read = hasRead || (!hasRead && !hasWrite); |
|
||||||
const write = hasWrite || (!hasRead && !hasWrite); |
|
||||||
relays.push({ url, read, write }); |
|
||||||
} |
|
||||||
} |
|
||||||
return relays; |
|
||||||
} |
|
||||||
/** |
|
||||||
* Fetch relay lists for a pubkey (kind 10002 and 10432) |
|
||||||
*/ |
|
||||||
export async function fetchRelayLists(pubkey, relays) { |
|
||||||
const relayList = relays || [ |
|
||||||
...config.defaultRelays, |
|
||||||
...config.profileRelays |
|
||||||
]; |
|
||||||
// Fetch both kind 10002 and 10432
|
|
||||||
const events = await nostrClient.fetchEvents([ |
|
||||||
{ kinds: [10002], authors: [pubkey], limit: 1 }, |
|
||||||
{ kinds: [10432], authors: [pubkey], limit: 1 } |
|
||||||
], relayList, { useCache: true, cacheResults: true }); |
|
||||||
const inbox = []; |
|
||||||
const outbox = []; |
|
||||||
for (const event of events) { |
|
||||||
const relayInfos = parseRelayList(event); |
|
||||||
for (const info of relayInfos) { |
|
||||||
if (info.read && !inbox.includes(info.url)) { |
|
||||||
inbox.push(info.url); |
|
||||||
} |
|
||||||
if (info.write && !outbox.includes(info.url)) { |
|
||||||
outbox.push(info.url); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
// Deduplicate
|
|
||||||
return { |
|
||||||
inbox: [...new Set(inbox)], |
|
||||||
outbox: [...new Set(outbox)] |
|
||||||
}; |
|
||||||
} |
|
||||||
//# sourceMappingURL=relay-list-fetcher.js.map
|
|
||||||
@ -1 +0,0 @@ |
|||||||
{"version":3,"file":"relay-list-fetcher.js","sourceRoot":"","sources":["relay-list-fetcher.ts"],"names":[],"mappings":"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"} |
|
||||||
@ -1,99 +0,0 @@ |
|||||||
/** |
|
||||||
* Session manager for active user sessions |
|
||||||
*/ |
|
||||||
// Simple store implementation for Svelte reactivity
|
|
||||||
function createStore(initial) { |
|
||||||
let value = initial; |
|
||||||
const subscribers = new Set(); |
|
||||||
return { |
|
||||||
get value() { |
|
||||||
return value; |
|
||||||
}, |
|
||||||
set(newValue) { |
|
||||||
value = newValue; |
|
||||||
subscribers.forEach((fn) => fn(value)); |
|
||||||
}, |
|
||||||
subscribe(fn) { |
|
||||||
subscribers.add(fn); |
|
||||||
fn(value); |
|
||||||
return () => subscribers.delete(fn); |
|
||||||
} |
|
||||||
}; |
|
||||||
} |
|
||||||
class SessionManager { |
|
||||||
currentSession = null; |
|
||||||
session = createStore(null); |
|
||||||
/** |
|
||||||
* Set current session |
|
||||||
*/ |
|
||||||
setSession(session) { |
|
||||||
this.currentSession = session; |
|
||||||
this.session.set(session); |
|
||||||
// Store in localStorage for persistence
|
|
||||||
if (typeof window !== 'undefined') { |
|
||||||
localStorage.setItem('aitherboard_session', JSON.stringify({ |
|
||||||
pubkey: session.pubkey, |
|
||||||
method: session.method, |
|
||||||
createdAt: session.createdAt |
|
||||||
})); |
|
||||||
} |
|
||||||
} |
|
||||||
/** |
|
||||||
* Get current session |
|
||||||
*/ |
|
||||||
getSession() { |
|
||||||
return this.currentSession; |
|
||||||
} |
|
||||||
/** |
|
||||||
* Check if user is logged in |
|
||||||
*/ |
|
||||||
isLoggedIn() { |
|
||||||
return this.currentSession !== null; |
|
||||||
} |
|
||||||
/** |
|
||||||
* Get current pubkey |
|
||||||
*/ |
|
||||||
getCurrentPubkey() { |
|
||||||
return this.currentSession?.pubkey || null; |
|
||||||
} |
|
||||||
/** |
|
||||||
* Sign event with current session |
|
||||||
*/ |
|
||||||
async signEvent(event) { |
|
||||||
if (!this.currentSession) { |
|
||||||
throw new Error('No active session'); |
|
||||||
} |
|
||||||
return this.currentSession.signer(event); |
|
||||||
} |
|
||||||
/** |
|
||||||
* Clear session |
|
||||||
*/ |
|
||||||
clearSession() { |
|
||||||
this.currentSession = null; |
|
||||||
this.session.set(null); |
|
||||||
if (typeof window !== 'undefined') { |
|
||||||
localStorage.removeItem('aitherboard_session'); |
|
||||||
} |
|
||||||
} |
|
||||||
/** |
|
||||||
* Restore session from localStorage |
|
||||||
*/ |
|
||||||
async restoreSession() { |
|
||||||
if (typeof window === 'undefined') |
|
||||||
return false; |
|
||||||
const stored = localStorage.getItem('aitherboard_session'); |
|
||||||
if (!stored) |
|
||||||
return false; |
|
||||||
try { |
|
||||||
const data = JSON.parse(stored); |
|
||||||
// Session restoration would require re-initializing the signer
|
|
||||||
// This is simplified - full implementation would restore the signer
|
|
||||||
return false; |
|
||||||
} |
|
||||||
catch { |
|
||||||
return false; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
export const sessionManager = new SessionManager(); |
|
||||||
//# sourceMappingURL=session-manager.js.map
|
|
||||||
@ -1 +0,0 @@ |
|||||||
{"version":3,"file":"session-manager.js","sourceRoot":"","sources":["session-manager.ts"],"names":[],"mappings":"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"} |
|
||||||
@ -1,12 +0,0 @@ |
|||||||
/** |
|
||||||
* User preferences fetcher |
|
||||||
* Placeholder for future user preference events |
|
||||||
*/ |
|
||||||
/** |
|
||||||
* Fetch user preferences |
|
||||||
*/ |
|
||||||
export async function fetchUserPreferences(pubkey) { |
|
||||||
// Placeholder - would fetch preference events
|
|
||||||
return null; |
|
||||||
} |
|
||||||
//# sourceMappingURL=user-preferences-fetcher.js.map
|
|
||||||
@ -1 +0,0 @@ |
|||||||
{"version":3,"file":"user-preferences-fetcher.js","sourceRoot":"","sources":["user-preferences-fetcher.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,MAAc;IACvD,8CAA8C;IAC9C,OAAO,IAAI,CAAC;AACd,CAAC"} |
|
||||||
@ -1,38 +0,0 @@ |
|||||||
/** |
|
||||||
* User status fetcher (kind 30315, NIP-38) |
|
||||||
*/ |
|
||||||
import { nostrClient } from '../nostr/applesauce-client.js'; |
|
||||||
import { config } from '../nostr/config.js'; |
|
||||||
/** |
|
||||||
* Parse user status from kind 30315 event |
|
||||||
*/ |
|
||||||
export function parseUserStatus(event) { |
|
||||||
if (event.kind !== 30315) |
|
||||||
return null; |
|
||||||
// Check for d tag with value "general"
|
|
||||||
const dTag = event.tags.find((t) => t[0] === 'd' && t[1] === 'general'); |
|
||||||
if (!dTag) |
|
||||||
return null; |
|
||||||
return event.content || null; |
|
||||||
} |
|
||||||
/** |
|
||||||
* Fetch user status for a pubkey |
|
||||||
*/ |
|
||||||
export async function fetchUserStatus(pubkey, relays) { |
|
||||||
const relayList = relays || [ |
|
||||||
...config.defaultRelays, |
|
||||||
...config.profileRelays |
|
||||||
]; |
|
||||||
const events = await nostrClient.fetchEvents([ |
|
||||||
{ |
|
||||||
kinds: [30315], |
|
||||||
authors: [pubkey], |
|
||||||
'#d': ['general'], |
|
||||||
limit: 1 |
|
||||||
} |
|
||||||
], relayList, { useCache: true, cacheResults: true }); |
|
||||||
if (events.length === 0) |
|
||||||
return null; |
|
||||||
return parseUserStatus(events[0]); |
|
||||||
} |
|
||||||
//# sourceMappingURL=user-status-fetcher.js.map
|
|
||||||
@ -1 +0,0 @@ |
|||||||
{"version":3,"file":"user-status-fetcher.js","sourceRoot":"","sources":["user-status-fetcher.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAC5D,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAG5C;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,KAAiB;IAC/C,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK;QAAE,OAAO,IAAI,CAAC;IAEtC,uCAAuC;IACvC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC;IACxE,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IAEvB,OAAO,KAAK,CAAC,OAAO,IAAI,IAAI,CAAC;AAC/B,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,MAAc,EACd,MAAiB;IAEjB,MAAM,SAAS,GAAG,MAAM,IAAI;QAC1B,GAAG,MAAM,CAAC,aAAa;QACvB,GAAG,MAAM,CAAC,aAAa;KACxB,CAAC;IAEF,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,WAAW,CAC1C;QACE;YACE,KAAK,EAAE,CAAC,KAAK,CAAC;YACd,OAAO,EAAE,CAAC,MAAM,CAAC;YACjB,IAAI,EAAE,CAAC,SAAS,CAAC;YACjB,KAAK,EAAE,CAAC;SACT;KACF,EACD,SAAS,EACT,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,CACvC,CAAC;IAEF,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAErC,OAAO,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AACpC,CAAC"} |
|
||||||
@ -1,52 +0,0 @@ |
|||||||
/** |
|
||||||
* Anonymous key storage (NIP-49 encrypted) |
|
||||||
*/ |
|
||||||
import { getDB } from './indexeddb-store.js'; |
|
||||||
import { encryptPrivateKey, decryptPrivateKey } from '../security/key-management.js'; |
|
||||||
/** |
|
||||||
* Store an anonymous key (encrypted) |
|
||||||
*/ |
|
||||||
export async function storeAnonymousKey(nsec, password, pubkey) { |
|
||||||
const ncryptsec = await encryptPrivateKey(nsec, password); |
|
||||||
const db = await getDB(); |
|
||||||
const stored = { |
|
||||||
id: pubkey, |
|
||||||
ncryptsec, |
|
||||||
pubkey, |
|
||||||
created_at: Date.now() |
|
||||||
}; |
|
||||||
await db.put('keys', stored); |
|
||||||
} |
|
||||||
/** |
|
||||||
* Retrieve and decrypt an anonymous key |
|
||||||
*/ |
|
||||||
export async function getAnonymousKey(pubkey, password) { |
|
||||||
const db = await getDB(); |
|
||||||
const stored = await db.get('keys', pubkey); |
|
||||||
if (!stored) |
|
||||||
return null; |
|
||||||
const key = stored; |
|
||||||
return decryptPrivateKey(key.ncryptsec, password); |
|
||||||
} |
|
||||||
/** |
|
||||||
* List all stored anonymous keys (pubkeys only) |
|
||||||
*/ |
|
||||||
export async function listAnonymousKeys() { |
|
||||||
const db = await getDB(); |
|
||||||
const keys = []; |
|
||||||
const tx = db.transaction('keys', 'readonly'); |
|
||||||
for await (const cursor of tx.store.iterate()) { |
|
||||||
const key = cursor.value; |
|
||||||
keys.push(key.pubkey); |
|
||||||
} |
|
||||||
await tx.done; |
|
||||||
return keys; |
|
||||||
} |
|
||||||
/** |
|
||||||
* Delete an anonymous key |
|
||||||
*/ |
|
||||||
export async function deleteAnonymousKey(pubkey) { |
|
||||||
const db = await getDB(); |
|
||||||
await db.delete('keys', pubkey); |
|
||||||
} |
|
||||||
//# sourceMappingURL=anonymous-key-store.js.map
|
|
||||||
@ -1 +0,0 @@ |
|||||||
{"version":3,"file":"anonymous-key-store.js","sourceRoot":"","sources":["anonymous-key-store.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAC7C,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AASrF;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,IAAY,EACZ,QAAgB,EAChB,MAAc;IAEd,MAAM,SAAS,GAAG,MAAM,iBAAiB,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAC1D,MAAM,EAAE,GAAG,MAAM,KAAK,EAAE,CAAC;IACzB,MAAM,MAAM,GAAuB;QACjC,EAAE,EAAE,MAAM;QACV,SAAS;QACT,MAAM;QACN,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE;KACvB,CAAC;IACF,MAAM,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAC/B,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,MAAc,EACd,QAAgB;IAEhB,MAAM,EAAE,GAAG,MAAM,KAAK,EAAE,CAAC;IACzB,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAEzB,MAAM,GAAG,GAAG,MAA4B,CAAC;IACzC,OAAO,iBAAiB,CAAC,GAAG,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AACpD,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB;IACrC,MAAM,EAAE,GAAG,MAAM,KAAK,EAAE,CAAC;IACzB,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,MAAM,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAE9C,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;QAC9C,MAAM,GAAG,GAAG,MAAM,CAAC,KAA2B,CAAC;QAC/C,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACxB,CAAC;IAED,MAAM,EAAE,CAAC,IAAI,CAAC;IACd,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,MAAc;IACrD,MAAM,EAAE,GAAG,MAAM,KAAK,EAAE,CAAC;IACzB,MAAM,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAClC,CAAC"} |
|
||||||
@ -1,88 +0,0 @@ |
|||||||
/** |
|
||||||
* Event caching with IndexedDB |
|
||||||
*/ |
|
||||||
import { getDB } from './indexeddb-store.js'; |
|
||||||
/** |
|
||||||
* Store an event in cache |
|
||||||
*/ |
|
||||||
export async function cacheEvent(event) { |
|
||||||
const db = await getDB(); |
|
||||||
const cached = { |
|
||||||
...event, |
|
||||||
cached_at: Date.now() |
|
||||||
}; |
|
||||||
await db.put('events', cached); |
|
||||||
} |
|
||||||
/** |
|
||||||
* Store multiple events in cache |
|
||||||
*/ |
|
||||||
export async function cacheEvents(events) { |
|
||||||
const db = await getDB(); |
|
||||||
const tx = db.transaction('events', 'readwrite'); |
|
||||||
for (const event of events) { |
|
||||||
const cached = { |
|
||||||
...event, |
|
||||||
cached_at: Date.now() |
|
||||||
}; |
|
||||||
await tx.store.put(cached); |
|
||||||
} |
|
||||||
await tx.done; |
|
||||||
} |
|
||||||
/** |
|
||||||
* Get event by ID from cache |
|
||||||
*/ |
|
||||||
export async function getEvent(id) { |
|
||||||
const db = await getDB(); |
|
||||||
return db.get('events', id); |
|
||||||
} |
|
||||||
/** |
|
||||||
* Get events by kind |
|
||||||
*/ |
|
||||||
export async function getEventsByKind(kind, limit) { |
|
||||||
const db = await getDB(); |
|
||||||
const tx = db.transaction('events', 'readonly'); |
|
||||||
const index = tx.store.index('kind'); |
|
||||||
const events = []; |
|
||||||
let count = 0; |
|
||||||
for await (const cursor of index.iterate(kind)) { |
|
||||||
if (limit && count >= limit) |
|
||||||
break; |
|
||||||
events.push(cursor.value); |
|
||||||
count++; |
|
||||||
} |
|
||||||
await tx.done; |
|
||||||
return events.sort((a, b) => b.created_at - a.created_at); |
|
||||||
} |
|
||||||
/** |
|
||||||
* Get events by pubkey |
|
||||||
*/ |
|
||||||
export async function getEventsByPubkey(pubkey, limit) { |
|
||||||
const db = await getDB(); |
|
||||||
const tx = db.transaction('events', 'readonly'); |
|
||||||
const index = tx.store.index('pubkey'); |
|
||||||
const events = []; |
|
||||||
let count = 0; |
|
||||||
for await (const cursor of index.iterate(pubkey)) { |
|
||||||
if (limit && count >= limit) |
|
||||||
break; |
|
||||||
events.push(cursor.value); |
|
||||||
count++; |
|
||||||
} |
|
||||||
await tx.done; |
|
||||||
return events.sort((a, b) => b.created_at - a.created_at); |
|
||||||
} |
|
||||||
/** |
|
||||||
* Clear old events (older than specified timestamp) |
|
||||||
*/ |
|
||||||
export async function clearOldEvents(olderThan) { |
|
||||||
const db = await getDB(); |
|
||||||
const tx = db.transaction('events', 'readwrite'); |
|
||||||
const index = tx.store.index('created_at'); |
|
||||||
for await (const cursor of index.iterate()) { |
|
||||||
if (cursor.value.created_at < olderThan) { |
|
||||||
await cursor.delete(); |
|
||||||
} |
|
||||||
} |
|
||||||
await tx.done; |
|
||||||
} |
|
||||||
//# sourceMappingURL=event-cache.js.map
|
|
||||||
@ -1 +0,0 @@ |
|||||||
{"version":3,"file":"event-cache.js","sourceRoot":"","sources":["event-cache.ts"],"names":[],"mappings":"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"} |
|
||||||
@ -1,48 +0,0 @@ |
|||||||
/** |
|
||||||
* Base IndexedDB store operations |
|
||||||
*/ |
|
||||||
import { openDB } from 'idb'; |
|
||||||
const DB_NAME = 'aitherboard'; |
|
||||||
const DB_VERSION = 1; |
|
||||||
let dbInstance = null; |
|
||||||
/** |
|
||||||
* Get or create database instance |
|
||||||
*/ |
|
||||||
export async function getDB() { |
|
||||||
if (dbInstance) |
|
||||||
return dbInstance; |
|
||||||
dbInstance = await openDB(DB_NAME, DB_VERSION, { |
|
||||||
upgrade(db) { |
|
||||||
// Events store
|
|
||||||
if (!db.objectStoreNames.contains('events')) { |
|
||||||
const eventStore = db.createObjectStore('events', { keyPath: 'id' }); |
|
||||||
eventStore.createIndex('kind', 'kind', { unique: false }); |
|
||||||
eventStore.createIndex('pubkey', 'pubkey', { unique: false }); |
|
||||||
eventStore.createIndex('created_at', 'created_at', { unique: false }); |
|
||||||
} |
|
||||||
// Profiles store
|
|
||||||
if (!db.objectStoreNames.contains('profiles')) { |
|
||||||
db.createObjectStore('profiles', { keyPath: 'pubkey' }); |
|
||||||
} |
|
||||||
// Keys store
|
|
||||||
if (!db.objectStoreNames.contains('keys')) { |
|
||||||
db.createObjectStore('keys', { keyPath: 'id' }); |
|
||||||
} |
|
||||||
// Search index store
|
|
||||||
if (!db.objectStoreNames.contains('search')) { |
|
||||||
db.createObjectStore('search', { keyPath: 'id' }); |
|
||||||
} |
|
||||||
} |
|
||||||
}); |
|
||||||
return dbInstance; |
|
||||||
} |
|
||||||
/** |
|
||||||
* Close database connection |
|
||||||
*/ |
|
||||||
export async function closeDB() { |
|
||||||
if (dbInstance) { |
|
||||||
dbInstance.close(); |
|
||||||
dbInstance = null; |
|
||||||
} |
|
||||||
} |
|
||||||
//# sourceMappingURL=indexeddb-store.js.map
|
|
||||||
@ -1 +0,0 @@ |
|||||||
{"version":3,"file":"indexeddb-store.js","sourceRoot":"","sources":["indexeddb-store.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,MAAM,EAAqB,MAAM,KAAK,CAAC;AAEhD,MAAM,OAAO,GAAG,aAAa,CAAC;AAC9B,MAAM,UAAU,GAAG,CAAC,CAAC;AAsBrB,IAAI,UAAU,GAAwC,IAAI,CAAC;AAE3D;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,KAAK;IACzB,IAAI,UAAU;QAAE,OAAO,UAAU,CAAC;IAElC,UAAU,GAAG,MAAM,MAAM,CAAiB,OAAO,EAAE,UAAU,EAAE;QAC7D,OAAO,CAAC,EAAE;YACR,eAAe;YACf,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC5C,MAAM,UAAU,GAAG,EAAE,CAAC,iBAAiB,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;gBACrE,UAAU,CAAC,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;gBAC1D,UAAU,CAAC,WAAW,CAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;gBAC9D,UAAU,CAAC,WAAW,CAAC,YAAY,EAAE,YAAY,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;YACxE,CAAC;YAED,iBAAiB;YACjB,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC9C,EAAE,CAAC,iBAAiB,CAAC,UAAU,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC;YAC1D,CAAC;YAED,aAAa;YACb,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC1C,EAAE,CAAC,iBAAiB,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YAClD,CAAC;YAED,qBAAqB;YACrB,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC5C,EAAE,CAAC,iBAAiB,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YACpD,CAAC;QACH,CAAC;KACF,CAAC,CAAC;IAEH,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO;IAC3B,IAAI,UAAU,EAAE,CAAC;QACf,UAAU,CAAC,KAAK,EAAE,CAAC;QACnB,UAAU,GAAG,IAAI,CAAC;IACpB,CAAC;AACH,CAAC"} |
|
||||||
@ -1,42 +0,0 @@ |
|||||||
/** |
|
||||||
* Profile caching (kind 0 events) |
|
||||||
*/ |
|
||||||
import { getDB } from './indexeddb-store.js'; |
|
||||||
/** |
|
||||||
* Store a profile in cache |
|
||||||
*/ |
|
||||||
export async function cacheProfile(event) { |
|
||||||
if (event.kind !== 0) |
|
||||||
throw new Error('Not a profile event'); |
|
||||||
const db = await getDB(); |
|
||||||
const cached = { |
|
||||||
pubkey: event.pubkey, |
|
||||||
event, |
|
||||||
cached_at: Date.now() |
|
||||||
}; |
|
||||||
await db.put('profiles', cached); |
|
||||||
} |
|
||||||
/** |
|
||||||
* Get profile by pubkey from cache |
|
||||||
*/ |
|
||||||
export async function getProfile(pubkey) { |
|
||||||
const db = await getDB(); |
|
||||||
return db.get('profiles', pubkey); |
|
||||||
} |
|
||||||
/** |
|
||||||
* Get multiple profiles |
|
||||||
*/ |
|
||||||
export async function getProfiles(pubkeys) { |
|
||||||
const db = await getDB(); |
|
||||||
const profiles = new Map(); |
|
||||||
const tx = db.transaction('profiles', 'readonly'); |
|
||||||
for (const pubkey of pubkeys) { |
|
||||||
const profile = await tx.store.get(pubkey); |
|
||||||
if (profile) { |
|
||||||
profiles.set(pubkey, profile); |
|
||||||
} |
|
||||||
} |
|
||||||
await tx.done; |
|
||||||
return profiles; |
|
||||||
} |
|
||||||
//# sourceMappingURL=profile-cache.js.map
|
|
||||||
@ -1 +0,0 @@ |
|||||||
{"version":3,"file":"profile-cache.js","sourceRoot":"","sources":["profile-cache.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAS7C;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,KAAiB;IAClD,IAAI,KAAK,CAAC,IAAI,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;IAC7D,MAAM,EAAE,GAAG,MAAM,KAAK,EAAE,CAAC;IACzB,MAAM,MAAM,GAAkB;QAC5B,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,KAAK;QACL,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;KACtB,CAAC;IACF,MAAM,EAAE,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;AACnC,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,MAAc;IAC7C,MAAM,EAAE,GAAG,MAAM,KAAK,EAAE,CAAC;IACzB,OAAO,EAAE,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;AACpC,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAAiB;IACjD,MAAM,EAAE,GAAG,MAAM,KAAK,EAAE,CAAC;IACzB,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAyB,CAAC;IAClD,MAAM,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IAElD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC3C,IAAI,OAAO,EAAE,CAAC;YACZ,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IAED,MAAM,EAAE,CAAC,IAAI,CAAC;IACd,OAAO,QAAQ,CAAC;AAClB,CAAC"} |
|
||||||
@ -1,43 +0,0 @@ |
|||||||
/** |
|
||||||
* Full-text search index (deferred implementation) |
|
||||||
*/ |
|
||||||
import { getDB } from './indexeddb-store.js'; |
|
||||||
/** |
|
||||||
* Index event content for search |
|
||||||
*/ |
|
||||||
export async function indexEvent(eventId, content) { |
|
||||||
// Placeholder - full implementation would:
|
|
||||||
// 1. Tokenize content
|
|
||||||
// 2. Create inverted index
|
|
||||||
// 3. Store in IndexedDB
|
|
||||||
const db = await getDB(); |
|
||||||
await db.put('search', { |
|
||||||
id: eventId, |
|
||||||
content: content.toLowerCase() |
|
||||||
}); |
|
||||||
} |
|
||||||
/** |
|
||||||
* Search events by query |
|
||||||
*/ |
|
||||||
export async function searchEvents(query, limit = 50) { |
|
||||||
// Placeholder - full implementation would:
|
|
||||||
// 1. Tokenize query
|
|
||||||
// 2. Look up in inverted index
|
|
||||||
// 3. Rank results
|
|
||||||
// 4. Return event IDs
|
|
||||||
const db = await getDB(); |
|
||||||
const results = []; |
|
||||||
const lowerQuery = query.toLowerCase(); |
|
||||||
const tx = db.transaction('search', 'readonly'); |
|
||||||
for await (const cursor of tx.store.iterate()) { |
|
||||||
if (results.length >= limit) |
|
||||||
break; |
|
||||||
const content = cursor.value.content; |
|
||||||
if (content.includes(lowerQuery)) { |
|
||||||
results.push(cursor.key); |
|
||||||
} |
|
||||||
} |
|
||||||
await tx.done; |
|
||||||
return results; |
|
||||||
} |
|
||||||
//# sourceMappingURL=search-index.js.map
|
|
||||||
@ -1 +0,0 @@ |
|||||||
{"version":3,"file":"search-index.js","sourceRoot":"","sources":["search-index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAE7C;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAAe,EAAE,OAAe;IAC/D,2CAA2C;IAC3C,sBAAsB;IACtB,2BAA2B;IAC3B,wBAAwB;IACxB,MAAM,EAAE,GAAG,MAAM,KAAK,EAAE,CAAC;IACzB,MAAM,EAAE,CAAC,GAAG,CAAC,QAAQ,EAAE;QACrB,EAAE,EAAE,OAAO;QACX,OAAO,EAAE,OAAO,CAAC,WAAW,EAAE;KAC/B,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,KAAa,EAAE,QAAgB,EAAE;IAClE,2CAA2C;IAC3C,oBAAoB;IACpB,+BAA+B;IAC/B,kBAAkB;IAClB,sBAAsB;IACtB,MAAM,EAAE,GAAG,MAAM,KAAK,EAAE,CAAC;IACzB,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,UAAU,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;IACvC,MAAM,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAEhD,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;QAC9C,IAAI,OAAO,CAAC,MAAM,IAAI,KAAK;YAAE,MAAM;QACnC,MAAM,OAAO,GAAI,MAAM,CAAC,KAA6B,CAAC,OAAO,CAAC;QAC9D,IAAI,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;YACjC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,GAAa,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IAED,MAAM,EAAE,CAAC,IAAI,CAAC;IACd,OAAO,OAAO,CAAC;AACjB,CAAC"} |
|
||||||
@ -1,101 +0,0 @@ |
|||||||
/** |
|
||||||
* Applesauce-core client wrapper |
|
||||||
* Main interface for Nostr operations |
|
||||||
*/ |
|
||||||
import { initializeRelayPool, relayPool } from './relay-pool.js'; |
|
||||||
import { subscriptionManager } from './subscription-manager.js'; |
|
||||||
import { eventStore } from './event-store.js'; |
|
||||||
import { config } from './config.js'; |
|
||||||
class ApplesauceClient { |
|
||||||
initialized = false; |
|
||||||
/** |
|
||||||
* Initialize the client |
|
||||||
*/ |
|
||||||
async initialize() { |
|
||||||
if (this.initialized) |
|
||||||
return; |
|
||||||
await initializeRelayPool(); |
|
||||||
this.initialized = true; |
|
||||||
} |
|
||||||
/** |
|
||||||
* Publish an event to relays |
|
||||||
*/ |
|
||||||
async publish(event, options = {}) { |
|
||||||
const relays = options.relays || relayPool.getConnectedRelays(); |
|
||||||
const message = JSON.stringify(['EVENT', event]); |
|
||||||
const results = { |
|
||||||
success: [], |
|
||||||
failed: [] |
|
||||||
}; |
|
||||||
for (const relay of relays) { |
|
||||||
try { |
|
||||||
const sent = relayPool.send(relay, message); |
|
||||||
if (sent) { |
|
||||||
results.success.push(relay); |
|
||||||
} |
|
||||||
else { |
|
||||||
results.failed.push({ relay, error: 'Not connected' }); |
|
||||||
} |
|
||||||
} |
|
||||||
catch (error) { |
|
||||||
results.failed.push({ |
|
||||||
relay, |
|
||||||
error: error instanceof Error ? error.message : 'Unknown error' |
|
||||||
}); |
|
||||||
} |
|
||||||
} |
|
||||||
// Store in cache
|
|
||||||
if (results.success.length > 0) { |
|
||||||
await eventStore.storeEvent(event); |
|
||||||
} |
|
||||||
return results; |
|
||||||
} |
|
||||||
/** |
|
||||||
* Subscribe to events |
|
||||||
*/ |
|
||||||
subscribe(filters, relays, onEvent, onEose) { |
|
||||||
const subId = subscriptionManager.generateSubId(); |
|
||||||
subscriptionManager.subscribe(subId, relays, filters, onEvent, onEose); |
|
||||||
return subId; |
|
||||||
} |
|
||||||
/** |
|
||||||
* Unsubscribe |
|
||||||
*/ |
|
||||||
unsubscribe(subId) { |
|
||||||
subscriptionManager.unsubscribe(subId); |
|
||||||
} |
|
||||||
/** |
|
||||||
* Fetch events |
|
||||||
*/ |
|
||||||
async fetchEvents(filters, relays, options) { |
|
||||||
return eventStore.fetchEvents(filters, relays, options || {}); |
|
||||||
} |
|
||||||
/** |
|
||||||
* Get event by ID |
|
||||||
*/ |
|
||||||
async getEventById(id, relays) { |
|
||||||
return eventStore.getEventById(id, relays); |
|
||||||
} |
|
||||||
/** |
|
||||||
* Get relay pool |
|
||||||
*/ |
|
||||||
getRelayPool() { |
|
||||||
return relayPool; |
|
||||||
} |
|
||||||
/** |
|
||||||
* Get config |
|
||||||
*/ |
|
||||||
getConfig() { |
|
||||||
return config; |
|
||||||
} |
|
||||||
/** |
|
||||||
* Close all connections |
|
||||||
*/ |
|
||||||
close() { |
|
||||||
subscriptionManager.closeAll(); |
|
||||||
relayPool.closeAll(); |
|
||||||
this.initialized = false; |
|
||||||
} |
|
||||||
} |
|
||||||
export const nostrClient = new ApplesauceClient(); |
|
||||||
//# sourceMappingURL=applesauce-client.js.map
|
|
||||||
@ -1 +0,0 @@ |
|||||||
{"version":3,"file":"applesauce-client.js","sourceRoot":"","sources":["applesauce-client.ts"],"names":[],"mappings":"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"} |
|
||||||
@ -1,119 +0,0 @@ |
|||||||
/** |
|
||||||
* Unified authentication handler |
|
||||||
*/ |
|
||||||
import { signEventWithNIP07, getPublicKeyWithNIP07 } from '../auth/nip07-signer.js'; |
|
||||||
import { signEventWithNsec, getPublicKeyFromNsec } from '../auth/nsec-signer.js'; |
|
||||||
import { signEventWithBunker, connectBunker } from '../auth/bunker-signer.js'; |
|
||||||
import { signEventWithAnonymous, generateAnonymousKey } from '../auth/anonymous-signer.js'; |
|
||||||
import { decryptPrivateKey } from '../security/key-management.js'; |
|
||||||
import { sessionManager } from '../auth/session-manager.js'; |
|
||||||
import { fetchRelayLists } from '../auth/relay-list-fetcher.js'; |
|
||||||
import { eventStore } from './event-store.js'; |
|
||||||
import { nostrClient } from './applesauce-client.js'; |
|
||||||
/** |
|
||||||
* Authenticate with NIP-07 |
|
||||||
*/ |
|
||||||
export async function authenticateWithNIP07() { |
|
||||||
const pubkey = await getPublicKeyWithNIP07(); |
|
||||||
sessionManager.setSession({ |
|
||||||
pubkey, |
|
||||||
method: 'nip07', |
|
||||||
signer: signEventWithNIP07, |
|
||||||
createdAt: Date.now() |
|
||||||
}); |
|
||||||
// Fetch user relay lists and mute list
|
|
||||||
await loadUserPreferences(pubkey); |
|
||||||
return pubkey; |
|
||||||
} |
|
||||||
/** |
|
||||||
* Authenticate with nsec |
|
||||||
*/ |
|
||||||
export async function authenticateWithNsec(ncryptsec, password) { |
|
||||||
// Decrypt the encrypted private key
|
|
||||||
const nsec = await decryptPrivateKey(ncryptsec, password); |
|
||||||
// Derive public key from private key
|
|
||||||
const pubkey = await getPublicKeyFromNsec(nsec); |
|
||||||
sessionManager.setSession({ |
|
||||||
pubkey, |
|
||||||
method: 'nsec', |
|
||||||
signer: async (event) => signEventWithNsec(event, ncryptsec, password), |
|
||||||
createdAt: Date.now() |
|
||||||
}); |
|
||||||
await loadUserPreferences(pubkey); |
|
||||||
return pubkey; |
|
||||||
} |
|
||||||
/** |
|
||||||
* Authenticate with bunker |
|
||||||
*/ |
|
||||||
export async function authenticateWithBunker(bunkerUri) { |
|
||||||
const connection = await connectBunker(bunkerUri); |
|
||||||
sessionManager.setSession({ |
|
||||||
pubkey: connection.pubkey, |
|
||||||
method: 'bunker', |
|
||||||
signer: async (event) => signEventWithBunker(event, connection), |
|
||||||
createdAt: Date.now() |
|
||||||
}); |
|
||||||
await loadUserPreferences(connection.pubkey); |
|
||||||
return connection.pubkey; |
|
||||||
} |
|
||||||
/** |
|
||||||
* Authenticate as anonymous |
|
||||||
*/ |
|
||||||
export async function authenticateAsAnonymous(password) { |
|
||||||
const { pubkey, nsec } = await generateAnonymousKey(password); |
|
||||||
// Store the key for later use
|
|
||||||
// In practice, we'd need to store the ncryptsec and decrypt when needed
|
|
||||||
// For now, this is simplified
|
|
||||||
sessionManager.setSession({ |
|
||||||
pubkey, |
|
||||||
method: 'anonymous', |
|
||||||
signer: async (event) => { |
|
||||||
// Simplified - would decrypt and sign
|
|
||||||
return signEventWithAnonymous(event, pubkey, password); |
|
||||||
}, |
|
||||||
createdAt: Date.now() |
|
||||||
}); |
|
||||||
return pubkey; |
|
||||||
} |
|
||||||
/** |
|
||||||
* Load user preferences (relay lists, mute list, blocked relays) |
|
||||||
*/ |
|
||||||
async function loadUserPreferences(pubkey) { |
|
||||||
// Fetch relay lists
|
|
||||||
const { inbox, outbox } = await fetchRelayLists(pubkey); |
|
||||||
// Relay lists would be used by relay selection logic
|
|
||||||
// Fetch mute list (kind 10000)
|
|
||||||
const muteEvents = await nostrClient.fetchEvents([{ kinds: [10000], authors: [pubkey], limit: 1 }], [...nostrClient.getConfig().defaultRelays, ...nostrClient.getConfig().profileRelays], { useCache: true, cacheResults: true }); |
|
||||||
if (muteEvents.length > 0) { |
|
||||||
const mutedPubkeys = muteEvents[0].tags |
|
||||||
.filter((t) => t[0] === 'p') |
|
||||||
.map((t) => t[1]) |
|
||||||
.filter(Boolean); |
|
||||||
eventStore.setMuteList(mutedPubkeys); |
|
||||||
} |
|
||||||
// Fetch blocked relays (kind 10006)
|
|
||||||
const blockedRelayEvents = await nostrClient.fetchEvents([{ kinds: [10006], authors: [pubkey], limit: 1 }], [...nostrClient.getConfig().defaultRelays, ...nostrClient.getConfig().profileRelays], { useCache: true, cacheResults: true }); |
|
||||||
if (blockedRelayEvents.length > 0) { |
|
||||||
const blockedRelays = blockedRelayEvents[0].tags |
|
||||||
.filter((t) => t[0] === 'relay') |
|
||||||
.map((t) => t[1]) |
|
||||||
.filter(Boolean); |
|
||||||
eventStore.setBlockedRelays(blockedRelays); |
|
||||||
} |
|
||||||
} |
|
||||||
/** |
|
||||||
* Sign and publish event |
|
||||||
*/ |
|
||||||
export async function signAndPublish(event, relays) { |
|
||||||
const signed = await sessionManager.signEvent(event); |
|
||||||
return nostrClient.publish(signed, { relays }); |
|
||||||
} |
|
||||||
/** |
|
||||||
* Logout |
|
||||||
*/ |
|
||||||
export function logout() { |
|
||||||
sessionManager.clearSession(); |
|
||||||
eventStore.setMuteList([]); |
|
||||||
eventStore.setBlockedRelays([]); |
|
||||||
} |
|
||||||
//# sourceMappingURL=auth-handler.js.map
|
|
||||||
@ -1 +0,0 @@ |
|||||||
{"version":3,"file":"auth-handler.js","sourceRoot":"","sources":["auth-handler.ts"],"names":[],"mappings":"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"} |
|
||||||
@ -1,49 +0,0 @@ |
|||||||
/** |
|
||||||
* Configuration for Nostr services |
|
||||||
* Handles environment variables and defaults |
|
||||||
*/ |
|
||||||
const DEFAULT_RELAYS = [ |
|
||||||
'wss://theforest.nostr1.com', |
|
||||||
'wss://nostr21.com', |
|
||||||
'wss://nostr.land', |
|
||||||
'wss://nostr.wine', |
|
||||||
'wss://nostr.sovbit.host' |
|
||||||
]; |
|
||||||
const PROFILE_RELAYS = [ |
|
||||||
'wss://relay.damus.io', |
|
||||||
'wss://aggr.nostr.land', |
|
||||||
'wss://profiles.nostr1.com' |
|
||||||
]; |
|
||||||
function parseRelays(envVar, fallback) { |
|
||||||
if (!envVar) |
|
||||||
return fallback; |
|
||||||
const relays = envVar |
|
||||||
.split(',') |
|
||||||
.map((r) => r.trim()) |
|
||||||
.filter((r) => r.length > 0); |
|
||||||
return relays.length > 0 ? relays : fallback; |
|
||||||
} |
|
||||||
function parseIntEnv(envVar, fallback, min = 0) { |
|
||||||
if (!envVar) |
|
||||||
return fallback; |
|
||||||
const parsed = parseInt(envVar, 10); |
|
||||||
if (isNaN(parsed) || parsed < min) |
|
||||||
return fallback; |
|
||||||
return parsed; |
|
||||||
} |
|
||||||
function parseBoolEnv(envVar, fallback) { |
|
||||||
if (!envVar) |
|
||||||
return fallback; |
|
||||||
return envVar.toLowerCase() === 'true' || envVar === '1'; |
|
||||||
} |
|
||||||
export function getConfig() { |
|
||||||
return { |
|
||||||
defaultRelays: parseRelays(import.meta.env.VITE_DEFAULT_RELAYS, DEFAULT_RELAYS), |
|
||||||
profileRelays: PROFILE_RELAYS, |
|
||||||
zapThreshold: parseIntEnv(import.meta.env.VITE_ZAP_THRESHOLD, 1, 0), |
|
||||||
threadTimeoutDays: parseIntEnv(import.meta.env.VITE_THREAD_TIMEOUT_DAYS, 30), |
|
||||||
pwaEnabled: parseBoolEnv(import.meta.env.VITE_PWA_ENABLED, true) |
|
||||||
}; |
|
||||||
} |
|
||||||
export const config = getConfig(); |
|
||||||
//# sourceMappingURL=config.js.map
|
|
||||||
@ -1 +0,0 @@ |
|||||||
{"version":3,"file":"config.js","sourceRoot":"","sources":["config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,cAAc,GAAG;IACrB,4BAA4B;IAC5B,mBAAmB;IACnB,kBAAkB;IAClB,kBAAkB;IAClB,yBAAyB;CAC1B,CAAC;AAEF,MAAM,cAAc,GAAG;IACrB,sBAAsB;IACtB,uBAAuB;IACvB,2BAA2B;CAC5B,CAAC;AAUF,SAAS,WAAW,CAAC,MAA0B,EAAE,QAAkB;IACjE,IAAI,CAAC,MAAM;QAAE,OAAO,QAAQ,CAAC;IAC7B,MAAM,MAAM,GAAG,MAAM;SAClB,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC/B,OAAO,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC;AAC/C,CAAC;AAED,SAAS,WAAW,CAAC,MAA0B,EAAE,QAAgB,EAAE,MAAc,CAAC;IAChF,IAAI,CAAC,MAAM;QAAE,OAAO,QAAQ,CAAC;IAC7B,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACpC,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,GAAG;QAAE,OAAO,QAAQ,CAAC;IACnD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,YAAY,CAAC,MAA0B,EAAE,QAAiB;IACjE,IAAI,CAAC,MAAM;QAAE,OAAO,QAAQ,CAAC;IAC7B,OAAO,MAAM,CAAC,WAAW,EAAE,KAAK,MAAM,IAAI,MAAM,KAAK,GAAG,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,OAAO;QACL,aAAa,EAAE,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,mBAAmB,EAAE,cAAc,CAAC;QAC/E,aAAa,EAAE,cAAc;QAC7B,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,kBAAkB,EAAE,CAAC,EAAE,CAAC,CAAC;QACnE,iBAAiB,EAAE,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,wBAAwB,EAAE,EAAE,CAAC;QAC5E,UAAU,EAAE,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,gBAAgB,EAAE,IAAI,CAAC;KACjE,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC"} |
|
||||||
@ -1,213 +0,0 @@ |
|||||||
/** |
|
||||||
* Event store with IndexedDB caching and filtering |
|
||||||
*/ |
|
||||||
import { cacheEvent, cacheEvents, getEvent, getEventsByKind, getEventsByPubkey } from '../cache/event-cache.js'; |
|
||||||
import { subscriptionManager } from './subscription-manager.js'; |
|
||||||
class EventStore { |
|
||||||
muteList = new Set(); |
|
||||||
blockedRelays = new Set(); |
|
||||||
activityTracker = new Map(); // pubkey -> last activity timestamp
|
|
||||||
/** |
|
||||||
* Update mute list |
|
||||||
*/ |
|
||||||
setMuteList(pubkeys) { |
|
||||||
this.muteList = new Set(pubkeys); |
|
||||||
} |
|
||||||
/** |
|
||||||
* Update blocked relays |
|
||||||
*/ |
|
||||||
setBlockedRelays(relays) { |
|
||||||
this.blockedRelays = new Set(relays); |
|
||||||
} |
|
||||||
/** |
|
||||||
* Filter out muted events |
|
||||||
*/ |
|
||||||
isMuted(event) { |
|
||||||
return this.muteList.has(event.pubkey); |
|
||||||
} |
|
||||||
/** |
|
||||||
* Filter out blocked relays |
|
||||||
*/ |
|
||||||
filterBlockedRelays(relays) { |
|
||||||
return relays.filter((r) => !this.blockedRelays.has(r)); |
|
||||||
} |
|
||||||
/** |
|
||||||
* Track activity for a pubkey |
|
||||||
*/ |
|
||||||
trackActivity(pubkey, timestamp) { |
|
||||||
const current = this.activityTracker.get(pubkey) || 0; |
|
||||||
if (timestamp > current) { |
|
||||||
this.activityTracker.set(pubkey, timestamp); |
|
||||||
} |
|
||||||
} |
|
||||||
/** |
|
||||||
* Get last activity timestamp for a pubkey |
|
||||||
*/ |
|
||||||
getLastActivity(pubkey) { |
|
||||||
return this.activityTracker.get(pubkey); |
|
||||||
} |
|
||||||
/** |
|
||||||
* Check if event should be hidden (content filtering) |
|
||||||
*/ |
|
||||||
shouldHideEvent(event) { |
|
||||||
// Check for content-warning or sensitive tags
|
|
||||||
const hasContentWarning = event.tags.some((t) => t[0] === 'content-warning' || t[0] === 'sensitive'); |
|
||||||
if (hasContentWarning) |
|
||||||
return true; |
|
||||||
// Check for #NSFW in content or tags
|
|
||||||
const content = event.content.toLowerCase(); |
|
||||||
const hasNSFW = content.includes('#nsfw') || event.tags.some((t) => t[1]?.toLowerCase() === 'nsfw'); |
|
||||||
if (hasNSFW) |
|
||||||
return true; |
|
||||||
return false; |
|
||||||
} |
|
||||||
/** |
|
||||||
* Fetch events with filters |
|
||||||
*/ |
|
||||||
async fetchEvents(filters, relays, options = {}) { |
|
||||||
const { useCache = true, cacheResults = true } = options; |
|
||||||
// Filter out blocked relays
|
|
||||||
const filteredRelays = this.filterBlockedRelays(relays); |
|
||||||
// Try cache first if enabled
|
|
||||||
if (useCache) { |
|
||||||
// Simple cache lookup - could be improved
|
|
||||||
const cachedEvents = []; |
|
||||||
for (const filter of filters) { |
|
||||||
if (filter.kinds && filter.kinds.length === 1) { |
|
||||||
const events = await getEventsByKind(filter.kinds[0], filter.limit); |
|
||||||
cachedEvents.push(...events); |
|
||||||
} |
|
||||||
if (filter.authors && filter.authors.length === 1) { |
|
||||||
const events = await getEventsByPubkey(filter.authors[0], filter.limit); |
|
||||||
cachedEvents.push(...events); |
|
||||||
} |
|
||||||
} |
|
||||||
if (cachedEvents.length > 0) { |
|
||||||
// Return cached events immediately (progressive loading)
|
|
||||||
// Continue fetching fresh data in background
|
|
||||||
this.fetchEventsFromRelays(filters, filteredRelays, { cacheResults }).catch((error) => { |
|
||||||
console.error('Error fetching fresh events from relays:', error); |
|
||||||
}); |
|
||||||
return this.filterEvents(cachedEvents); |
|
||||||
} |
|
||||||
} |
|
||||||
// Fetch from relays
|
|
||||||
return this.fetchEventsFromRelays(filters, filteredRelays, { cacheResults }); |
|
||||||
} |
|
||||||
/** |
|
||||||
* Fetch events from relays |
|
||||||
*/ |
|
||||||
async fetchEventsFromRelays(filters, relays, options) { |
|
||||||
return new Promise((resolve, reject) => { |
|
||||||
const events = new Map(); |
|
||||||
const subId = subscriptionManager.generateSubId(); |
|
||||||
const relayCount = new Set(); |
|
||||||
let resolved = false; |
|
||||||
let eoseTimeout = null; |
|
||||||
let timeoutId = null; |
|
||||||
const finish = (eventArray) => { |
|
||||||
if (resolved) |
|
||||||
return; |
|
||||||
resolved = true; |
|
||||||
// Clean up timeouts
|
|
||||||
if (timeoutId) { |
|
||||||
clearTimeout(timeoutId); |
|
||||||
timeoutId = null; |
|
||||||
} |
|
||||||
if (eoseTimeout) { |
|
||||||
clearTimeout(eoseTimeout); |
|
||||||
eoseTimeout = null; |
|
||||||
} |
|
||||||
subscriptionManager.unsubscribe(subId); |
|
||||||
resolve(this.filterEvents(eventArray)); |
|
||||||
}; |
|
||||||
const onEvent = (event, relay) => { |
|
||||||
// Skip muted events
|
|
||||||
if (this.isMuted(event)) |
|
||||||
return; |
|
||||||
// Skip hidden events
|
|
||||||
if (this.shouldHideEvent(event)) |
|
||||||
return; |
|
||||||
// Track activity
|
|
||||||
this.trackActivity(event.pubkey, event.created_at); |
|
||||||
// Deduplicate by event ID
|
|
||||||
events.set(event.id, event); |
|
||||||
relayCount.add(relay); |
|
||||||
}; |
|
||||||
const onEose = (relay) => { |
|
||||||
relayCount.add(relay); |
|
||||||
// Wait a bit for all relays to respond
|
|
||||||
if (eoseTimeout) { |
|
||||||
clearTimeout(eoseTimeout); |
|
||||||
} |
|
||||||
eoseTimeout = setTimeout(() => { |
|
||||||
if (!resolved && relayCount.size >= Math.min(relays.length, 3)) { |
|
||||||
// Got responses from enough relays
|
|
||||||
const eventArray = Array.from(events.values()); |
|
||||||
if (options.cacheResults) { |
|
||||||
cacheEvents(eventArray).catch((error) => { |
|
||||||
console.error('Error caching events:', error); |
|
||||||
}); |
|
||||||
} |
|
||||||
finish(eventArray); |
|
||||||
} |
|
||||||
}, 1000); |
|
||||||
}; |
|
||||||
try { |
|
||||||
subscriptionManager.subscribe(subId, relays, filters, onEvent, onEose); |
|
||||||
} |
|
||||||
catch (error) { |
|
||||||
reject(error); |
|
||||||
return; |
|
||||||
} |
|
||||||
// Timeout after 10 seconds
|
|
||||||
timeoutId = setTimeout(() => { |
|
||||||
if (!resolved) { |
|
||||||
const eventArray = Array.from(events.values()); |
|
||||||
if (options.cacheResults) { |
|
||||||
cacheEvents(eventArray).catch((error) => { |
|
||||||
console.error('Error caching events:', error); |
|
||||||
}); |
|
||||||
} |
|
||||||
finish(eventArray); |
|
||||||
} |
|
||||||
}, 10000); |
|
||||||
}); |
|
||||||
} |
|
||||||
/** |
|
||||||
* Filter events (remove muted, hidden, etc.) |
|
||||||
*/ |
|
||||||
filterEvents(events) { |
|
||||||
return events.filter((event) => { |
|
||||||
if (this.isMuted(event)) |
|
||||||
return false; |
|
||||||
if (this.shouldHideEvent(event)) |
|
||||||
return false; |
|
||||||
return true; |
|
||||||
}); |
|
||||||
} |
|
||||||
/** |
|
||||||
* Get event by ID (from cache or fetch) |
|
||||||
*/ |
|
||||||
async getEventById(id, relays) { |
|
||||||
// Try cache first
|
|
||||||
const cached = await getEvent(id); |
|
||||||
if (cached) |
|
||||||
return cached; |
|
||||||
// Fetch from relays
|
|
||||||
const filters = [{ ids: [id] }]; |
|
||||||
const events = await this.fetchEvents(filters, relays, { useCache: false }); |
|
||||||
return events[0] || null; |
|
||||||
} |
|
||||||
/** |
|
||||||
* Store event in cache |
|
||||||
*/ |
|
||||||
async storeEvent(event) { |
|
||||||
if (this.isMuted(event) || this.shouldHideEvent(event)) |
|
||||||
return; |
|
||||||
this.trackActivity(event.pubkey, event.created_at); |
|
||||||
await cacheEvent(event); |
|
||||||
} |
|
||||||
} |
|
||||||
export const eventStore = new EventStore(); |
|
||||||
//# sourceMappingURL=event-store.js.map
|
|
||||||
File diff suppressed because one or more lines are too long
@ -1,41 +0,0 @@ |
|||||||
/** |
|
||||||
* Event utilities for creating and signing events |
|
||||||
*/ |
|
||||||
/** |
|
||||||
* Create event ID (SHA256 of serialized event) |
|
||||||
*/ |
|
||||||
export async function createEventId(event) { |
|
||||||
const serialized = JSON.stringify([ |
|
||||||
0, |
|
||||||
event.pubkey, |
|
||||||
event.created_at, |
|
||||||
event.kind, |
|
||||||
event.tags, |
|
||||||
event.content |
|
||||||
]); |
|
||||||
const encoder = new TextEncoder(); |
|
||||||
const data = encoder.encode(serialized); |
|
||||||
const hashBuffer = await crypto.subtle.digest('SHA-256', data); |
|
||||||
const hashArray = Array.from(new Uint8Array(hashBuffer)); |
|
||||||
return hashArray.map((b) => b.toString(16).padStart(2, '0')).join(''); |
|
||||||
} |
|
||||||
/** |
|
||||||
* Sign event (placeholder) |
|
||||||
* |
|
||||||
* TEMPORARY: Generates a deterministic signature-like string. |
|
||||||
* This is NOT a valid secp256k1 signature but has the correct length. |
|
||||||
* Production code MUST compute actual secp256k1 signature. |
|
||||||
*/ |
|
||||||
export async function signEvent(event) { |
|
||||||
const id = await createEventId(event); |
|
||||||
// TEMPORARY: Generate deterministic signature-like string (128 chars)
|
|
||||||
const encoder = new TextEncoder(); |
|
||||||
const sigData = encoder.encode(event.pubkey + id); |
|
||||||
const sigHashBuffer = await crypto.subtle.digest('SHA-256', sigData); |
|
||||||
const sigHashArray = Array.from(new Uint8Array(sigHashBuffer)); |
|
||||||
const sigHash = sigHashArray.map((b) => b.toString(16).padStart(2, '0')).join(''); |
|
||||||
// Double the hash to get 128 chars (64 * 2)
|
|
||||||
const sig = (sigHash + sigHash).slice(0, 128); |
|
||||||
return { ...event, id, sig }; |
|
||||||
} |
|
||||||
//# sourceMappingURL=event-utils.js.map
|
|
||||||
@ -1 +0,0 @@ |
|||||||
{"version":3,"file":"event-utils.js","sourceRoot":"","sources":["event-utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAqC;IACvE,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC;QAChC,CAAC;QACD,KAAK,CAAC,MAAM;QACZ,KAAK,CAAC,UAAU;QAChB,KAAK,CAAC,IAAI;QACV,KAAK,CAAC,IAAI;QACV,KAAK,CAAC,OAAO;KACd,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;IAClC,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IACxC,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC/D,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC;IACzD,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACxE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,KAAqC;IAErC,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,KAAK,CAAC,CAAC;IACtC,sEAAsE;IACtE,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;IAClC,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;IAClD,MAAM,aAAa,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IACrE,MAAM,YAAY,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,aAAa,CAAC,CAAC,CAAC;IAC/D,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAClF,4CAA4C;IAC5C,MAAM,GAAG,GAAG,CAAC,OAAO,GAAG,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC9C,OAAO,EAAE,GAAG,KAAK,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC;AAC/B,CAAC"} |
|
||||||
@ -1,181 +0,0 @@ |
|||||||
/** |
|
||||||
* Relay pool management |
|
||||||
* Manages WebSocket connections to Nostr relays |
|
||||||
*/ |
|
||||||
import { config } from './config.js'; |
|
||||||
class RelayPool { |
|
||||||
relays = new Map(); |
|
||||||
status = new Map(); |
|
||||||
statusCallbacks = new Set(); |
|
||||||
reconnectTimeouts = new Map(); |
|
||||||
/** |
|
||||||
* Add relay to pool |
|
||||||
*/ |
|
||||||
async addRelay(url) { |
|
||||||
if (this.relays.has(url)) |
|
||||||
return; |
|
||||||
this.relays.set(url, null); |
|
||||||
this.updateStatus(url, { connected: false }); |
|
||||||
await this.connect(url); |
|
||||||
} |
|
||||||
/** |
|
||||||
* Remove relay from pool |
|
||||||
*/ |
|
||||||
removeRelay(url) { |
|
||||||
const ws = this.relays.get(url); |
|
||||||
if (ws) { |
|
||||||
ws.close(); |
|
||||||
} |
|
||||||
this.relays.delete(url); |
|
||||||
this.status.delete(url); |
|
||||||
const timeout = this.reconnectTimeouts.get(url); |
|
||||||
if (timeout) { |
|
||||||
clearTimeout(timeout); |
|
||||||
this.reconnectTimeouts.delete(url); |
|
||||||
} |
|
||||||
} |
|
||||||
/** |
|
||||||
* Connect to a relay |
|
||||||
*/ |
|
||||||
async connect(url) { |
|
||||||
try { |
|
||||||
const ws = new WebSocket(url); |
|
||||||
const startTime = Date.now(); |
|
||||||
ws.onopen = () => { |
|
||||||
const latency = Date.now() - startTime; |
|
||||||
this.relays.set(url, ws); |
|
||||||
this.updateStatus(url, { |
|
||||||
connected: true, |
|
||||||
latency, |
|
||||||
lastConnected: Date.now() |
|
||||||
}); |
|
||||||
}; |
|
||||||
ws.onerror = (error) => { |
|
||||||
this.updateStatus(url, { |
|
||||||
connected: false, |
|
||||||
lastError: error.message || 'Connection error' |
|
||||||
}); |
|
||||||
this.scheduleReconnect(url); |
|
||||||
}; |
|
||||||
ws.onclose = () => { |
|
||||||
this.relays.set(url, null); |
|
||||||
this.updateStatus(url, { connected: false }); |
|
||||||
this.scheduleReconnect(url); |
|
||||||
}; |
|
||||||
// Store WebSocket for message sending
|
|
||||||
this.relays.set(url, ws); |
|
||||||
} |
|
||||||
catch (error) { |
|
||||||
this.updateStatus(url, { |
|
||||||
connected: false, |
|
||||||
lastError: error instanceof Error ? error.message : 'Unknown error' |
|
||||||
}); |
|
||||||
this.scheduleReconnect(url); |
|
||||||
} |
|
||||||
} |
|
||||||
/** |
|
||||||
* Schedule reconnection attempt |
|
||||||
*/ |
|
||||||
scheduleReconnect(url) { |
|
||||||
const existing = this.reconnectTimeouts.get(url); |
|
||||||
if (existing) |
|
||||||
clearTimeout(existing); |
|
||||||
const timeout = setTimeout(() => { |
|
||||||
this.reconnectTimeouts.delete(url); |
|
||||||
this.connect(url); |
|
||||||
}, 5000); // 5 second delay
|
|
||||||
this.reconnectTimeouts.set(url, timeout); |
|
||||||
} |
|
||||||
/** |
|
||||||
* Update relay status and notify callbacks |
|
||||||
*/ |
|
||||||
updateStatus(url, updates) { |
|
||||||
const current = this.status.get(url) || { url, connected: false }; |
|
||||||
const updated = { ...current, ...updates }; |
|
||||||
this.status.set(url, updated); |
|
||||||
// Notify callbacks
|
|
||||||
this.statusCallbacks.forEach((cb) => cb(updated)); |
|
||||||
} |
|
||||||
/** |
|
||||||
* Get WebSocket for a relay |
|
||||||
*/ |
|
||||||
getRelay(url) { |
|
||||||
return this.relays.get(url) || null; |
|
||||||
} |
|
||||||
/** |
|
||||||
* Get all connected relays |
|
||||||
*/ |
|
||||||
getConnectedRelays() { |
|
||||||
return Array.from(this.relays.entries()) |
|
||||||
.filter(([, ws]) => ws && ws.readyState === WebSocket.OPEN) |
|
||||||
.map(([url]) => url); |
|
||||||
} |
|
||||||
/** |
|
||||||
* Get relay status |
|
||||||
*/ |
|
||||||
getStatus(url) { |
|
||||||
return this.status.get(url); |
|
||||||
} |
|
||||||
/** |
|
||||||
* Get all relay statuses |
|
||||||
*/ |
|
||||||
getAllStatuses() { |
|
||||||
return Array.from(this.status.values()); |
|
||||||
} |
|
||||||
/** |
|
||||||
* Subscribe to status updates |
|
||||||
*/ |
|
||||||
onStatusUpdate(callback) { |
|
||||||
this.statusCallbacks.add(callback); |
|
||||||
return () => this.statusCallbacks.delete(callback); |
|
||||||
} |
|
||||||
/** |
|
||||||
* Send message to relay |
|
||||||
*/ |
|
||||||
send(url, message) { |
|
||||||
const ws = this.relays.get(url); |
|
||||||
if (ws && ws.readyState === WebSocket.OPEN) { |
|
||||||
ws.send(message); |
|
||||||
return true; |
|
||||||
} |
|
||||||
return false; |
|
||||||
} |
|
||||||
/** |
|
||||||
* Send message to all connected relays |
|
||||||
*/ |
|
||||||
broadcast(message) { |
|
||||||
const sent = []; |
|
||||||
for (const [url, ws] of this.relays.entries()) { |
|
||||||
if (ws && ws.readyState === WebSocket.OPEN) { |
|
||||||
ws.send(message); |
|
||||||
sent.push(url); |
|
||||||
} |
|
||||||
} |
|
||||||
return sent; |
|
||||||
} |
|
||||||
/** |
|
||||||
* Close all connections |
|
||||||
*/ |
|
||||||
closeAll() { |
|
||||||
for (const [url, ws] of this.relays.entries()) { |
|
||||||
if (ws) { |
|
||||||
ws.close(); |
|
||||||
} |
|
||||||
const timeout = this.reconnectTimeouts.get(url); |
|
||||||
if (timeout) { |
|
||||||
clearTimeout(timeout); |
|
||||||
this.reconnectTimeouts.delete(url); |
|
||||||
} |
|
||||||
} |
|
||||||
this.relays.clear(); |
|
||||||
this.status.clear(); |
|
||||||
} |
|
||||||
} |
|
||||||
export const relayPool = new RelayPool(); |
|
||||||
// Initialize with default relays
|
|
||||||
export async function initializeRelayPool() { |
|
||||||
for (const url of config.defaultRelays) { |
|
||||||
await relayPool.addRelay(url); |
|
||||||
} |
|
||||||
} |
|
||||||
//# sourceMappingURL=relay-pool.js.map
|
|
||||||
File diff suppressed because one or more lines are too long
@ -1,119 +0,0 @@ |
|||||||
/** |
|
||||||
* Subscription manager for Nostr subscriptions |
|
||||||
*/ |
|
||||||
import { relayPool } from './relay-pool.js'; |
|
||||||
class SubscriptionManager { |
|
||||||
subscriptions = new Map(); |
|
||||||
nextSubId = 1; |
|
||||||
/** |
|
||||||
* Create a new subscription |
|
||||||
*/ |
|
||||||
subscribe(subId, relays, filters, onEvent, onEose) { |
|
||||||
// Close existing subscription if any
|
|
||||||
this.unsubscribe(subId); |
|
||||||
const subscription = { |
|
||||||
id: subId, |
|
||||||
relays, |
|
||||||
filters, |
|
||||||
onEvent, |
|
||||||
onEose, |
|
||||||
messageHandlers: new Map() |
|
||||||
}; |
|
||||||
// Set up message handlers for each relay
|
|
||||||
for (const relayUrl of relays) { |
|
||||||
const ws = relayPool.getRelay(relayUrl); |
|
||||||
if (!ws) |
|
||||||
continue; |
|
||||||
const handler = (event) => { |
|
||||||
try { |
|
||||||
const data = JSON.parse(event.data); |
|
||||||
if (Array.isArray(data)) { |
|
||||||
const [type, ...rest] = data; |
|
||||||
if (type === 'EVENT' && rest[0] === subId) { |
|
||||||
const event = rest[1]; |
|
||||||
if (this.matchesFilters(event, filters)) { |
|
||||||
onEvent(event, relayUrl); |
|
||||||
} |
|
||||||
} |
|
||||||
else if (type === 'EOSE' && rest[0] === subId) { |
|
||||||
onEose?.(relayUrl); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
catch (error) { |
|
||||||
console.error('Error parsing relay message:', error); |
|
||||||
} |
|
||||||
}; |
|
||||||
ws.addEventListener('message', handler); |
|
||||||
subscription.messageHandlers.set(relayUrl, handler); |
|
||||||
// Send subscription request
|
|
||||||
const message = JSON.stringify(['REQ', subId, ...filters]); |
|
||||||
relayPool.send(relayUrl, message); |
|
||||||
} |
|
||||||
this.subscriptions.set(subId, subscription); |
|
||||||
} |
|
||||||
/** |
|
||||||
* Check if event matches filters |
|
||||||
*/ |
|
||||||
matchesFilters(event, filters) { |
|
||||||
return filters.some((filter) => { |
|
||||||
if (filter.ids && !filter.ids.includes(event.id)) |
|
||||||
return false; |
|
||||||
if (filter.authors && !filter.authors.includes(event.pubkey)) |
|
||||||
return false; |
|
||||||
if (filter.kinds && !filter.kinds.includes(event.kind)) |
|
||||||
return false; |
|
||||||
if (filter.since && event.created_at < filter.since) |
|
||||||
return false; |
|
||||||
if (filter.until && event.created_at > filter.until) |
|
||||||
return false; |
|
||||||
// Tag filters
|
|
||||||
if (filter['#e']) { |
|
||||||
const hasE = event.tags.some((t) => t[0] === 'e' && filter['#e'].includes(t[1])); |
|
||||||
if (!hasE) |
|
||||||
return false; |
|
||||||
} |
|
||||||
if (filter['#p']) { |
|
||||||
const hasP = event.tags.some((t) => t[0] === 'p' && filter['#p'].includes(t[1])); |
|
||||||
if (!hasP) |
|
||||||
return false; |
|
||||||
} |
|
||||||
return true; |
|
||||||
}); |
|
||||||
} |
|
||||||
/** |
|
||||||
* Unsubscribe from a subscription |
|
||||||
*/ |
|
||||||
unsubscribe(subId) { |
|
||||||
const subscription = this.subscriptions.get(subId); |
|
||||||
if (!subscription) |
|
||||||
return; |
|
||||||
// Remove message handlers
|
|
||||||
for (const [relayUrl, handler] of subscription.messageHandlers.entries()) { |
|
||||||
const ws = relayPool.getRelay(relayUrl); |
|
||||||
if (ws) { |
|
||||||
ws.removeEventListener('message', handler); |
|
||||||
} |
|
||||||
// Send close message
|
|
||||||
const message = JSON.stringify(['CLOSE', subId]); |
|
||||||
relayPool.send(relayUrl, message); |
|
||||||
} |
|
||||||
this.subscriptions.delete(subId); |
|
||||||
} |
|
||||||
/** |
|
||||||
* Generate a unique subscription ID |
|
||||||
*/ |
|
||||||
generateSubId() { |
|
||||||
return `sub_${this.nextSubId++}_${Date.now()}`; |
|
||||||
} |
|
||||||
/** |
|
||||||
* Close all subscriptions |
|
||||||
*/ |
|
||||||
closeAll() { |
|
||||||
for (const subId of this.subscriptions.keys()) { |
|
||||||
this.unsubscribe(subId); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
export const subscriptionManager = new SubscriptionManager(); |
|
||||||
//# sourceMappingURL=subscription-manager.js.map
|
|
||||||
@ -1 +0,0 @@ |
|||||||
{"version":3,"file":"subscription-manager.js","sourceRoot":"","sources":["subscription-manager.ts"],"names":[],"mappings":"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"} |
|
||||||
@ -1,42 +0,0 @@ |
|||||||
/** |
|
||||||
* Bech32 utilities for NIP-19 encoding/decoding |
|
||||||
*/ |
|
||||||
/** |
|
||||||
* Decode a bech32 string (simplified - full implementation would use bech32 library) |
|
||||||
* This is a placeholder - in production, use a proper bech32 library |
|
||||||
*/ |
|
||||||
export function decodeBech32(bech32) { |
|
||||||
try { |
|
||||||
const prefix = bech32.split('1')[0]; |
|
||||||
if (!prefix) |
|
||||||
return null; |
|
||||||
// Basic validation - full implementation needed
|
|
||||||
if (prefix === 'npub' || prefix === 'nsec' || prefix === 'note') { |
|
||||||
return { |
|
||||||
type: prefix, |
|
||||||
data: new Uint8Array(32) // Placeholder
|
|
||||||
}; |
|
||||||
} |
|
||||||
return null; |
|
||||||
} |
|
||||||
catch { |
|
||||||
return null; |
|
||||||
} |
|
||||||
} |
|
||||||
/** |
|
||||||
* Encode data to bech32 format |
|
||||||
*/ |
|
||||||
export function encodeBech32(type, data, relay) { |
|
||||||
// Placeholder - full implementation needed with bech32 library
|
|
||||||
// For now, return hex representation
|
|
||||||
return `${type}1${Array.from(data) |
|
||||||
.map((b) => b.toString(16).padStart(2, '0')) |
|
||||||
.join('')}`;
|
|
||||||
} |
|
||||||
/** |
|
||||||
* Validate bech32 string format |
|
||||||
*/ |
|
||||||
export function isValidBech32(bech32) { |
|
||||||
return /^(npub|nsec|note|nevent|naddr|nprofile)1[a-z0-9]+$/.test(bech32); |
|
||||||
} |
|
||||||
//# sourceMappingURL=bech32-utils.js.map
|
|
||||||
@ -1 +0,0 @@ |
|||||||
{"version":3,"file":"bech32-utils.js","sourceRoot":"","sources":["bech32-utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAQH;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACpC,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QAEzB,gDAAgD;QAChD,IAAI,MAAM,KAAK,MAAM,IAAI,MAAM,KAAK,MAAM,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;YAChE,OAAO;gBACL,IAAI,EAAE,MAAkC;gBACxC,IAAI,EAAE,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,cAAc;aACxC,CAAC;QACJ,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,IAAgB,EAAE,KAAc;IACzE,+DAA+D;IAC/D,qCAAqC;IACrC,OAAO,GAAG,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;SAC/B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;SAC3C,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,MAAc;IAC1C,OAAO,oDAAoD,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC3E,CAAC"} |
|
||||||
@ -1,48 +0,0 @@ |
|||||||
/** |
|
||||||
* Event validation utilities |
|
||||||
*/ |
|
||||||
/** |
|
||||||
* Validate event structure |
|
||||||
*/ |
|
||||||
export function isValidEvent(event) { |
|
||||||
if (!event || typeof event !== 'object') |
|
||||||
return false; |
|
||||||
const e = event; |
|
||||||
return (typeof e.kind === 'number' && |
|
||||||
typeof e.pubkey === 'string' && |
|
||||||
typeof e.created_at === 'number' && |
|
||||||
typeof e.content === 'string' && |
|
||||||
typeof e.id === 'string' && |
|
||||||
typeof e.sig === 'string' && |
|
||||||
Array.isArray(e.tags) && |
|
||||||
e.pubkey.length === 64 && |
|
||||||
e.id.length === 64 && |
|
||||||
e.sig.length === 128); |
|
||||||
} |
|
||||||
/** |
|
||||||
* Check if event has required tags for a kind |
|
||||||
*/ |
|
||||||
export function hasRequiredTags(event, kind) { |
|
||||||
switch (kind) { |
|
||||||
case 0: |
|
||||||
// Kind 0 can have tags or JSON content
|
|
||||||
return true; |
|
||||||
case 11: |
|
||||||
// Thread - should have title tag
|
|
||||||
return true; |
|
||||||
case 1111: |
|
||||||
// Comment - should have K and E tags
|
|
||||||
return event.tags.some((t) => t[0] === 'K' || t[0] === 'E'); |
|
||||||
default: |
|
||||||
return true; |
|
||||||
} |
|
||||||
} |
|
||||||
/** |
|
||||||
* Validate event signature (placeholder - would need crypto library) |
|
||||||
*/ |
|
||||||
export function isValidSignature(event) { |
|
||||||
// Placeholder - full implementation would verify signature
|
|
||||||
// using secp256k1 cryptography
|
|
||||||
return event.sig.length === 128; |
|
||||||
} |
|
||||||
//# sourceMappingURL=event-validator.js.map
|
|
||||||
@ -1 +0,0 @@ |
|||||||
{"version":3,"file":"event-validator.js","sourceRoot":"","sources":["event-validator.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAEtD,MAAM,CAAC,GAAG,KAAgC,CAAC;IAE3C,OAAO,CACL,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ;QAC1B,OAAO,CAAC,CAAC,MAAM,KAAK,QAAQ;QAC5B,OAAO,CAAC,CAAC,UAAU,KAAK,QAAQ;QAChC,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ;QAC7B,OAAO,CAAC,CAAC,EAAE,KAAK,QAAQ;QACxB,OAAO,CAAC,CAAC,GAAG,KAAK,QAAQ;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;QACrB,CAAC,CAAC,MAAM,CAAC,MAAM,KAAK,EAAE;QACtB,CAAC,CAAC,EAAE,CAAC,MAAM,KAAK,EAAE;QAClB,CAAC,CAAC,GAAG,CAAC,MAAM,KAAK,GAAG,CACrB,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,KAAiB,EAAE,IAAY;IAC7D,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,CAAC;YACJ,uCAAuC;YACvC,OAAO,IAAI,CAAC;QACd,KAAK,EAAE;YACL,iCAAiC;YACjC,OAAO,IAAI,CAAC;QACd,KAAK,IAAI;YACP,qCAAqC;YACrC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC;QAC9D;YACE,OAAO,IAAI,CAAC;IAChB,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAiB;IAChD,2DAA2D;IAC3D,+BAA+B;IAC/B,OAAO,KAAK,CAAC,GAAG,CAAC,MAAM,KAAK,GAAG,CAAC;AAClC,CAAC"} |
|
||||||
@ -1,68 +0,0 @@ |
|||||||
/** |
|
||||||
* Key management with NIP-49 encryption |
|
||||||
* All private keys MUST be encrypted before storage |
|
||||||
*/ |
|
||||||
/** |
|
||||||
* Encrypt a private key using NIP-49 (password-based encryption) |
|
||||||
* This is a placeholder - full implementation requires: |
|
||||||
* - scrypt for key derivation |
|
||||||
* - AES-256-GCM for encryption |
|
||||||
* - Base64 encoding |
|
||||||
* |
|
||||||
* WARNING: Current implementation stores keys in plaintext format. |
|
||||||
* This is insecure and should only be used for development. |
|
||||||
* Production code MUST implement proper NIP-49 encryption. |
|
||||||
*/ |
|
||||||
export async function encryptPrivateKey(nsec, password) { |
|
||||||
// TEMPORARY: Store as base64-encoded plaintext with a marker
|
|
||||||
// This allows the system to function while proper crypto is implemented
|
|
||||||
// Full NIP-49 implementation would:
|
|
||||||
// 1. Derive key from password using scrypt
|
|
||||||
// 2. Generate random salt and nonce
|
|
||||||
// 3. Encrypt nsec with AES-256-GCM
|
|
||||||
// 4. Encode as ncryptsec format
|
|
||||||
const encoded = btoa(JSON.stringify({ nsec, password })); |
|
||||||
return `ncryptsec1${encoded}`; |
|
||||||
} |
|
||||||
/** |
|
||||||
* Decrypt a private key using NIP-49 |
|
||||||
* |
|
||||||
* WARNING: Current implementation reads plaintext keys. |
|
||||||
* This is insecure and should only be used for development. |
|
||||||
* Production code MUST implement proper NIP-49 decryption. |
|
||||||
*/ |
|
||||||
export async function decryptPrivateKey(ncryptsec, password) { |
|
||||||
// TEMPORARY: Decode from base64 plaintext format
|
|
||||||
// This allows the system to function while proper crypto is implemented
|
|
||||||
// Full NIP-49 implementation would:
|
|
||||||
// 1. Decode ncryptsec format
|
|
||||||
// 2. Derive key from password using scrypt
|
|
||||||
// 3. Decrypt with AES-256-GCM
|
|
||||||
// 4. Return plain nsec
|
|
||||||
if (!ncryptsec.startsWith('ncryptsec1')) { |
|
||||||
throw new Error('Invalid ncryptsec format'); |
|
||||||
} |
|
||||||
try { |
|
||||||
const decoded = JSON.parse(atob(ncryptsec.slice(11))); |
|
||||||
if (decoded.password !== password) { |
|
||||||
throw new Error('Invalid password'); |
|
||||||
} |
|
||||||
return decoded.nsec; |
|
||||||
} |
|
||||||
catch (error) { |
|
||||||
throw new Error('Failed to decrypt private key: ' + (error instanceof Error ? error.message : 'Unknown error')); |
|
||||||
} |
|
||||||
} |
|
||||||
/** |
|
||||||
* Generate a new private key |
|
||||||
*/ |
|
||||||
export function generatePrivateKey() { |
|
||||||
// Placeholder - would use crypto.getRandomValues to generate 32 random bytes
|
|
||||||
// then encode as hex
|
|
||||||
const array = new Uint8Array(32); |
|
||||||
crypto.getRandomValues(array); |
|
||||||
return Array.from(array) |
|
||||||
.map((b) => b.toString(16).padStart(2, '0')) |
|
||||||
.join(''); |
|
||||||
} |
|
||||||
//# sourceMappingURL=key-management.js.map
|
|
||||||
@ -1 +0,0 @@ |
|||||||
{"version":3,"file":"key-management.js","sourceRoot":"","sources":["key-management.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,IAAY,EAAE,QAAgB;IACpE,6DAA6D;IAC7D,wEAAwE;IACxE,oCAAoC;IACpC,2CAA2C;IAC3C,oCAAoC;IACpC,mCAAmC;IACnC,gCAAgC;IAChC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;IACzD,OAAO,aAAa,OAAO,EAAE,CAAC;AAChC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,SAAiB,EAAE,QAAgB;IACzE,iDAAiD;IACjD,wEAAwE;IACxE,oCAAoC;IACpC,6BAA6B;IAC7B,2CAA2C;IAC3C,8BAA8B;IAC9B,uBAAuB;IACvB,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QACxC,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;IAC9C,CAAC;IACD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACtD,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;QACtC,CAAC;QACD,OAAO,OAAO,CAAC,IAAI,CAAC;IACtB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,iCAAiC,GAAG,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC;IAClH,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB;IAChC,6EAA6E;IAC7E,qBAAqB;IACrB,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;IACjC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;IAC9B,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC;SACrB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;SAC3C,IAAI,CAAC,EAAE,CAAC,CAAC;AACd,CAAC"} |
|
||||||
@ -1,44 +0,0 @@ |
|||||||
/** |
|
||||||
* HTML sanitization using DOMPurify |
|
||||||
*/ |
|
||||||
import DOMPurify from 'dompurify'; |
|
||||||
/** |
|
||||||
* Sanitize HTML content |
|
||||||
*/ |
|
||||||
export function sanitizeHtml(dirty) { |
|
||||||
return DOMPurify.sanitize(dirty, { |
|
||||||
ALLOWED_TAGS: [ |
|
||||||
'p', |
|
||||||
'br', |
|
||||||
'strong', |
|
||||||
'em', |
|
||||||
'u', |
|
||||||
's', |
|
||||||
'code', |
|
||||||
'pre', |
|
||||||
'a', |
|
||||||
'ul', |
|
||||||
'ol', |
|
||||||
'li', |
|
||||||
'blockquote', |
|
||||||
'h1', |
|
||||||
'h2', |
|
||||||
'h3', |
|
||||||
'h4', |
|
||||||
'h5', |
|
||||||
'h6', |
|
||||||
'img', |
|
||||||
'video', |
|
||||||
'audio' |
|
||||||
], |
|
||||||
ALLOWED_ATTR: ['href', 'src', 'alt', 'title', 'class', 'controls', 'preload'], |
|
||||||
ALLOW_DATA_ATTR: false |
|
||||||
}); |
|
||||||
} |
|
||||||
/** |
|
||||||
* Sanitize markdown-rendered HTML |
|
||||||
*/ |
|
||||||
export function sanitizeMarkdown(html) { |
|
||||||
return sanitizeHtml(html); |
|
||||||
} |
|
||||||
//# sourceMappingURL=sanitizer.js.map
|
|
||||||
@ -1 +0,0 @@ |
|||||||
{"version":3,"file":"sanitizer.js","sourceRoot":"","sources":["sanitizer.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,SAAS,MAAM,WAAW,CAAC;AAElC;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,OAAO,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE;QAC/B,YAAY,EAAE;YACZ,GAAG;YACH,IAAI;YACJ,QAAQ;YACR,IAAI;YACJ,GAAG;YACH,GAAG;YACH,MAAM;YACN,KAAK;YACL,GAAG;YACH,IAAI;YACJ,IAAI;YACJ,IAAI;YACJ,YAAY;YACZ,IAAI;YACJ,IAAI;YACJ,IAAI;YACJ,IAAI;YACJ,IAAI;YACJ,IAAI;YACJ,KAAK;YACL,OAAO;YACP,OAAO;SACR;QACD,YAAY,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,CAAC;QAC7E,eAAe,EAAE,KAAK;KACvB,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC;AAC5B,CAAC"} |
|
||||||
@ -1,5 +1,16 @@ |
|||||||
<script lang="ts"> |
<script lang="ts"> |
||||||
import '../app.css'; |
import '../app.css'; |
||||||
|
import { sessionManager } from '../lib/services/auth/session-manager.js'; |
||||||
|
import { onMount } from 'svelte'; |
||||||
|
|
||||||
|
// Restore session on app load |
||||||
|
onMount(async () => { |
||||||
|
try { |
||||||
|
await sessionManager.restoreSession(); |
||||||
|
} catch (error) { |
||||||
|
console.error('Failed to restore session:', error); |
||||||
|
} |
||||||
|
}); |
||||||
</script> |
</script> |
||||||
|
|
||||||
<slot /> |
<slot /> |
||||||
|
|||||||
Loading…
Reference in new issue