/** * Write orchestrator service - manages writes/updates to WebSockets/API and to write Web Worker * Orchestrates communication between interface, network (Nostr/WebSockets) and write Web Worker */ import { websocketService } from './websocketService' import { writeService } from './writeService' import type { EventTemplate } from 'nostr-tools' import { finalizeEvent } from 'nostr-tools' import { hexToBytes } from 'nostr-tools/utils' import type { ObjectType } from './objectCache' import type { NostrEvent } from 'nostr-tools' interface WriteObjectParams { objectType: ObjectType hash: string event: NostrEvent parsed: unknown version: number hidden: boolean index?: number published?: false | string[] } class WriteOrchestrator { private privateKey: string | null = null /** * Set private key for signing events */ setPrivateKey(privateKey: string): void { this.privateKey = privateKey } /** * Write object to IndexedDB and publish to network * Orchestrates: WebSockets → Web Worker → IndexedDB * Écriture en parallèle réseau et local indépendamment * Si réseau échoue mais écriture locale réussit, rien (un autre service worker réessaiera) */ async writeAndPublish( params: WriteObjectParams, relays: string[] ): Promise<{ success: boolean; eventId: string; published: false | string[] }> { const { objectType, hash, event, parsed, version, hidden, index } = params // Écriture en parallèle : réseau et local indépendamment const [networkResult, localResult] = await Promise.allSettled([ // 1. Publish to network via WebSocket service (en parallèle) websocketService.publishEvent(event, relays).then((statuses) => { return statuses .map((status, statusIndex) => (status.success ? relays[statusIndex] : null)) .filter((relay): relay is string => relay !== null) }), // 2. Write to IndexedDB via Web Worker (en parallèle, avec published: false initialement) writeService.writeObject({ objectType, hash, event, parsed, version, hidden, ...(index !== undefined ? { index } : {}), published: false, }), ]) // Traiter le résultat réseau let publishedRelays: string[] = [] if (networkResult.status === 'fulfilled') { publishedRelays = networkResult.value } else { // Si réseau échoue, rien : un autre service worker réessaiera console.warn('[WriteOrchestrator] Network publish failed, will retry later:', networkResult.reason) } // Traiter le résultat local if (localResult.status === 'rejected') { console.error('[WriteOrchestrator] Local write failed:', localResult.reason) throw new Error(`Failed to write to IndexedDB: ${localResult.reason}`) } // 3. Update published status in IndexedDB via Web Worker (même si réseau a échoué) const publishedStatus: false | string[] = publishedRelays.length > 0 ? publishedRelays : false await writeService.updatePublished(objectType, hash, publishedStatus) return { success: publishedRelays.length > 0, eventId: event.id, published: publishedStatus, } } /** * Create and publish event from template */ async createAndPublishEvent(params: { eventTemplate: EventTemplate relays: string[] objectType: ObjectType hash: string parsed: unknown version: number hidden: boolean index?: number }): Promise<{ success: boolean; event: NostrEvent; published: false | string[] }> { if (!this.privateKey) { throw new Error('Private key not set') } // Create event const unsignedEvent: EventTemplate = { ...params.eventTemplate, created_at: params.eventTemplate.created_at ?? Math.floor(Date.now() / 1000), } const secretKey = hexToBytes(this.privateKey) const finalizedEvent = finalizeEvent(unsignedEvent, secretKey) // Write and publish const result = await this.writeAndPublish( { objectType: params.objectType, hash: params.hash, event: finalizedEvent, parsed: params.parsed, version: params.version, hidden: params.hidden, ...(params.index !== undefined ? { index: params.index } : {}), }, params.relays ) return { success: result.success, event: finalizedEvent, published: result.published, } } /** * Update published status (for republishing) */ async updatePublishedStatus( objectType: ObjectType, id: string, published: false | string[] ): Promise { await writeService.updatePublished(objectType, id, published) } } export const writeOrchestrator = new WriteOrchestrator()