/ utils / mailbox.ts
mailbox.ts
 1  import { createSignal } from './signal.js'
 2  
 3  export type MessageSource = 'user' | 'teammate' | 'system' | 'tick' | 'task'
 4  
 5  export type Message = {
 6    id: string
 7    source: MessageSource
 8    content: string
 9    from?: string
10    color?: string
11    timestamp: string
12  }
13  
14  type Waiter = {
15    fn: (msg: Message) => boolean
16    resolve: (msg: Message) => void
17  }
18  
19  export class Mailbox {
20    private queue: Message[] = []
21    private waiters: Waiter[] = []
22    private changed = createSignal()
23    private _revision = 0
24  
25    get length(): number {
26      return this.queue.length
27    }
28  
29    get revision(): number {
30      return this._revision
31    }
32  
33    send(msg: Message): void {
34      this._revision++
35      const idx = this.waiters.findIndex(w => w.fn(msg))
36      if (idx !== -1) {
37        const waiter = this.waiters.splice(idx, 1)[0]
38        if (waiter) {
39          waiter.resolve(msg)
40          this.notify()
41          return
42        }
43      }
44      this.queue.push(msg)
45      this.notify()
46    }
47  
48    poll(fn: (msg: Message) => boolean = () => true): Message | undefined {
49      const idx = this.queue.findIndex(fn)
50      if (idx === -1) return undefined
51      return this.queue.splice(idx, 1)[0]
52    }
53  
54    receive(fn: (msg: Message) => boolean = () => true): Promise<Message> {
55      const idx = this.queue.findIndex(fn)
56      if (idx !== -1) {
57        const msg = this.queue.splice(idx, 1)[0]
58        if (msg) {
59          this.notify()
60          return Promise.resolve(msg)
61        }
62      }
63      return new Promise<Message>(resolve => {
64        this.waiters.push({ fn, resolve })
65      })
66    }
67  
68    subscribe = this.changed.subscribe
69  
70    private notify(): void {
71      this.changed.emit()
72    }
73  }