story-research-zapwall/lib/writeOrchestrator.ts
2026-01-08 21:49:57 +01:00

145 lines
4.4 KiB
TypeScript

/**
* 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 { Event, 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, 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(
eventTemplate: EventTemplate,
relays: string[],
objectType: ObjectType,
hash: string,
parsed: unknown,
version: number,
hidden: boolean,
index?: number
): Promise<{ success: boolean; event: Event; published: false | string[] }> {
if (!this.privateKey) {
throw new Error('Private key not set')
}
// Create event
const unsignedEvent: EventTemplate = {
...eventTemplate,
created_at: 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,
hash,
event: finalizedEvent,
parsed,
version,
hidden,
...(index !== undefined ? { index } : {}),
},
relays
)
return {
success: result.success,
event,
published: result.published,
}
}
/**
* Update published status (for republishing)
*/
async updatePublishedStatus(
objectType: ObjectType,
id: string,
published: false | string[]
): Promise<void> {
await writeService.updatePublished(objectType, id, published)
}
}
export const writeOrchestrator = new WriteOrchestrator()