@ghostlytools/cli 1.3.1 → 1.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/README.md +6 -6
  2. package/dist/assets/{api-Dq2QXQDk.js → api-B1fADtqd.js} +65 -20
  3. package/dist/assets/api-B1fADtqd.js.map +1 -0
  4. package/dist/assets/{api-TiB5QSG9.js → api-Bg7bh_6E.js} +2 -2
  5. package/dist/assets/{apiKit-CA2x5GJF.js → apiKit-BsUZPHLn.js} +44 -33
  6. package/dist/assets/apiKit-BsUZPHLn.js.map +1 -0
  7. package/dist/assets/{apps-BxV9i_Y4.js → apps-BaUNt-5c.js} +91 -24
  8. package/dist/assets/apps-BaUNt-5c.js.map +1 -0
  9. package/dist/assets/{apps-C-T8jeji.js → apps-DepomCiX.js} +1 -1
  10. package/dist/assets/{arkade-C_qDmHnm.js → arkade-CNnMaoEQ.js} +4 -4
  11. package/dist/assets/arkade-CNnMaoEQ.js.map +1 -0
  12. package/dist/assets/{backup-Bugnn3Ue.js → backup-B5Q1W-Ey.js} +1 -1
  13. package/dist/assets/{backup-CnMN2oUK.js → backup-Bbz3lyC5.js} +6 -6
  14. package/dist/assets/{backup-CnMN2oUK.js.map → backup-Bbz3lyC5.js.map} +1 -1
  15. package/dist/assets/buttons-CUd7j0ZA.js +2 -0
  16. package/dist/assets/{buttons-CvNBzh2S.js → buttons-DS1iURp8.js} +4 -4
  17. package/dist/assets/{buttons-CvNBzh2S.js.map → buttons-DS1iURp8.js.map} +1 -1
  18. package/dist/assets/{envelope-WjingYSc.js → envelope-fNC941z4.js} +2 -2
  19. package/dist/assets/{envelope-WjingYSc.js.map → envelope-fNC941z4.js.map} +1 -1
  20. package/dist/assets/{errors-DO5z6h_l.js → errors-B-zMxbcd.js} +6 -5
  21. package/dist/assets/{errors-DO5z6h_l.js.map → errors-B-zMxbcd.js.map} +1 -1
  22. package/dist/assets/{ghostly_iroh_web-Dvzh3Y6E.js → ghostly_iroh_web-BsAEFIjl.js} +30 -36
  23. package/dist/assets/ghostly_iroh_web-BsAEFIjl.js.map +1 -0
  24. package/dist/assets/ghostly_iroh_web_bg.wasm +0 -0
  25. package/dist/assets/{groupFiles-IEUW3DQz.js → groupFiles-6qnLCnOm.js} +82 -29
  26. package/dist/assets/groupFiles-6qnLCnOm.js.map +1 -0
  27. package/dist/assets/{host-CWt-jtqz.js → host-Dj3C1fn3.js} +305 -68
  28. package/dist/assets/host-Dj3C1fn3.js.map +1 -0
  29. package/dist/assets/{hyperdhtRelay-CPYwSLLS.js → hyperdhtRelay-CrgYQN-g.js} +2 -2
  30. package/dist/assets/{hyperdhtRelay-CPYwSLLS.js.map → hyperdhtRelay-CrgYQN-g.js.map} +1 -1
  31. package/dist/assets/{irohWeb-4oChIOIp.js → irohWeb-DSyLp5Re.js} +3 -3
  32. package/dist/assets/{irohWeb-4oChIOIp.js.map → irohWeb-DSyLp5Re.js.map} +1 -1
  33. package/dist/assets/{main-DbqcdShN.js → main-BDd11Yd1.js} +159 -54
  34. package/dist/assets/main-BDd11Yd1.js.map +1 -0
  35. package/dist/assets/{mainline-ZkiOnevs.js → mainline-CLTo0nGa.js} +2 -2
  36. package/dist/assets/{mainline-ZkiOnevs.js.map → mainline-CLTo0nGa.js.map} +1 -1
  37. package/dist/assets/{networkReason-CetKkTPo.js → networkReason-DwcadZz5.js} +2 -2
  38. package/dist/assets/{networkReason-CetKkTPo.js.map → networkReason-DwcadZz5.js.map} +1 -1
  39. package/dist/assets/{nwc-CgkqyvsE.js → nwc-BNFyyECH.js} +3 -3
  40. package/dist/assets/{nwc-CgkqyvsE.js.map → nwc-BNFyyECH.js.map} +1 -1
  41. package/dist/assets/{nwcProvider-4eVz_8HP.js → nwcProvider-BbjJOfLD.js} +2 -2
  42. package/dist/assets/{nwcProvider-4eVz_8HP.js.map → nwcProvider-BbjJOfLD.js.map} +1 -1
  43. package/dist/assets/{profiles-BHc9pEYg.js → profiles-gPCjLOQq.js} +2 -2
  44. package/dist/assets/{profiles-BHc9pEYg.js.map → profiles-gPCjLOQq.js.map} +1 -1
  45. package/dist/assets/{registry-DlAKO0Fy.js → registry-CoLHpxkb.js} +17 -32
  46. package/dist/assets/registry-CoLHpxkb.js.map +1 -0
  47. package/dist/assets/{registry-OBJBnHVQ.js → registry-DoTb6uEr.js} +1 -1
  48. package/dist/assets/{resolve-CouHhCpJ.js → resolve-BIDJ-UUl.js} +18 -3
  49. package/dist/assets/resolve-BIDJ-UUl.js.map +1 -0
  50. package/dist/assets/{server-Ss0gJ-nw.js → server-Bx3RGkBC.js} +380 -116
  51. package/dist/assets/server-Bx3RGkBC.js.map +1 -0
  52. package/dist/assets/{src-DDzSERyc.js → src-D7gcJElT.js} +470 -242
  53. package/dist/assets/src-D7gcJElT.js.map +1 -0
  54. package/dist/assets/{turnRefs-B87dtrHM.js → turnRefs-PN644M9o.js} +2 -2
  55. package/dist/assets/{turnRefs-B87dtrHM.js.map → turnRefs-PN644M9o.js.map} +1 -1
  56. package/dist/assets/{usdt-Fj8CQO2z.js → usdt-B2TLRlYa.js} +3 -3
  57. package/dist/assets/{usdt-Fj8CQO2z.js.map → usdt-B2TLRlYa.js.map} +1 -1
  58. package/dist/ghostly.mjs +1 -1
  59. package/package.json +1 -1
  60. package/dist/assets/api-Dq2QXQDk.js.map +0 -1
  61. package/dist/assets/apiKit-CA2x5GJF.js.map +0 -1
  62. package/dist/assets/apps-BxV9i_Y4.js.map +0 -1
  63. package/dist/assets/arkade-C_qDmHnm.js.map +0 -1
  64. package/dist/assets/buttons-Bj7Vf7Pn.js +0 -2
  65. package/dist/assets/ghostly_iroh_web-Dvzh3Y6E.js.map +0 -1
  66. package/dist/assets/groupFiles-IEUW3DQz.js.map +0 -1
  67. package/dist/assets/host-CWt-jtqz.js.map +0 -1
  68. package/dist/assets/main-DbqcdShN.js.map +0 -1
  69. package/dist/assets/registry-DlAKO0Fy.js.map +0 -1
  70. package/dist/assets/resolve-CouHhCpJ.js.map +0 -1
  71. package/dist/assets/server-Ss0gJ-nw.js.map +0 -1
  72. package/dist/assets/src-DDzSERyc.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host-Dj3C1fn3.js","names":["dnsLookup"],"sources":["../../src/announce.ts","../../src/calls/pcm.ts","../../src/calls/audioSocket.ts","../../src/calls/rtp.ts","../../src/calls/audio.ts","../../src/calls/media.ts","../../src/calls/manager.ts","../../src/events.ts","../../src/joinLog.ts","../../src/runtime/fedimint.ts","../../src/runtime/fileFetch.ts","../../src/runtime/pushSend.ts","../../src/runtime/rows.ts","../../src/runtime/keyBytes.ts","../../src/runtime/sqlTree.ts","../../src/runtime/storage.ts","../../src/runtime/engine.ts","../../src/host.ts"],"sourcesContent":["import type { GhostlyNode } from \"@ghostly/browser/engine/node\";\nimport type { EventHub } from \"./events\";\n\n/**\n * What the app's chat screen does by itself, done here for a profile no screen shows (apps/ui/src/hooks/useChat.ts): the side\n * that joined says \"👋 <name> joined\" once the chat first goes live, and the side that invited answers once. Contacts\n * see the bot arrive as they see a person; each is said once per chat, across restarts.\n */\nexport function announceJoins(hub: EventHub, node: GhostlyNode): () => void {\n const say = (chat: string) => {\n if (hub.said(chat, \"join\")) return;\n hub.markSaid(chat, \"join\");\n const nick = node.getState().settings.nick?.trim();\n void node.sendMessage({ linkId: chat, text: nick ? `👋 ${nick} joined` : \"👋 joined\" }).catch(() => {});\n };\n return hub.onEvent((event) => {\n const chat = event.chat as string | undefined;\n if (!chat) return;\n const link = node.getState().links.find((l) => l.id === chat);\n if (!link?.profile) return;\n // The joiner once its first pairing is live; the other side when a notice arrives (said once either way).\n if (event.type === \"chat.connection\" && event.live && link.pairingProgress?.role === \"joiner\") say(chat);\n else if (event.type === \"chat.joined\") say(chat);\n });\n}\n","/**\n * The audio a call hands to an outside program, and takes from it (WISP 1100 § Calls): raw PCM, signed 16-bit\n * little-endian, mono, at the call's rate, in frames of 20 ms. Opus runs at every one of these rates itself, so\n * nothing is resampled.\n */\nexport const FRAME_MS = 20;\nexport const CALL_RATES = [8000, 12000, 16000, 24000, 48000] as const;\nexport type CallRate = (typeof CALL_RATES)[number];\nexport const DEFAULT_RATE: CallRate = 48000;\n\nexport const frameSamples = (rate: CallRate): number => (rate * FRAME_MS) / 1000;\nexport const frameBytes = (rate: CallRate): number => frameSamples(rate) * 2;\nexport const bytesToMs = (rate: CallRate, bytes: number): number => Math.round((bytes / 2 / rate) * 1000);\n\nexport function isCallRate(value: unknown): value is CallRate {\n return CALL_RATES.includes(value as CallRate);\n}\n\n/** The most a program may queue ahead of the call: five minutes of speech. What goes past it is dropped. */\nexport const MAX_QUEUE_MS = 5 * 60_000;\n\n/**\n * A partial frame waits this long for the rest of it before it is played padded with silence: a program writing at\n * real time, a little late, is not cut into gaps; a program that stopped mid-frame is still heard to its end.\n */\nexport const PARTIAL_WAIT_MS = 60;\n\n/**\n * What the program wrote, waiting to be sent: taken one 20 ms frame per tick, silence when there is nothing, and\n * dropped whole by a flush (barge-in). The program may write any amount at any time, faster than real time too.\n */\nexport class PlaybackQueue {\n private chunks: Buffer[] = [];\n private head = 0;\n private length = 0;\n private lastPush = 0;\n private readonly frame: number;\n private readonly max: number;\n private readonly silence: Buffer;\n /** Bytes dropped because the queue was full, since the start. */\n dropped = 0;\n\n constructor(readonly rate: CallRate, private readonly now: () => number = Date.now, maxMs = MAX_QUEUE_MS) {\n this.frame = frameBytes(rate);\n // Whole samples only: an odd byte waits for its other half.\n this.max = Math.floor((rate * maxMs) / 1000) * 2;\n this.silence = Buffer.alloc(this.frame);\n }\n\n /** Adds what the program wrote. Returns the bytes it could not keep (the queue was full). */\n push(chunk: Buffer): number {\n if (!chunk.length) return 0;\n this.lastPush = this.now();\n const room = this.max - this.length;\n const kept = chunk.length <= room ? chunk : chunk.subarray(0, Math.max(0, room));\n const lost = chunk.length - kept.length;\n this.dropped += lost;\n if (kept.length) { this.chunks.push(Buffer.from(kept)); this.length += kept.length; }\n return lost;\n }\n\n /** Queued audio, in milliseconds (whole frames and a partial one). */\n get queuedMs(): number {\n return bytesToMs(this.rate, this.length - (this.length % 2));\n }\n\n /** One frame to send now, and whether it is the program's audio or silence. */\n next(): { frame: Buffer; silent: boolean } {\n if (this.length >= this.frame) return { frame: this.take(this.frame), silent: false };\n const whole = this.length - (this.length % 2);\n if (whole > 0 && this.now() - this.lastPush >= PARTIAL_WAIT_MS) {\n const frame = Buffer.alloc(this.frame);\n this.take(whole).copy(frame);\n return { frame, silent: false };\n }\n return { frame: this.silence, silent: true };\n }\n\n /** Drops everything queued; returns how much, in milliseconds. */\n flush(): number {\n const ms = this.queuedMs;\n this.chunks = [];\n this.head = 0;\n this.length = 0;\n return ms;\n }\n\n /** Drops up to that much from the front, in whole frames (audio whose time went by); returns how much, in milliseconds. */\n skip(ms: number): number {\n const frames = Math.min(Math.floor(ms / FRAME_MS), Math.floor(this.length / this.frame));\n if (frames > 0) this.take(frames * this.frame);\n return Math.max(0, frames) * FRAME_MS;\n }\n\n private take(bytes: number): Buffer {\n const out = Buffer.allocUnsafe(bytes);\n let filled = 0;\n while (filled < bytes) {\n const chunk = this.chunks[0];\n const n = Math.min(bytes - filled, chunk.length - this.head);\n chunk.copy(out, filled, this.head, this.head + n);\n filled += n;\n this.head += n;\n if (this.head === chunk.length) { this.chunks.shift(); this.head = 0; }\n }\n this.length -= bytes;\n return out;\n }\n}\n","import { createHash } from \"node:crypto\";\nimport { chmodSync, existsSync, lstatSync, readdirSync, rmSync } from \"node:fs\";\nimport { createServer, type Server, type Socket } from \"node:net\";\nimport { dirname, join } from \"node:path\";\nimport { privateFolder } from \"../privateFolder\";\nimport { bytesToMs, type CallRate } from \"./pcm\";\n\n/**\n * A call's audio socket (WISP 1100 § Calls): `<profile>/calls/<call>.sock`, 0600, in a folder only the owner may\n * open. When that path is too long for a Unix socket: `/tmp/ghostly-calls-<hash of the profile>/<call>.sock`, in a\n * folder of the owner's, 0700, checked as such before use. A named pipe on Windows.\n */\nexport function audioSocketPath(profileDir: string, callId: string): string {\n const inside = join(profileDir, \"calls\", `${callId}.sock`);\n if (process.platform === \"win32\") return `\\\\\\\\.\\\\pipe\\\\ghostly-call-${createHash(\"sha256\").update(inside).digest(\"hex\").slice(0, 24)}`;\n if (Buffer.byteLength(inside) <= 100) return inside;\n return join(\"/tmp\", `ghostly-calls-${createHash(\"sha256\").update(profileDir).digest(\"hex\").slice(0, 24)}`, `${callId}.sock`);\n}\n\n/**\n * Removes the call sockets a daemon that died during a call left in this profile's folder (`audioSocketPath`; call\n * ids are 12 hex digits): no call outlives its daemon. A folder that is a link or someone else's is left alone.\n */\nexport function clearAudioSockets(profileDir: string): void {\n if (process.platform === \"win32\") return;\n const folder = dirname(audioSocketPath(profileDir, \"0\".repeat(12)));\n try {\n const info = lstatSync(folder);\n if (!info.isDirectory() || (process.getuid && info.uid !== process.getuid())) return;\n for (const entry of readdirSync(folder, { withFileTypes: true })) {\n if (entry.name.endsWith(\".sock\") && !entry.isDirectory()) rmSync(join(folder, entry.name), { force: true });\n }\n } catch {\n // No folder yet, or one it cannot read: nothing to clear.\n }\n}\n\n/** How far a program may fall behind reading the call's audio before frames are dropped for it. */\nexport const MAX_UNREAD_MS = 1000;\n\n/**\n * Serves one call's audio to one program at a time: what the program writes is handed to `onAudio` as it comes (raw\n * PCM, any amount); `write` gives it the caller's audio. A second connection replaces the first. `close` ends the\n * program's connection (it reads EOF) and removes the socket.\n */\nexport class AudioSocket {\n private client: Socket | null = null;\n private closed = false;\n /** Frames the program did not read in time, since the start. */\n dropped = 0;\n\n private constructor(readonly path: string, private readonly server: Server, private readonly rate: CallRate) {}\n\n static async open(path: string, rate: CallRate, handlers: { onAudio(chunk: Buffer): void; onClient?(connected: boolean): void }): Promise<AudioSocket> {\n if (process.platform !== \"win32\") {\n privateFolder(join(path, \"..\"), \"calls will not put their audio there\");\n if (existsSync(path)) rmSync(path, { force: true });\n }\n let self: AudioSocket | null = null;\n const server = createServer((socket) => self!.accept(socket, handlers));\n await new Promise<void>((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(path, () => { server.off(\"error\", reject); resolve(); });\n });\n if (process.platform !== \"win32\") chmodSync(path, 0o600);\n self = new AudioSocket(path, server, rate);\n return self;\n }\n\n get connected(): boolean {\n return !!this.client && !this.client.destroyed;\n }\n\n private accept(socket: Socket, handlers: { onAudio(chunk: Buffer): void; onClient?(connected: boolean): void }): void {\n if (this.closed) { socket.destroy(); return; }\n const before = this.client;\n this.client = socket;\n before?.end();\n socket.setNoDelay?.(true);\n socket.on(\"data\", (chunk: Buffer) => { if (this.client === socket) handlers.onAudio(chunk); });\n socket.on(\"error\", () => {});\n socket.on(\"close\", () => {\n if (this.client !== socket) return;\n this.client = null;\n handlers.onClient?.(false);\n });\n handlers.onClient?.(true);\n }\n\n /** One frame of the caller's audio for the program; dropped when nobody listens or the program stopped reading. */\n write(frame: Buffer): void {\n const client = this.client;\n if (!client || client.destroyed) return;\n if (bytesToMs(this.rate, client.writableLength) > MAX_UNREAD_MS) { this.dropped++; return; }\n client.write(frame);\n }\n\n /** Ends the program's connection (EOF once what was written is read) and removes the socket. */\n close(): Promise<void> {\n if (this.closed) return Promise.resolve();\n this.closed = true;\n const client = this.client;\n this.client = null;\n client?.end();\n if (client) setTimeout(() => client.destroy(), 2000).unref();\n return new Promise((resolve) => {\n this.server.close(() => resolve());\n if (process.platform !== \"win32\") rmSync(this.path, { force: true });\n });\n }\n}\n","import { randomBytes } from \"node:crypto\";\n\n/**\n * RTP for a call's Opus audio (RFC 3550, RFC 7587): libdatachannel runs ICE, DTLS and SRTP, and hands this side\n * plain RTP packets each way. The RTP clock of Opus is 48 kHz whatever the audio's own rate.\n */\nexport const OPUS_CLOCK = 48_000;\n/** RTP timestamp ticks in one 20 ms frame. */\nexport const TICKS_PER_FRAME = (OPUS_CLOCK * 20) / 1000;\n\nexport interface RtpPacket { payloadType: number; marker: boolean; sequence: number; timestamp: number; ssrc: number; payload: Buffer }\n\n/** Whether a packet on the muxed port is RTCP (RFC 5761 § 4: its second byte is 192-223), not RTP. */\nexport function isRtcp(packet: Buffer): boolean {\n return packet.length >= 2 && packet[1] >= 192 && packet[1] <= 223;\n}\n\n/** Reads an RTP packet: CSRCs, a header extension and padding are stepped over. Null for anything malformed. */\nexport function parseRtp(packet: Buffer): RtpPacket | null {\n if (packet.length < 12 || packet[0] >> 6 !== 2 || isRtcp(packet)) return null;\n let offset = 12 + (packet[0] & 0x0f) * 4;\n if (packet[0] & 0x10) {\n if (packet.length < offset + 4) return null;\n offset += 4 + packet.readUInt16BE(offset + 2) * 4;\n }\n let end = packet.length;\n if (packet[0] & 0x20) {\n const padding = packet[packet.length - 1];\n if (padding === 0) return null;\n end -= padding;\n }\n if (end < offset) return null;\n return {\n payloadType: packet[1] & 0x7f,\n marker: (packet[1] & 0x80) !== 0,\n sequence: packet.readUInt16BE(2),\n timestamp: packet.readUInt32BE(4),\n ssrc: packet.readUInt32BE(8),\n payload: packet.subarray(offset, end),\n };\n}\n\n/** Numbers this side's packets: one SSRC, a sequence and a timestamp from random starts. */\nexport class RtpWriter {\n private sequence = randomBytes(2).readUInt16BE(0);\n private timestamp = randomBytes(4).readUInt32BE(0);\n\n constructor(readonly ssrc: number, readonly payloadType: number) {}\n\n /** The next frame's packet. `marker` starts a talkspurt (the first sound after silence). */\n packet(payload: Buffer, marker = false): Buffer {\n const header = Buffer.allocUnsafe(12);\n header[0] = 0x80;\n header[1] = (marker ? 0x80 : 0) | (this.payloadType & 0x7f);\n header.writeUInt16BE(this.sequence, 2);\n header.writeUInt32BE(this.timestamp, 4);\n header.writeUInt32BE(this.ssrc >>> 0, 8);\n this.sequence = (this.sequence + 1) & 0xffff;\n this.timestamp = (this.timestamp + TICKS_PER_FRAME) >>> 0;\n return Buffer.concat([header, payload]);\n }\n}\n\n/** What the reorder buffer gives out: a packet's payload to decode, or a frame that never came. */\nexport type Playout = { payload: Buffer } | { lost: true };\n\n/** The most silence a gap in the packets becomes. */\nconst MAX_LOST_FRAMES = 3;\n\n/** Sequence numbers compared across the 16-bit wrap: positive when `a` comes after `b`. */\nexport function sequenceDelta(a: number, b: number): number {\n const d = (a - b) & 0xffff;\n return d >= 0x8000 ? d - 0x10000 : d;\n}\n\n/**\n * Puts packets back in order, holding at most `depth` of them for one that is late: a lost packet costs one frame\n * of silence (40 ms of waiting at most), a duplicate or a packet that came too late is dropped, and a new stream (its\n * SSRC changed, or its sequence jumped) starts over.\n */\nexport class ReorderBuffer {\n private expected: number | null = null;\n private ssrc: number | null = null;\n private readonly held = new Map<number, Buffer>();\n\n constructor(private readonly depth = 2) {}\n\n push(packet: RtpPacket): Playout[] {\n if (this.ssrc !== packet.ssrc || this.expected === null || Math.abs(sequenceDelta(packet.sequence, this.expected)) > 1000) {\n this.ssrc = packet.ssrc;\n this.expected = packet.sequence;\n this.held.clear();\n }\n const ahead = sequenceDelta(packet.sequence, this.expected);\n if (ahead < 0 || this.held.has(packet.sequence)) return [];\n this.held.set(packet.sequence, packet.payload);\n const out: Playout[] = [];\n this.release(out);\n // Still waiting for one that is late: once too many wait behind it, the gap is given up for lost. A long gap\n // (the network stalled) is not replayed as that much silence: a few frames of it mark the break.\n while (this.held.size > this.depth) {\n const next = [...this.held.keys()].reduce((a, b) => (sequenceDelta(a, b) < 0 ? a : b));\n const gap = sequenceDelta(next, this.expected);\n for (let i = 0; i < Math.min(gap, MAX_LOST_FRAMES); i++) out.push({ lost: true });\n this.expected = next;\n this.release(out);\n }\n return out;\n }\n\n private release(out: Playout[]): void {\n for (;;) {\n const payload = this.held.get(this.expected!);\n if (!payload) return;\n this.held.delete(this.expected!);\n out.push({ payload });\n this.expected = (this.expected! + 1) & 0xffff;\n }\n }\n}\n","import { FRAME_MS, frameBytes, frameSamples, PlaybackQueue, type CallRate } from \"./pcm\";\nimport { isRtcp, parseRtp, ReorderBuffer, RtpWriter } from \"./rtp\";\n\n/** The longest stall of the sender whose frames are still sent after it (see `catchUp`). */\nexport const MAX_CATCH_UP_MS = 1000;\n\n/**\n * Silence is encoded until its packet came out the same this many frames in a row (half a second): by then the\n * encoder gives that packet for as long as the silence lasts, and each encode (libopus in WebAssembly, 0.5 to 1 ms at\n * 48 kHz, 50 times a second) only finds it again. See `settle`.\n */\nexport const SETTLED_FRAMES = 25;\n\n/** An Opus encoder or decoder at one rate, mono (opusscript's shape). */\nexport interface OpusCodec {\n encode(pcm: Buffer, frameSize: number): Buffer;\n decode(packet: Buffer): Buffer;\n delete?(): void;\n}\n\n/** The call's audio lane: RTP packets out, and every packet (RTP and RTCP) that comes in. */\nexport interface RtpTrack {\n send(packet: Buffer): void;\n onPacket(listener: (packet: Buffer) => void): void;\n}\n\nexport interface CallAudioOptions {\n rate: CallRate;\n /** What the call's SDP gives Opus. */\n payloadType: number;\n ssrc: number;\n track: RtpTrack;\n encoder: OpusCodec;\n decoder: OpusCodec;\n /** A 20 ms frame of the contact's audio, decoded. */\n onFrame(frame: Buffer): void;\n /** What the program wrote, to send (the call's own, kept from before the media existed); a new one if none. */\n queue?: PlaybackQueue;\n now?: () => number;\n}\n\n/**\n * One call's audio: every 20 ms a frame from the queue (the program's audio, or silence) is encoded and sent; what\n * comes in is put back in order, decoded, and handed on in 20 ms frames as soon as it is. Nothing waits on a clock\n * but the sending side, so the audio adds at most a frame of delay each way inside the CLI.\n */\nexport class CallAudio {\n readonly queue: PlaybackQueue;\n private readonly writer: RtpWriter;\n private readonly reorder = new ReorderBuffer();\n private readonly frame: number;\n private readonly samples: number;\n private readonly silence: Buffer;\n private pending: Buffer = Buffer.alloc(0);\n private timer: ReturnType<typeof setTimeout> | null = null;\n private startedAt = 0;\n private ticks = 0;\n /** What was left in the queue after the last frame sent: the program's audio written ahead of the call. */\n private ahead = 0;\n /** After a stall that was not replayed: how much of its audio is still to drop, down to what queue, and until when. */\n private owed = 0;\n private kept = 0;\n private owedUntil = 0;\n private talking = false;\n /** The last silent frame's packet, how many silent frames in a row gave it, and that packet once it is kept (see `settle`). */\n private lastQuiet: Buffer | null = null;\n private same = 0;\n private quiet: Buffer | null = null;\n private stopped = false;\n /** Frames sent and received, for `call list`. */\n sent = 0;\n received = 0;\n /** When the last packet, RTP or RTCP, came from the contact (Date.now(); 0: none yet), for the call's watch. */\n heardAt = 0;\n\n constructor(private readonly options: CallAudioOptions) {\n const now = options.now ?? Date.now;\n this.queue = options.queue ?? new PlaybackQueue(options.rate, now);\n this.writer = new RtpWriter(options.ssrc, options.payloadType);\n this.frame = frameBytes(options.rate);\n this.samples = frameSamples(options.rate);\n this.silence = Buffer.alloc(this.frame);\n options.track.onPacket((packet) => this.receive(packet));\n }\n\n /** Starts sending, a frame every 20 ms from now on. */\n start(): void {\n if (this.timer || this.stopped) return;\n this.startedAt = (this.options.now ?? Date.now)();\n this.ticks = 0;\n this.schedule();\n }\n\n stop(): void {\n if (this.timer) clearTimeout(this.timer);\n this.timer = null;\n if (this.stopped) return;\n this.stopped = true;\n // Once, and never throwing: a codec of a broken Opus module (\"memory access out of bounds\") does not keep the call\n // from ending (its connection, its program's socket, the hang-up and call.ended come after).\n for (const codec of [this.options.encoder, this.options.decoder]) {\n try { codec.delete?.(); } catch { /* gone with the module */ }\n }\n }\n\n /** Sends the frame that is due now. Exposed for tests, which drive the clock themselves. */\n tick(): void {\n if (this.stopped) return;\n const { frame, silent } = this.queue.next();\n // Silence the program writes itself (a muted microphone) is the same silence to the encoder.\n const still = silent || frame.equals(this.silence);\n let payload: Buffer;\n if (still && this.quiet) payload = this.quiet;\n else {\n try { payload = this.options.encoder.encode(frame, this.samples); } catch { return; }\n this.settle(still, payload);\n }\n // A talkspurt starts at the first sound after silence (RFC 3551 § 4.1).\n const marker = !silent && !this.talking;\n this.talking = !silent;\n this.options.track.send(this.writer.packet(payload, marker));\n this.sent++;\n this.ahead = this.queue.queuedMs;\n }\n\n /**\n * Keeps the packet silence encodes to once the encoder gave it SETTLED_FRAMES times in a row: it is sent again for\n * the silent frames that follow, without the encoder, until the program's audio is back.\n */\n private settle(still: boolean, payload: Buffer): void {\n if (!still) { this.lastQuiet = null; this.quiet = null; this.same = 0; return; }\n if (this.lastQuiet?.equals(payload)) { if (++this.same >= SETTLED_FRAMES) this.quiet = this.lastQuiet; return; }\n // A copy: the encoder gives a view of its own memory.\n this.lastQuiet = Buffer.from(payload);\n this.same = 1;\n }\n\n /** Handles one packet from the track. Exposed for tests. */\n receive(packet: Buffer): void {\n if (this.stopped) return;\n this.heardAt = (this.options.now ?? Date.now)();\n if (isRtcp(packet)) return;\n const rtp = parseRtp(packet);\n if (!rtp || rtp.payloadType !== this.options.payloadType) return;\n for (const item of this.reorder.push(rtp)) {\n let pcm: Buffer;\n if (\"lost\" in item) pcm = this.silence;\n else {\n try { pcm = this.options.decoder.decode(item.payload); } catch { pcm = this.silence; }\n }\n this.emit(pcm);\n }\n }\n\n /** Hands decoded audio on in 20 ms frames, whatever the contact's packet length (10, 20, 40 or 60 ms). */\n private emit(pcm: Buffer): void {\n let data = this.pending.length ? Buffer.concat([this.pending, pcm]) : pcm;\n while (data.length >= this.frame) {\n this.options.onFrame(Buffer.from(data.subarray(0, this.frame)));\n this.received++;\n data = data.subarray(this.frame);\n }\n this.pending = Buffer.from(data);\n }\n\n private schedule(): void {\n const now = this.options.now ?? Date.now;\n const due = this.startedAt + (this.ticks + 1) * FRAME_MS;\n this.timer = setTimeout(() => {\n if (this.stopped) return;\n if (now() - due < FRAME_MS) { this.ticks++; this.drop(); this.tick(); this.schedule(); return; }\n // Late (a busy event loop: a file going out, a journal fsync, a Pkarr publish): what the program wrote meanwhile\n // is still in its socket, read after the timers of this turn, so the frames wait for that.\n setImmediate(() => {\n if (this.stopped) return;\n this.catchUp(now() - due);\n this.schedule();\n });\n }, Math.max(0, due - now()));\n }\n\n /**\n * Meets the clock again after the loop was busy: every frame that is due goes out at once, so a loop that turns\n * slower than a frame for a while (every 40 ms under a large file) keeps up, and a program writing at real time\n * stays there. The contact's jitter buffer takes the short burst. A stall past MAX_CATCH_UP_MS is not replayed: the\n * clock starts again and what the program wrote during it is dropped, or it would be heard that much late for the\n * rest of the call.\n */\n private catchUp(late: number): void {\n const now = this.options.now ?? Date.now;\n if (late > MAX_CATCH_UP_MS) {\n this.owed = late;\n this.kept = this.ahead;\n this.owedUntil = now() + MAX_CATCH_UP_MS;\n this.startedAt = now();\n this.ticks = 0;\n this.drop();\n this.tick();\n return;\n }\n do { this.ticks++; this.drop(); this.tick(); } while (this.startedAt + (this.ticks + 1) * FRAME_MS <= now());\n }\n\n /**\n * Drops the audio of a stall that was not replayed, as it is read: a long one does not fit the program's socket, so\n * the rest of it comes over the next turns. What was queued before the stall (a clip written ahead) is kept.\n */\n private drop(): void {\n if (this.owed <= 0) return;\n if ((this.options.now ?? Date.now)() > this.owedUntil) { this.owed = 0; return; }\n this.owed -= this.queue.skip(Math.min(this.owed, this.queue.queuedMs - this.kept));\n }\n}\n","import { randomBytes } from \"node:crypto\";\nimport { lookup } from \"node:dns/promises\";\nimport { createRequire } from \"node:module\";\nimport type { IceServer, PeerConnection, Track } from \"node-datachannel\";\nimport { buildSdpFromSignal, callRtcConfig, extractParamsFromSdp, PAIRED_CALL_CANDIDATES, RTC_CONFIG, type CallIceServer, type CallSignal } from \"@ghostly/core\";\nimport { CallAudio, type OpusCodec } from \"./audio\";\nimport type { CallRate, PlaybackQueue } from \"./pcm\";\n\n/**\n * A call's media on Node (WISP 601, WISP 1100 § Calls): libdatachannel (node-datachannel, the same native module\n * the CLI's chats use) runs ICE, DTLS-SRTP and RTP; Opus is libopus built to WebAssembly (opusscript). Audio only:\n * a call's video section is answered closed (`vr: 0`), and nothing is sent or read on it.\n */\nexport interface CallStack {\n ndc: typeof import(\"node-datachannel\");\n opus(rate: CallRate): OpusCodec;\n}\n\nlet loading: Promise<CallStack | string> | null = null;\n\n/** opusscript's libopus in WebAssembly, the module its own wrapper drives. */\ninterface OpusNative {\n OpusScriptHandler: { new (rate: number, channels: number, application: number): OpusHandler; destroy_handler(handler: OpusHandler): void };\n _malloc(bytes: number): number;\n _free(pointer: number): void;\n HEAPU8: Uint8Array;\n HEAPU16: Uint16Array;\n}\ninterface OpusHandler {\n _encode(pcm: number, bytes: number, packet: number, frameSize: number): number;\n _decode(packet: number, bytes: number, pcm: number): number;\n _encoder_ctl(ctl: number, value: number): number;\n}\n\nconst OPUS_APPLICATION_VOIP = 2048, OPUS_SET_BITRATE = 4002;\n/** The largest packet a codec reads or writes (opusscript's MAX_PACKET_SIZE). */\nconst MAX_PACKET_BYTES = 1276 * 3;\n/**\n * Room for a frame of PCM as the module reads and writes it, one byte of PCM in each 16-bit element: a concealment (an\n * empty packet) writes 120 ms at 48 kHz, 5,760 samples, 23,040 bytes, at any rate.\n */\nconst PCM_ROOM_BYTES = 5760 * 2 * 2;\n/** The most PCM a decode hands back, as opusscript's: its largest frame, 60 ms at 48 kHz (CallAudio's `conceal`). */\nconst MAX_PCM_OUT = (48_000 * 60) / 1000 * 2;\n\n/**\n * An Opus encoder or decoder on opusscript's libopus, with buffers that hold what libopus writes. opusscript's own\n * wrapper (0.1.1) keeps 5,760 bytes for its PCM and reaches it through views at twice the buffer's address, past it: a\n * 20 ms frame at 48 kHz takes 3,840 bytes there and a concealment 23,040. With three calls or more, as calls ended and\n * others started, one codec's audio landed on another's state: noise, then \"memory access out of bounds\" in every\n * call until the daemon restarted. The bytes in and out are the same as opusscript's.\n */\nfunction opusCodec(native: OpusNative, rate: CallRate): OpusCodec {\n const handler = new native.OpusScriptHandler(rate, 1, OPUS_APPLICATION_VOIP);\n const pcm = native._malloc(PCM_ROOM_BYTES), packet = native._malloc(MAX_PACKET_BYTES);\n let deleted = false;\n const codec: OpusCodec = {\n encode(frame, frameSize) {\n if (deleted || frame.length * 2 > PCM_ROOM_BYTES) throw new Error(\"Encode error: Bad argument\");\n native.HEAPU16.set(frame, pcm / 2);\n const length = handler._encode(pcm, frame.length, packet, frameSize);\n if (length < 0) throw new Error(`Encode error: ${length}`);\n return Buffer.from(native.HEAPU8.subarray(packet, packet + length));\n },\n decode(data) {\n if (deleted || data.length > MAX_PACKET_BYTES) throw new Error(\"Decode error: Bad argument\");\n native.HEAPU8.set(data, packet);\n const samples = handler._decode(packet, data.length, pcm);\n if (samples < 0) throw new Error(`Decode error: ${samples}`);\n return Buffer.from(native.HEAPU16.subarray(pcm / 2, pcm / 2 + Math.min(samples * 2, MAX_PCM_OUT)));\n },\n // Once: a second delete would free blocks another codec may hold by then.\n delete() {\n if (deleted) return;\n deleted = true;\n native.OpusScriptHandler.destroy_handler(handler);\n native._free(pcm);\n native._free(packet);\n },\n };\n // What browsers send for speech; the default of the build is far more.\n if (handler._encoder_ctl(OPUS_SET_BITRATE, 32_000) < 0) { codec.delete?.(); throw new Error(\"Encoder CTL error\"); }\n return codec;\n}\n\n/** The media stack, or why calls are not available here (the reason the call buttons of the contact's app show). */\nexport function loadCallStack(): Promise<CallStack | string> {\n return (loading ??= (async () => {\n if (process.env.GHOSTLY_WEBRTC === \"0\") return \"Calls are off on this headless Ghostly (GHOSTLY_WEBRTC=0)\";\n try {\n const ndc = (await import(\"node-datachannel\")) as unknown as CallStack[\"ndc\"] & { default?: CallStack[\"ndc\"] };\n const lib = (ndc.PeerConnection ? ndc : ndc.default!) as CallStack[\"ndc\"];\n // opusscript's module itself (its factory is synchronous), not its wrapper: see `opusCodec`.\n const native = (createRequire(import.meta.url)(\"opusscript/build/opusscript_native_wasm.js\") as () => OpusNative)();\n const opus = (rate: CallRate): OpusCodec => opusCodec(native, rate);\n // Checked once here: a broken wasm build would otherwise fail the first call.\n opus(48000).delete?.();\n return { ndc: lib, opus };\n } catch (error) {\n return `Calls are not available on this headless Ghostly (${error instanceof Error ? error.message : String(error)})`;\n }\n })());\n}\n\n/**\n * The ICE servers of a call, as libdatachannel takes them: the apps' STUN servers (RTC_CONFIG; libjuice uses the\n * first; none with `GHOSTLY_STUN=0`), then the profile's own (Settings `iceServers`: a TURN relay, typically), as the\n * apps' calls use them.\n */\nexport function callIceServers(extra: readonly CallIceServer[] = [], env = process.env): (string | IceServer)[] {\n const out: (string | IceServer)[] = env.GHOSTLY_STUN === \"0\" ? [] : (RTC_CONFIG.iceServers ?? []).flatMap((server) => [server.urls].flat()).slice(0, 2);\n for (const server of callRtcConfig(extra).iceServers?.slice(RTC_CONFIG.iceServers?.length ?? 0) ?? []) {\n for (const url of [server.urls].flat()) {\n const m = /^(stun|turns?):([^:?\\s]+)(?::(\\d+))?(?:\\?transport=(udp|tcp))?$/i.exec(url);\n if (!m) continue;\n const scheme = m[1].toLowerCase(), hostname = m[2], transport = m[4]?.toLowerCase();\n if (scheme === \"stun\") { out.push(`stun:${hostname}:${m[3] ?? 3478}`); continue; }\n out.push({\n hostname, port: Number(m[3] ?? (scheme === \"turns\" ? 5349 : 3478)),\n ...(server.username ? { username: server.username } : {}), ...(typeof server.credential === \"string\" ? { password: server.credential } : {}),\n relayType: scheme === \"turns\" ? \"TurnTls\" : transport === \"tcp\" ? \"TurnTcp\" : \"TurnUdp\",\n });\n }\n }\n return out;\n}\n\nexport type MediaState = \"connected\" | \"failed\" | \"closed\";\n\nexport interface MediaOptions {\n rate: CallRate;\n /** A 20 ms frame of the contact's audio. */\n onFrame(frame: Buffer): void;\n onState(state: MediaState): void;\n /** The call's queue of the program's audio. */\n queue?: PlaybackQueue;\n /** The profile's own ICE servers (Settings `iceServers`), after the apps' STUN servers. */\n iceServers?: readonly CallIceServer[];\n /** A line about the connection (candidates, ICE states, the pair it runs on), for the daemon's log. */\n log?(line: string): void;\n}\n\n/** The call's connection as `call list` reports it: the ICE state and, once connected, the pair it runs on. */\nexport interface IceView { state: string; pair: string | null }\n\n/** A candidate as `call list` and the log show it: address, port and type. */\nfunction describePair(pair: ReturnType<PeerConnection[\"getSelectedCandidatePair\"]>): string | null {\n if (!pair) return null;\n const one = (c: { address: string; port: number; type: string }) => `${c.address}:${c.port} ${c.type}`;\n return `${one(pair.local)} <-> ${one(pair.remote)}`;\n}\n\n/**\n * For tests (the e2e of a web app calling back a bot): the first this many answers the process applies are refused\n * as libdatachannel 0.24.5 refuses one in its race (see CallManager's `redial`): the connection closes, and it throws.\n */\nlet refuseAnswers = Number(process.env.GHOSTLY_CALL_REFUSE_ANSWERS) || 0;\n\n/** How long a description waits for its candidates: a server reflexive one and a little after it, or this long. */\nconst GATHER_MS = 5000;\n/** The signal keeps the first server reflexive candidate only: the apps' calls and the chats' data link settle as long. */\nconst SETTLE_MS = 150;\n/**\n * With a candidate in (a host one comes in milliseconds), the server reflexive one is waited for this long at most,\n * not the whole of `GATHER_MS`: a STUN server that answers does in tens of milliseconds, and one that never does (UDP\n * to it blocked, as on many office, school and hotel networks) made every call ring 5 s late and connect 5 s after\n * the answer. The apps' calls and the chats' data link bound theirs the same way (`HOST_GATHER_MS`,\n * `OFFER_HOST_GATHER_MS`).\n */\nexport const HOST_GATHER_MS = 2000;\n\n/** What a connection needs before the call's audio does (an early answer's: the rate is known only at the answer). */\ntype ConnectionOptions = Pick<MediaOptions, \"iceServers\" | \"log\">;\n\n/** A call's peer connection, its tracks and its description, before and once it has its audio. */\ninterface Connection {\n pc: PeerConnection;\n track: Track;\n videoTrack: Track | null;\n local: Partial<CallSignal>;\n payloadType: number;\n ssrc: number;\n log: (line: string) => void;\n ice: IceView;\n closed: boolean;\n /** libjuice gave up on it (an early answer's, after about 40 s with nothing to check). */\n failed: boolean;\n /** Who hears its states once it has its audio: none are told before. */\n onState: ((state: MediaState) => void) | null;\n}\n\n/**\n * A connection and its description, with its candidates. An early answer's (`held`) takes the contact's offer\n * without its candidates: with none, this side sends the contact no check, so nothing connects, and no audio is sent\n * or read, before the answer adds them.\n */\nasync function openConnection(stack: CallStack, options: ConnectionOptions, offer: CallSignal | null, held = false): Promise<Connection> {\n const { ndc } = stack;\n const bind = process.env.GHOSTLY_CALL_BIND;\n const iceServers = callIceServers(options.iceServers);\n const pc = new ndc.PeerConnection(\"ghostly-call\", { iceServers, ...(bind ? { bindAddress: bind } : {}) });\n // Each line says how long after this connection was made it happened: the order of the connection's states and\n // the contact's answer is what explains a call that failed as it connected. The states are heard from the start.\n const born = Date.now();\n const sink = options.log ?? (() => {});\n const log = (line: string) => sink(`+${Date.now() - born}ms ${line}`);\n let conn: Connection | null = null;\n pc.onSignalingStateChange((state) => { if (!conn?.closed) log(`signaling ${state}`); });\n pc.onIceStateChange((state) => {\n if (conn?.closed) return;\n let pair: string | null = null;\n if (state === \"connected\" || state === \"completed\") {\n try { pair = describePair(pc.getSelectedCandidatePair()); } catch { /* none yet */ }\n }\n if (conn) { conn.ice.state = state; if (state === \"connected\" || state === \"completed\") conn.ice.pair = pair; }\n log(`ice ${state}${pair ? ` over ${pair}` : \"\"}`);\n });\n pc.onStateChange((state) => {\n if (conn?.closed) return;\n log(`connection ${state}`);\n if (!conn) return;\n if (state === \"failed\") conn.failed = true;\n if (state === \"connected\") conn.onState?.(\"connected\");\n else if (state === \"failed\" || state === \"closed\") conn.onState?.(state);\n });\n try {\n const payloadType = offer?.ap ?? 111;\n const ssrc = randomBytes(4).readUInt32BE(0) || 1;\n const audioSection = new ndc.Audio(\"0\", \"SendRecv\");\n audioSection.addOpusCodec(payloadType);\n audioSection.addSSRC(ssrc, \"ghostly\", \"stream\", \"audio0\");\n const track = pc.addTrack(audioSection);\n // The contact's offer always has a video section (the apps open one for a camera or a screen later): it is\n // answered closed, and the answer says so (`vr: 0` below), so the contact's app sends no picture on it. An app\n // that does not know `vr` sends one all the same: it is dropped as it comes (libdatachannel keeps what nobody reads).\n let videoTrack: Track | null = null;\n if (offer?.m?.includes(\"v\")) {\n const video = new ndc.Video(String(offer.m.indexOf(\"v\")), \"Inactive\");\n video.addVP8Codec(offer.vp ?? 96);\n videoTrack = pc.addTrack(video);\n videoTrack.onMessage(() => {});\n }\n const gathered = gathering(pc, { turn: callUsesTurn(iceServers) });\n if (offer) pc.setRemoteDescription(buildSdpFromSignal(held ? { ...offer, c: [] } : offer), \"offer\");\n else pc.setLocalDescription(\"offer\");\n await gathered;\n const sdp = pc.localDescription()?.sdp;\n if (!sdp) throw new Error(\"No local description\");\n // Every path, as a signal on the chat session may carry (pickCallCandidates); bound to loopback (the tests,\n // GHOSTLY_CALL_BIND=127.0.0.1), that is the one candidate there is.\n const params = extractParamsFromSdp(sdp, { maxCandidates: PAIRED_CALL_CANDIDATES, loopback: !!bind });\n const local: Partial<CallSignal> = { ...params, ss: [ssrc] };\n // An answer lists the offer's sections, video too: the rebuilt SDP must have as many as the offer.\n if (offer?.m) local.m = [...offer.m];\n // No picture is taken here, and none is sent: the contact's app rebuilds the video section closed (WISP 601).\n if (videoTrack) local.vr = 0;\n conn = { pc, track, videoTrack, local, payloadType, ssrc, log, ice: { state: \"new\", pair: null }, closed: false, failed: false, onState: null };\n // What each side offered to meet on: when a call does not connect, the log says between which addresses.\n log(`candidates here: ${(local.c ?? []).join(\" | \") || \"none\"}`);\n try { conn.ice.state = pc.iceState(); } catch { /* keeps \"new\" */ }\n return conn;\n } catch (error) {\n pc.close();\n throw error;\n }\n}\n\nfunction closeConnection(conn: Connection): void {\n conn.closed = true;\n try { conn.track.close(); } catch { /* closed with the connection */ }\n try { conn.videoTrack?.close(); } catch { /* same */ }\n try { conn.pc.close(); } catch { /* already */ }\n}\n\n/**\n * The answer's connection, made while the call rings: its description and candidates are ready when the call is\n * answered, so the answer goes at once, where gathering them then took up to HOST_GATHER_MS (a STUN server that never\n * answers). The offer's candidates wait for the answer (see `openConnection`). libjuice gives up on a connection that\n * has nothing to check after about 40 s: a call that rings longer has its early answer made again (CallManager).\n */\nexport class EarlyAnswer {\n private constructor(private conn: Connection | null, readonly offer: CallSignal) {}\n\n static async open(stack: CallStack, offer: CallSignal, options: ConnectionOptions): Promise<EarlyAnswer> {\n return new EarlyAnswer(await openConnection(stack, options, offer, true), offer);\n }\n\n /** Whether it can still answer: not closed, not given up on, not used. */\n get usable(): boolean {\n return !!this.conn && !this.conn.closed && !this.conn.failed;\n }\n\n /** Its connection, once, for the answer to the offer it was made for. */\n take(offer: CallSignal): Connection | null {\n if (!this.usable || offer !== this.offer) return null;\n const conn = this.conn;\n this.conn = null;\n return conn;\n }\n\n /** Closed unused: the call ended, or a newer one replaced it. Nothing once taken. */\n close(): void {\n if (this.conn) closeConnection(this.conn);\n this.conn = null;\n }\n}\n\n/**\n * One call's peer connection. Its SDP never travels: the compact signal does (`local`), and each side rebuilds the\n * other's SDP from it (`buildSdpFromSignal`), so the payload types, SSRC and mids here are the ones that rebuilt SDP\n * uses: audio is mid 0, Opus is 111 unless the contact's offer says otherwise.\n */\nexport class CallMedia {\n readonly audio: CallAudio;\n private readonly log: (line: string) => void;\n\n private constructor(private readonly conn: Connection, audio: CallAudio) {\n this.audio = audio;\n this.log = conn.log;\n }\n\n get local(): Partial<CallSignal> {\n return this.conn.local;\n }\n\n /** An offer: audio only (the contact's app answers one section, and its video lane stays closed). */\n static async offer(stack: CallStack, options: MediaOptions): Promise<CallMedia> {\n return CallMedia.create(stack, options, null);\n }\n\n /**\n * The answer to a contact's offer, which is rebuilt into its SDP here: on the connection made while the call rang\n * when it is still good, else on a new one.\n */\n static async answer(stack: CallStack, offer: CallSignal, options: MediaOptions, early?: EarlyAnswer | null): Promise<CallMedia> {\n return CallMedia.create(stack, options, offer, early?.take(offer) ?? null);\n }\n\n private static async create(stack: CallStack, options: MediaOptions, offer: CallSignal | null, early: Connection | null = null): Promise<CallMedia> {\n const conn = early ?? await openConnection(stack, options, offer);\n const { pc, track, log } = conn;\n try {\n const listeners: ((packet: Buffer) => void)[] = [];\n track.onMessage((packet) => { for (const listener of listeners) listener(packet); });\n const audio = new CallAudio({\n rate: options.rate, payloadType: conn.payloadType, ssrc: conn.ssrc,\n track: { send: (packet) => { if (track.isOpen()) track.sendMessageBinary(packet); }, onPacket: (listener) => listeners.push(listener) },\n encoder: stack.opus(options.rate), decoder: stack.opus(options.rate), onFrame: options.onFrame, queue: options.queue,\n });\n const media = new CallMedia(conn, audio);\n conn.onState = options.onState;\n if (offer) log(`contact's candidates: ${(offer.c ?? []).join(\" | \") || \"none\"}`);\n if (offer) mdnsNote(offer, log);\n track.onOpen(() => audio.start());\n // The early answer's connection gets the offer's candidates now: its checks start, and the call can connect.\n if (early && offer) {\n log(\"answered on the connection made as the call rang: the contact's candidates go in now\");\n const mid = String(Math.max(0, (offer.m ?? [\"a\"]).indexOf(\"a\")));\n for (const candidate of offer.c ?? []) pc.addRemoteCandidate(`candidate:${candidate}`, mid);\n }\n if (track.isOpen()) audio.start();\n return media;\n } catch (error) {\n closeConnection(conn);\n throw error;\n }\n }\n\n /** The contact's answer to this side's offer. */\n applyAnswer(answer: CallSignal): void {\n this.log(`contact's candidates: ${(answer.c ?? []).join(\" | \") || \"none\"}`);\n mdnsNote(answer, this.log);\n if (refuseAnswers > 0) {\n refuseAnswers--;\n this.conn.pc.close();\n throw new Error(\"libdatachannel error while adding remote description: Got a remote candidate without ICE transport (GHOSTLY_CALL_REFUSE_ANSWERS)\");\n }\n this.conn.pc.setRemoteDescription(buildSdpFromSignal(answer), \"answer\");\n this.log(\"answer applied\");\n }\n\n get ice(): IceView {\n return this.conn.ice;\n }\n\n close(): void {\n if (this.conn.closed) return;\n this.audio.stop();\n closeConnection(this.conn);\n }\n}\n\n/**\n * WebKit (Safari, the macOS app) hides its host addresses behind mDNS names, which other machines, and often this\n * one, cannot resolve: then the call can only connect when the contact's own checks reach this side. The log says so,\n * since that is what a call stuck on \"connecting\" needs to be understood.\n */\nfunction mdnsNote(signal: CallSignal, log: (line: string) => void): void {\n for (const candidate of signal.c ?? []) {\n const host = candidate.split(\" \")[4];\n if (!host?.endsWith(\".local\")) continue;\n void lookup(host, { all: true }).then(\n (found) => log(`${host} resolves here to ${found.map((a) => a.address).join(\", \")}`),\n () => log(`${host} does not resolve here: the call connects only if the contact's checks reach this side`),\n );\n }\n}\n\n/** Whether a call's ICE servers (`callIceServers`) have a TURN server: its relay candidate is then the one waited for. */\nexport function callUsesTurn(servers: readonly (string | IceServer)[]): boolean {\n return servers.some((server) => typeof server !== \"string\");\n}\n\n/**\n * Resolves once the local description has its candidates (see GATHER_MS and HOST_GATHER_MS). `turn`: the connection\n * has a TURN server, and its relay candidate is waited for rather than the reflexive one, as the apps' calls do\n * (`waitForIceGathering`): an allocation takes two round trips, and a TURN server 100 ms away gave its relay after the\n * description had gone without it, on the networks where only the relay connects.\n */\nexport function gathering(pc: Pick<PeerConnection, \"onGatheringStateChange\" | \"onLocalCandidate\">, { turn = false }: { turn?: boolean } = {}): Promise<void> {\n const wanted = turn ? / typ relay/ : / typ srflx/;\n return new Promise((resolve) => {\n let settle: ReturnType<typeof setTimeout> | null = null;\n let any = false;\n const done = () => { clearTimeout(cap); clearTimeout(bounded); if (settle) clearTimeout(settle); resolve(); };\n const cap = setTimeout(done, GATHER_MS);\n const bounded = setTimeout(() => { if (any) done(); }, HOST_GATHER_MS);\n pc.onGatheringStateChange((state) => { if (state === \"complete\") done(); });\n pc.onLocalCandidate((candidate) => {\n any = true;\n if (!settle && wanted.test(candidate)) settle = setTimeout(done, SETTLE_MS);\n });\n });\n}\n","import { randomBytes } from \"node:crypto\";\nimport { existsSync, readFileSync, renameSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { answersOffer, callSignalHeardAt, parseCallSignal, signalHasVideo, CALL_SIGNAL_MAX_AGE_MS, type CallSignal } from \"@ghostly/core\";\nimport type { EngineState, LinkView } from \"@ghostly/browser/shared/types\";\nimport { findChat } from \"../apiKit\";\nimport { CliError } from \"../errors\";\nimport { AudioSocket, audioSocketPath, clearAudioSockets } from \"./audioSocket\";\nimport { CallMedia, EarlyAnswer, loadCallStack, type CallStack, type MediaOptions } from \"./media\";\nimport { DEFAULT_RATE, FRAME_MS, isCallRate, PlaybackQueue, type CallRate } from \"./pcm\";\n\n/**\n * Voice calls on the headless Ghostly (WISP 1100 § Calls): the calls/1 signals of the chat session (WISP 601), the\n * media on a WebRTC connection of the CLI's own, and the audio handed to an outside program over a Unix socket per\n * call. The rules are the apps' (packages/react/src/useWebRTC.ts): a call per chat; an offer rings while this side\n * is idle, or when it wins a glare with this side's own offer; an answer counts only after this side's offer; a hang-up ends whatever is on; a signal older than the\n * last one handled is ignored; after a hang-up the signal is cleared 5 s later.\n */\n\nexport type CallState = \"ringing\" | \"connecting\" | \"connected\";\nexport type EndReason = \"hangup\" | \"remote-hangup\" | \"missed\" | \"rejected\" | \"unanswered\" | \"crossed\" | \"failed\" | \"stopped\";\n\n/** What the engine gives the calls: its state, and a way to send this side's signal. */\nexport interface CallEngine {\n getState(): EngineState;\n setCallSignal(params: { linkId: string; signal: string | null }): Promise<void> | void;\n}\n\nexport interface CallHost {\n engine: CallEngine;\n emit(type: string, id: string, fields: Record<string, unknown>): void;\n profileDir: string;\n /** Whether this host answers and places calls: a daemon does; a one-shot command leaves, so it only reports. */\n answers?: boolean;\n /** The media stack (tests give their own). */\n stack?: () => Promise<CallStack | string>;\n now?: () => number;\n /**\n * How many times an outgoing call offers again (MAX_REDIALS): tests that refuse some answers themselves raise it by\n * theirs, so the real race's own refusals, which can come on top, still find the app's budget whole.\n */\n maxRedials?: number;\n /** STALL_MS and STALL_END_MS (tests shorten them). */\n stall?: { afterMs: number; endMs: number };\n /** EARLY_ANSWER_MS (tests shorten it). */\n earlyAnswerMs?: number;\n /** CALL_PACE_MS (0: no pace; a test that redials at once on a clock that stands still). */\n callPaceMs?: number;\n}\n\n/** The audio contract of one call, as commands and events report it. */\nexport interface AudioInfo { socket: string; rate: CallRate; channels: 1; format: \"s16le\"; frameMs: typeof FRAME_MS }\n\n/** An outgoing call rings this long, then ends as `unanswered`. */\nexport const RING_MS = 60_000;\n/** An answered or accepted call has this long to connect its media, then ends as `failed`. */\nexport const CONNECT_MS = 30_000;\n/** How long a call whose media closed or failed waits for the contact's hang-up signal to say why. */\nexport const MEDIA_GRACE_MS = 3_000;\n/** After a hang-up, the signal is cleared this much later (the apps' own delay). */\nexport const CLEAR_MS = 5_000;\n/** How many times an outgoing call offers again after an answer was refused (`redial`), and the wait before each. */\nexport const MAX_REDIALS = 4;\nexport const REDIAL_BACKOFF_MS = [150, 300, 600, 600] as const;\n/** A connected call that hears no packet from the contact this long says so (`call.stalled`). */\nexport const STALL_MS = 5_000;\n/** A connected call that hears no packet from the contact this long ends as `failed`. */\nexport const STALL_END_MS = 15_000;\n/** A connection that fails this soon after its answer went in lost the race `redial` describes, and starts over. */\nexport const RACE_FAIL_MS = 2_000;\n/**\n * A ringing call's answer is made ahead (`prepare`), and made again this often while it rings: libjuice gives up on a\n * connection that has nothing to check (the offer's candidates wait for the answer) after about 40 s.\n */\nexport const EARLY_ANSWER_MS = 30_000;\n/**\n * A call this side placed that ended before it was answered (hung up while it rang, or rang out) is followed by the\n * next one in the same chat no sooner than this: the contact's app keeps a call that comes this soon after a missed one\n * that barely rang as missed and does not ring it (packages/react `QUICK_REDIAL_MS`, 2 s), so a loop would only add lines. A bot looping\n * `call.start` / `call.hangup` placed 1,375 calls a minute (agents#974). `call.start` refuses sooner with `busy` and the\n * wait left (`retryAfterMs`); a person placing calls by hand never meets it.\n */\nexport const CALL_PACE_MS = 2_000;\n\n/**\n * At most this many early answers are being made or held at once, and one being made per chat: a call that ends as\n * its early answer is made closes it only once made (up to HOST_GATHER_MS later). Past either, the call rings without\n * one, and its answer makes its own connection (or it gets one once the chat's is made).\n */\nexport const MAX_EARLY_ANSWERS = 4;\n\ninterface Call {\n id: string;\n chat: string;\n direction: \"in\" | \"out\";\n state: CallState;\n rate: CallRate;\n video: boolean;\n offer: CallSignal | null;\n /** This side's offer's timestamp: only a later answer counts. */\n offerTs: number;\n /** This side's offer waits for its answer. */\n offering: boolean;\n /** How many times this side offered again on a new connection after an answer was refused (`redial`). */\n redials: number;\n /** Counts the connections made for the call: one that comes back after a newer one was asked for is closed. */\n attempt: number;\n /** The attempt whose connection has the contact's answer (0: none yet), and when it went in (Date.now()). */\n answered: number;\n answeredAt: number;\n media: CallMedia | null;\n /** While an incoming call rings: its answer's connection, made ahead (`prepare`), the one being made, and the timer that makes it again. */\n early: EarlyAnswer | null;\n making: Making | null;\n earlyTimer: ReturnType<typeof setTimeout> | null;\n socket: AudioSocket | null;\n /** What the program wrote and the call has not sent yet: kept from the moment the socket opens. */\n queue: PlaybackQueue | null;\n startedAt: number;\n connectedAt: number | null;\n timer: ReturnType<typeof setTimeout> | null;\n /** The connected call's watch on the contact's packets (`watch`). */\n watcher: ReturnType<typeof setInterval> | null;\n /** Since when nothing came from the contact, while the call says it stalled (null: it does not); how many times it did. */\n stalledFrom: number | null;\n stalls: number;\n ended: boolean;\n /** Why it ended, once it has. */\n reason?: EndReason;\n}\n\n/** An early answer being made; `claimed` once the call's answer waits for it, which then takes it itself. */\ninterface Making { chat: string; done: Promise<EarlyAnswer | null>; claimed: boolean }\n\n/** Auto-answer, kept in the profile's `calls.json`: every chat, or the listed ones, at a rate. */\nexport interface AutoAnswer { on: boolean; from: string[]; rate: CallRate }\n\nexport class CallManager {\n private readonly calls = new Map<string, Call>();\n /** Per chat: the timestamp of the last signal handled. */\n private readonly lastSignal = new Map<string, number>();\n /** Per chat: the timestamp of the last signal sent (`stamp`). */\n private readonly lastSent = new Map<string, number>();\n /** Per chat: the timer that clears the hang-up signal. */\n private readonly clearing = new Map<string, ReturnType<typeof setTimeout>>();\n /** Per chat: when this side's last call there ended before it was answered (`CALL_PACE_MS`). */\n private readonly unansweredAt = new Map<string, number>();\n /** The early answers being made, those of ended calls too (MAX_EARLY_ANSWERS). */\n private readonly makings = new Set<Making>();\n private auto: AutoAnswer;\n private readonly configPath: string;\n private stopping = false;\n\n constructor(private readonly host: CallHost) {\n this.configPath = join(host.profileDir, \"calls.json\");\n this.auto = readAuto(this.configPath);\n // A daemon starts with no call: a socket left in its folder is one a daemon that crashed during a call never closed.\n if (host.answers !== false) clearAudioSockets(host.profileDir);\n }\n\n private get now(): number { return (this.host.now ?? Date.now)(); }\n\n /**\n * A new signal's timestamp in a chat: now, or 1 ms after the last one sent there. The contact drops a signal no\n * newer than the last it heard, so a redial or a hang-up in the same millisecond as the signal before it would be lost.\n */\n private stamp(chat: string): number {\n const ts = Math.max(this.now, (this.lastSent.get(chat) ?? 0) + 1);\n this.lastSent.set(chat, ts);\n return ts;\n }\n\n // ---------- signals from the contact ----------\n\n /** A signal the contact's app sent in a chat (the engine's `call-signal`). */\n onSignal(chat: string, json: string): void {\n // A stopping daemon takes no call, and its own have been hung up (`stopAll`): nothing here is heard any more.\n if (this.stopping) return;\n const signal = parseCallSignal(json, this.now);\n if (!signal) return;\n if (signal.ts <= (this.lastSignal.get(chat) ?? 0)) return;\n const call = this.byChat(chat);\n // Reconnecting (WISP 601): this side never says it restarts ICE (`x`), so an app sends it no restart offer (`r`),\n // and one that comes all the same is dropped here, on a call or not. libdatachannel (0.24.5) cannot restart ICE\n // on a connection that has started: it refuses a remote offer with new ICE credentials (\"Invalid ICE settings\n // from remote SDP\") and cannot make one. A call whose path is lost ends as before (`MEDIA_GRACE_MS`).\n if (signal.t === \"r\" || (signal.t === \"a\" && signal.re !== undefined)) return;\n // A connected call only hears a hang-up (and picture changes, which are no business of an audio-only side).\n if (call?.state === \"connected\" && signal.t !== \"h\" && signal.t !== \"v\") return;\n if (signal.t === \"o\" && !call) {\n this.lastSignal.set(chat, signal.ts);\n this.incoming(chat, signal);\n } else if (signal.t === \"a\" && call?.direction === \"out\" && call.offering && answersOffer(signal, call.offerTs)) {\n this.lastSignal.set(chat, signal.ts);\n this.accepted(call, signal);\n } else if (signal.t === \"o\" && call?.direction === \"out\" && call.state === \"ringing\") {\n // Both called at once (WISP 601, \"Both call at once\"): the earlier offer wins on both sides, at the same\n // millisecond the lower DTLS fingerprint; ours still being made counts as later. The loser rings.\n this.lastSignal.set(chat, signal.ts);\n const mine = call.offering ? call.offerTs : 0;\n const theirsFirst = !mine || signal.ts < mine || (signal.ts === mine && (signal.f ?? \"\") < (call.media?.local.f ?? \"\"));\n if (theirsFirst) void this.yieldTo(call, signal);\n } else if (signal.t === \"o\" && call?.direction === \"in\" && call.state === \"connecting\" && call.media && signal.ts > (call.offer?.ts ?? 0)) {\n // The contact's side offered again on a new connection (see `redial`): the answered call starts over on one.\n this.lastSignal.set(chat, signal.ts);\n void this.reanswer(call, signal);\n } else if (signal.t === \"v\") {\n this.lastSignal.set(chat, signal.ts);\n } else if (signal.t === \"h\") {\n this.lastSignal.set(chat, signal.ts);\n if (call) {\n // A hang-up while this side starts over (`redial`) is the contact's app ending a connection that failed. Once\n // a connection came up, the call was on: a start-over before it does not make the hang-up a failure.\n // A hang-up that says the contact's side could not connect (`r: \"u\"`, WISP 601): the call failed, not declined.\n const unreachable = signal.r === \"u\" && call.state !== \"connected\";\n const failed = unreachable || (call.state !== \"connected\" && (call.offering || (call.direction === \"out\" && call.redials > 0)));\n const reason: EndReason = call.state === \"ringing\" && call.direction === \"in\" ? \"missed\"\n : call.state === \"ringing\" && !unreachable ? \"rejected\" : failed ? \"failed\" : \"remote-hangup\";\n void this.end(call, reason, false);\n }\n }\n }\n\n /**\n * The contact's offer won a glare: our call ends as `crossed`, sending nothing (a hang-up would end the contact's\n * call, and clearing our signal could drop an answer we are about to give), and the contact's call rings here.\n */\n private async yieldTo(call: Call, offer: CallSignal): Promise<void> {\n await this.end(call, \"crossed\", false);\n // Something newer (the contact's hang-up) came while ours ended, or another call started: nothing rings.\n if (this.lastSignal.get(call.chat) !== offer.ts || this.byChat(call.chat) || this.stopping) return;\n this.incoming(call.chat, offer);\n }\n\n private incoming(chat: string, offer: CallSignal): void {\n const link = this.link(chat);\n const call = this.add(chat, \"in\", this.auto.rate);\n call.offer = offer;\n call.video = signalHasVideo(offer);\n // Unanswered, an offer goes stale: the contact's app has given up or will, and answering it would be refused.\n this.arm(call, Math.max(1000, callSignalHeardAt(offer) + CALL_SIGNAL_MAX_AGE_MS - this.now), \"missed\", false);\n this.host.emit(\"call.incoming\", `call.incoming:${call.id}`, { call: call.id, chat, name: link ? nameOf(link) : null, video: call.video, auto: this.autoFor(chat) });\n this.prepare(call);\n if (this.host.answers !== false && this.autoFor(chat)) {\n void this.answer(call.id, {}).catch((error) => process.stderr.write(`ghostly: auto-answer failed: ${error instanceof Error ? error.message : String(error)}\\n`));\n }\n }\n\n private accepted(call: Call, answer: CallSignal): void {\n call.offering = false;\n call.state = \"connecting\";\n const attempt = (call.answered = call.attempt);\n call.answeredAt = Date.now();\n try {\n call.media!.applyAnswer(answer);\n } catch (error) {\n // The connection may have failed first, while the answer was added: then it already started over.\n if (attempt === call.attempt) this.refused(call, `the answer was refused (${error instanceof Error ? error.message : String(error)})`);\n return;\n }\n if (attempt === call.attempt) this.arm(call, CONNECT_MS, \"failed\", true);\n }\n\n /**\n * The contact's answer on this connection was refused, or the connection failed right after it (the same race,\n * whose handshake can also fail once the answer is in): offering again (`redial`) while there are tries left.\n */\n private refused(call: Call, what: string): void {\n if (call.redials < this.maxRedials) {\n process.stderr.write(`ghostly: call ${call.id}: ${what}: offering again on a new connection\\n`);\n void this.redial(call);\n return;\n }\n process.stderr.write(`ghostly: call ${call.id}: ${what}\\n`);\n void this.end(call, \"failed\", true);\n }\n\n private get maxRedials(): number { return this.host.maxRedials ?? MAX_REDIALS; }\n\n /**\n * A new offer on a new connection, up to MAX_REDIALS times, each after a short wait. libdatachannel (0.24.5) can\n * refuse a good answer: when the contact's checks and its DTLS hello came before the answer (they do, the answer\n * crosses the chat session), ICE connects and the handshake ends inside setRemoteDescription, before the answer's\n * fingerprint is recorded; the fingerprint check fails, the connection closes its transports, and adding the\n * answer's candidates throws \"Got a remote candidate without ICE transport\". That connection is done for, and the\n * contact's side saw the handshake fail too, so both start over: the contact's side answers the newer offer\n * (`reanswer`). Each new connection runs the same race (CI runners lose about one in six, Linux arm64 twice in a\n * row), hence several. The race can also let the answer in and fail the handshake a moment later: a connection\n * that fails after its answer, before it ever connected, starts over the same way. The contact's side ends a call whose connection failed after MEDIA_GRACE_MS: every wait stays\n * well under it. Fixed upstream in libdatachannel 0235225a, which no release has yet.\n */\n private async redial(call: Call): Promise<void> {\n const wait = REDIAL_BACKOFF_MS[Math.min(call.redials, REDIAL_BACKOFF_MS.length - 1)];\n call.redials++;\n // The refused connection is dropped now: its closing must not arm its own timer over the one below while we wait.\n call.attempt++;\n call.media?.close();\n call.media = null;\n this.arm(call, CONNECT_MS, \"failed\", true);\n await new Promise((resolve) => setTimeout(resolve, wait));\n if (call.ended) return;\n try {\n const stack = await this.stack();\n if (!(await this.connect(call, (media) => CallMedia.offer(stack, media)))) return;\n call.offerTs = this.stamp(call.chat);\n call.offering = true;\n await this.send(call.chat, { t: \"o\", ts: call.offerTs, ...call.media!.local, v: 0 });\n } catch (error) {\n process.stderr.write(`ghostly: call ${call.id}: offering again failed (${error instanceof Error ? error.message : String(error)})\\n`);\n void this.end(call, \"failed\", true);\n }\n }\n\n /** The contact's newer offer (its `redial`): answered on a new connection, on the call's socket. */\n private async reanswer(call: Call, offer: CallSignal): Promise<void> {\n process.stderr.write(`ghostly: call ${call.id}: the contact offered again: answering on a new connection\\n`);\n call.offer = offer;\n call.video = signalHasVideo(offer);\n this.arm(call, CONNECT_MS, \"failed\", true);\n try {\n const stack = await this.stack();\n if (!(await this.connect(call, (media) => CallMedia.answer(stack, offer, media)))) return;\n await this.send(call.chat, { t: \"a\", ts: this.stamp(call.chat), o: offer.ts, ...call.media!.local, v: 0 });\n } catch (error) {\n process.stderr.write(`ghostly: call ${call.id}: answering again failed (${error instanceof Error ? error.message : String(error)})\\n`);\n void this.end(call, \"failed\", true);\n }\n }\n\n /**\n * The ringing call's answer, made now (`EarlyAnswer`): when the call is answered, its candidates have been gathered\n * and the answer goes at once. Made again every EARLY_ANSWER_MS while it rings; when it cannot be made, the answer makes its own.\n */\n private prepare(call: Call): void {\n if (this.host.answers === false || call.ended || call.state !== \"ringing\") return;\n this.stopEarly(call);\n call.earlyTimer = setTimeout(() => this.prepare(call), this.host.earlyAnswerMs ?? EARLY_ANSWER_MS);\n call.earlyTimer.unref?.();\n if (call.making) return;\n // Bounded (MAX_EARLY_ANSWERS): a contact that rings and hangs up over and over makes one at a time.\n const held = [...this.calls.values()].filter((c) => c !== call && c.early).length;\n if (this.makings.size + held >= MAX_EARLY_ANSWERS || [...this.makings].some((m) => m.chat === call.chat)) return;\n const offer = call.offer!;\n const making: Making = {\n chat: call.chat,\n done: this.stack().then((stack) => EarlyAnswer.open(stack, offer, {\n iceServers: this.host.engine.getState().settings?.iceServers ?? [],\n log: (line) => process.stderr.write(`ghostly: call ${call.id}: ${line}\\n`),\n })).catch((error) => {\n // Not tried again while it rings: the answer makes its own connection, as it did before.\n this.stopEarly(call);\n process.stderr.write(`ghostly: call ${call.id}: the answer could not be made ahead (${error instanceof Error ? error.message : String(error)})\\n`);\n return null;\n }),\n claimed: false,\n };\n call.making = making;\n this.makings.add(making);\n void making.done.then((early) => {\n this.makings.delete(making);\n if (call.making === making) call.making = null;\n if (!making.claimed && early) {\n // The call ended, or was answered on the one made before, while this one was made.\n if (call.ended || call.state !== \"ringing\") early.close();\n else { call.early?.close(); call.early = early; }\n }\n // A newer call in this chat rang without one while this was made: its own is made now.\n const next = this.byChat(making.chat);\n if (next && next !== call && next.direction === \"in\" && !next.making && !next.early) this.prepare(next);\n });\n }\n\n /** The answered call's early answer: the one made, if still good, or the one being made; none from then on. */\n private async takeEarly(call: Call): Promise<EarlyAnswer | null> {\n this.stopEarly(call);\n const early = call.early;\n call.early = null;\n if (early?.usable) return early;\n early?.close();\n const making = call.making;\n if (!making) return null;\n making.claimed = true;\n return making.done;\n }\n\n /** The early answer is not made again. */\n private stopEarly(call: Call): void {\n if (call.earlyTimer) clearTimeout(call.earlyTimer);\n call.earlyTimer = null;\n }\n\n // ---------- commands ----------\n\n async start(chat: string, options: { rate?: unknown }): Promise<Record<string, unknown>> {\n const link = this.callable(chat);\n if (this.byChat(link.id)) throw new CliError(\"busy\", \"A call is already on in this chat\");\n this.paced(link.id);\n const rate = rateOf(options.rate, this.auto.rate);\n const stack = await this.stack();\n // The contact's call may have come in while the stack loaded.\n if (this.byChat(link.id)) throw new CliError(\"busy\", \"A call is already on in this chat\");\n const call = this.add(link.id, \"out\", rate);\n try {\n await this.attach(call, (media) => CallMedia.offer(stack, media));\n if (call.reason === \"crossed\") throw new CliError(\"busy\", \"The contact called at the same time: their call rings here\");\n if (call.ended) throw new CliError(\"unavailable\", \"The call ended before it was placed\");\n call.offerTs = this.stamp(link.id);\n call.offering = true;\n await this.send(link.id, { t: \"o\", ts: call.offerTs, ...call.media!.local, v: 0 });\n } catch (error) {\n await this.end(call, \"failed\", false, false);\n throw error;\n }\n this.arm(call, RING_MS, \"unanswered\", true);\n this.host.emit(\"call.outgoing\", `call.outgoing:${call.id}`, { call: call.id, chat: link.id, name: nameOf(link), audio: this.audioInfo(call) });\n return this.view(call);\n }\n\n async answer(ref: string | undefined, options: { rate?: unknown }): Promise<Record<string, unknown>> {\n const call = this.find(ref, (c) => c.direction === \"in\" && c.state === \"ringing\");\n if (call.direction !== \"in\" || call.state !== \"ringing\") throw new CliError(\"refused\", \"That call is not ringing here\");\n this.callable(call.chat);\n call.rate = rateOf(options.rate, call.rate);\n const stack = await this.stack();\n // The call may have ended (the contact hung up, the offer went stale) or been answered by another command while\n // the stack loaded: an ended call opens nothing, and a call is answered once.\n if (call.ended) throw new CliError(\"unavailable\", \"The call ended before it was answered\");\n if (call.state !== \"ringing\") throw new CliError(\"refused\", \"That call is not ringing here\");\n // Answered: no longer missed if it goes stale; the media has its own time to connect.\n call.state = \"connecting\";\n this.disarm(call);\n let early: EarlyAnswer | null = null;\n try {\n early = await this.takeEarly(call);\n await this.attach(call, (media) => CallMedia.answer(stack, call.offer!, media, early));\n if (call.ended) throw new CliError(\"unavailable\", \"The call ended before it was answered\");\n await this.send(call.chat, { t: \"a\", ts: this.stamp(call.chat), o: call.offer!.ts, ...call.media!.local, v: 0 });\n } catch (error) {\n await this.end(call, \"failed\", true, false);\n throw error;\n } finally {\n // Unused (the call ended first): closed. Nothing once its connection is the call's.\n early?.close();\n }\n this.arm(call, CONNECT_MS, \"failed\", true);\n return this.view(call);\n }\n\n async hangup(ref: string | undefined): Promise<Record<string, unknown>> {\n const call = this.find(ref);\n const view = this.view(call);\n await this.end(call, \"hangup\", true);\n return { ...view, state: \"ended\", reason: \"hangup\" };\n }\n\n /** One call, by its id or its chat (or the only one). */\n get(ref: string | undefined): Record<string, unknown> {\n return this.view(this.find(ref));\n }\n\n list(): Record<string, unknown>[] {\n return [...this.calls.values()].map((call) => this.view(call));\n }\n\n /** Drops the audio the program queued and the call has not sent yet (barge-in). */\n flush(ref: string | undefined): Record<string, unknown> {\n const call = this.find(ref);\n const flushedMs = call.queue?.flush() ?? 0;\n return { call: call.id, chat: call.chat, flushedMs };\n }\n\n getAuto(): AutoAnswer { return { ...this.auto, from: [...this.auto.from] }; }\n\n setAuto(next: { on: boolean; from?: string[]; rate?: unknown }): AutoAnswer {\n const from = (next.from ?? []).map((ref) => this.chatId(ref));\n this.auto = { on: next.on, from: next.on ? from : [], rate: rateOf(next.rate, next.on ? this.auto.rate : DEFAULT_RATE) };\n const tmp = `${this.configPath}.tmp`;\n writeFileSync(tmp, JSON.stringify({ autoAnswer: this.auto }, null, 2) + \"\\n\", { mode: 0o600 });\n renameSync(tmp, this.configPath);\n return this.getAuto();\n }\n\n /** Before the engine stops: every call is hung up (the contact's app is told), its program gets EOF. */\n async stopAll(): Promise<void> {\n this.stopping = true;\n await Promise.all([...this.calls.values()].map((call) => this.end(call, \"stopped\", true)));\n for (const timer of this.clearing.values()) clearTimeout(timer);\n this.clearing.clear();\n }\n\n // ---------- inside ----------\n\n private add(chat: string, direction: \"in\" | \"out\", rate: CallRate): Call {\n if (this.stopping) throw new CliError(\"unavailable\", \"The daemon is stopping\");\n // A new call in this chat: the last one's hang-up must not be cleared over its signal.\n const clearing = this.clearing.get(chat);\n if (clearing) { clearTimeout(clearing); this.clearing.delete(chat); }\n const call: Call = {\n id: randomBytes(6).toString(\"hex\"), chat, direction, state: \"ringing\", rate, video: false, offer: null, offerTs: 0,\n offering: false, redials: 0, attempt: 0, answered: 0, answeredAt: 0, media: null, early: null, making: null, earlyTimer: null, socket: null, queue: null, startedAt: this.now, connectedAt: null, timer: null,\n watcher: null, stalledFrom: null, stalls: 0, ended: false,\n };\n this.calls.set(call.id, call);\n return call;\n }\n\n /**\n * The call's socket (open from now on: a program may connect before the media is up) and its media. A call that\n * ended while its socket opened closes it here: `end` found none to close, and nothing lists the call any more.\n */\n private async attach(call: Call, create: (options: MediaOptions) => Promise<CallMedia>): Promise<void> {\n const queue = (call.queue = new PlaybackQueue(call.rate));\n const socket = await AudioSocket.open(audioSocketPath(this.host.profileDir, call.id), call.rate, {\n onAudio: (chunk) => queue.push(chunk),\n });\n if (call.ended) { await socket.close(); return; }\n call.socket = socket;\n await this.connect(call, create);\n }\n\n /**\n * A connection for the call, in place of the one it had (closed first); the socket and its queue stay. False when\n * the call ended, or asked for a newer connection, while this one was made: then it is closed.\n */\n private async connect(call: Call, create: (options: MediaOptions) => Promise<CallMedia>): Promise<boolean> {\n // No connection is made for a call that is over (it ended while the stack loaded or its socket opened).\n if (call.ended) return false;\n const attempt = ++call.attempt;\n call.media?.close();\n call.media = null;\n let failed = false;\n const media = await create({\n rate: call.rate,\n queue: call.queue ?? undefined,\n log: (line) => process.stderr.write(`ghostly: call ${call.id}: ${line}\\n`),\n iceServers: this.host.engine.getState().settings?.iceServers ?? [],\n onFrame: (frame) => call.socket?.write(frame),\n onState: (state) => {\n if (call.ended || attempt !== call.attempt) return;\n if (state === \"connected\") { this.connected(call); return; }\n // A connection fails, then closes: it still failed.\n if (state === \"closed\" && failed) return;\n failed ||= state === \"failed\";\n // The answer went in and the connection failed at once, before it ever connected: the race's other ending (a\n // contact out of reach fails later, and ends the call as before).\n if (state === \"failed\" && call.direction === \"out\" && call.answered === attempt && call.state !== \"connected\"\n && Date.now() - call.answeredAt < RACE_FAIL_MS) {\n this.refused(call, \"the connection failed as the answer went in\");\n return;\n }\n // The contact's app closes its connection as it hangs up, and that is often here before its hang-up signal\n // (which crosses the chat session): the signal gets a moment to say so. Without one, a connection the\n // contact closed was still a hang-up; one that failed ends the call as it does in the apps, with nothing to say.\n this.arm(call, MEDIA_GRACE_MS, state === \"closed\" ? \"remote-hangup\" : \"failed\", false);\n },\n });\n if (call.ended || attempt !== call.attempt) {\n media.close();\n return false;\n }\n call.media = media;\n return true;\n }\n\n private connected(call: Call): void {\n if (call.state === \"connected\") return;\n call.state = \"connected\";\n call.connectedAt = this.now;\n this.disarm(call);\n this.host.emit(\"call.connected\", `call.connected:${call.id}`, { call: call.id, chat: call.chat, direction: call.direction, audio: this.audioInfo(call) });\n this.watch(call);\n }\n\n /**\n * libjuice says nothing of a contact whose side went away (a crash, a closed lid, a lost network) until its consent\n * checks fail, about 30 s later: a connected call watches the contact's packets instead. An app sends them all along\n * (silence while muted, and RTCP), and so does the CLI. Nothing for STALL_MS: `call.stalled`; packets again:\n * `call.resumed`; nothing for STALL_END_MS: the call ends as `failed`, and the contact's app is told.\n */\n private watch(call: Call): void {\n const { afterMs, endMs } = this.host.stall ?? { afterMs: STALL_MS, endMs: STALL_END_MS };\n const since = Date.now();\n call.watcher = setInterval(() => {\n if (call.ended) return;\n const heardAt = Math.max(since, call.media?.audio.heardAt ?? 0);\n const silentMs = Date.now() - heardAt;\n const fields = { call: call.id, chat: call.chat, direction: call.direction };\n if (silentMs >= endMs) {\n process.stderr.write(`ghostly: call ${call.id}: nothing heard from the contact for ${Math.round(silentMs / 1000)} s\\n`);\n void this.end(call, \"failed\", true);\n } else if (silentMs >= afterMs && call.stalledFrom === null) {\n call.stalledFrom = heardAt;\n call.stalls++;\n process.stderr.write(`ghostly: call ${call.id}: stalled, nothing heard from the contact for ${silentMs} ms\\n`);\n this.host.emit(\"call.stalled\", `call.stalled:${call.id}:${call.stalls}`, { ...fields, silentMs });\n } else if (silentMs < afterMs && call.stalledFrom !== null) {\n const gapMs = heardAt - call.stalledFrom;\n call.stalledFrom = null;\n process.stderr.write(`ghostly: call ${call.id}: resumed after ${gapMs} ms\\n`);\n this.host.emit(\"call.resumed\", `call.resumed:${call.id}:${call.stalls}`, { ...fields, silentMs: gapMs });\n }\n }, Math.min(1000, afterMs / 5));\n call.watcher.unref?.();\n }\n\n private async end(call: Call, reason: EndReason, tell: boolean, report = true): Promise<void> {\n if (call.ended) return;\n call.ended = true;\n call.reason = reason;\n this.disarm(call);\n if (call.watcher) clearInterval(call.watcher);\n this.calls.delete(call.id);\n // A call placed here that the contact never answered: the next one waits a moment (`CALL_PACE_MS`).\n if (call.direction === \"out\" && call.connectedAt === null && call.state === \"ringing\" && (reason === \"hangup\" || reason === \"unanswered\")) this.unansweredAt.set(call.chat, this.now);\n if (report) process.stderr.write(`ghostly: call ${call.id}: ended (${reason})${call.media ? `, ice ${call.media.ice.state}` : \"\"}\\n`);\n // Whatever the media's close throws, the rest of the end follows: the contact is told, the program reads EOF.\n try { call.media?.close(); } catch (error) { process.stderr.write(`ghostly: call ${call.id}: closing its media failed: ${error instanceof Error ? error.message : String(error)}\\n`); }\n // A call that ends ringing (declined, missed, stale) closes the answer it made ahead; one being made, once made.\n this.stopEarly(call);\n call.early?.close();\n call.early = null;\n await call.socket?.close();\n if (tell) {\n await this.send(call.chat, { t: \"h\", ts: this.stamp(call.chat) }).catch(() => {});\n if (!this.stopping) {\n const timer = setTimeout(() => {\n this.clearing.delete(call.chat);\n void this.sendRaw(call.chat, null).catch(() => {});\n }, CLEAR_MS);\n timer.unref?.();\n this.clearing.set(call.chat, timer);\n }\n } else if (reason !== \"crossed\") {\n // Nothing left to say: a signal still waiting for the next session is dropped.\n await this.sendRaw(call.chat, null).catch(() => {});\n }\n if (report) {\n this.host.emit(\"call.ended\", `call.ended:${call.id}`, {\n call: call.id, chat: call.chat, direction: call.direction, reason,\n ...(call.connectedAt !== null ? { duration: this.now - call.connectedAt } : {}),\n });\n }\n }\n\n /** Refuses a call placed too soon after this side's last unanswered one in the chat (`CALL_PACE_MS`). */\n private paced(chat: string): void {\n const pace = this.host.callPaceMs ?? CALL_PACE_MS, last = this.unansweredAt.get(chat);\n if (!pace || last === undefined) return;\n const left = last + pace - this.now;\n if (left <= 0) { this.unansweredAt.delete(chat); return; }\n throw new CliError(\"busy\", `Your last call in this chat ended unanswered a moment ago: call again in ${Math.ceil(left / 100) / 10} s`, { retryAfterMs: left });\n }\n\n private arm(call: Call, ms: number, reason: EndReason, tell: boolean): void {\n this.disarm(call);\n call.timer = setTimeout(() => void this.end(call, reason, tell), ms);\n call.timer.unref?.();\n }\n\n private disarm(call: Call): void {\n if (call.timer) clearTimeout(call.timer);\n call.timer = null;\n }\n\n private send(chat: string, signal: CallSignal): Promise<void> {\n return this.sendRaw(chat, JSON.stringify(signal));\n }\n\n private async sendRaw(chat: string, signal: string | null): Promise<void> {\n try {\n await this.host.engine.setCallSignal({ linkId: chat, signal });\n } catch (error) {\n throw new CliError(\"unavailable\", error instanceof Error ? error.message : String(error));\n }\n }\n\n private async stack(): Promise<CallStack> {\n if (this.host.answers === false) throw new CliError(\"unavailable\", \"Calls need the daemon (a call outlives this command): start it with `ghostly daemon --detach`\");\n const stack = await (this.host.stack ?? loadCallStack)();\n if (typeof stack === \"string\") throw new CliError(\"unavailable\", stack);\n return stack;\n }\n\n private audioInfo(call: Call): AudioInfo | null {\n return call.socket ? { socket: call.socket.path, rate: call.rate, channels: 1, format: \"s16le\", frameMs: FRAME_MS } : null;\n }\n\n private view(call: Call): Record<string, unknown> {\n const audio = call.media?.audio;\n return {\n call: call.id, chat: call.chat, direction: call.direction, state: call.state, stalled: call.stalledFrom !== null, video: call.video,\n startedAt: call.startedAt, connectedAt: call.connectedAt,\n audio: this.audioInfo(call),\n ...(call.socket ? { stats: { framesIn: audio?.received ?? 0, framesOut: audio?.sent ?? 0, queuedMs: call.queue?.queuedMs ?? 0, programConnected: call.socket.connected, ice: call.media?.ice ?? null } } : {}),\n };\n }\n\n private byChat(chat: string): Call | undefined {\n for (const call of this.calls.values()) if (call.chat === chat) return call;\n return undefined;\n }\n\n /** A call by its id, or by its chat; with no ref, the only call (or the only one `prefer` picks). */\n private find(ref: string | undefined, prefer?: (call: Call) => boolean): Call {\n if (ref) {\n const byId = this.calls.get(ref);\n if (byId) return byId;\n const call = this.byChat(this.chatId(ref));\n if (!call) throw new CliError(\"not_found\", `No call in chat ${ref}`);\n return call;\n }\n const all = [...this.calls.values()];\n const picked = prefer && all.filter(prefer).length === 1 ? all.filter(prefer) : all;\n if (picked.length === 1) return picked[0];\n if (!picked.length) throw new CliError(\"not_found\", \"No call is on\");\n throw new CliError(\"bad_request\", \"More than one call is on: name one (its id or its chat)\", { calls: picked.map((c) => c.id) });\n }\n\n private link(chat: string): LinkView | undefined {\n return this.host.engine.getState().links.find((l) => l.id === chat);\n }\n\n /** A chat by id, contact key, prefix or name (the API's rules, `findChat`). */\n private chatId(ref: string): string {\n return findChat(this.host.engine.getState().links, ref).id;\n }\n\n /** The chat, if a call can go in it now; else why not (the engine's reason). */\n private callable(ref: string): LinkView {\n const link = this.link(this.chatId(ref))!;\n if (!link.profile) throw new CliError(\"unavailable\", \"Calls are in chats made since 0.4 (the chat session): this is a compatibility chat\");\n if (link.callsUnavailable) throw new CliError(\"unavailable\", link.callsUnavailable);\n return link;\n }\n\n private autoFor(chat: string): boolean {\n return this.auto.on && (this.auto.from.length === 0 || this.auto.from.includes(chat));\n }\n}\n\nfunction nameOf(link: LinkView): string | null {\n return link.label?.trim() || link.peerNick?.trim() || null;\n}\n\nfunction rateOf(value: unknown, fallback: CallRate): CallRate {\n if (value === undefined || value === null) return fallback;\n if (!isCallRate(value)) throw new CliError(\"bad_request\", \"rate must be 8000, 12000, 16000, 24000 or 48000\");\n return value;\n}\n\nfunction readAuto(path: string): AutoAnswer {\n const off: AutoAnswer = { on: false, from: [], rate: DEFAULT_RATE };\n if (!existsSync(path)) return off;\n try {\n const saved = (JSON.parse(readFileSync(path, \"utf8\")) as { autoAnswer?: Partial<AutoAnswer> }).autoAnswer;\n if (!saved) return off;\n return {\n on: saved.on === true,\n from: Array.isArray(saved.from) ? saved.from.filter((c): c is string => typeof c === \"string\") : [],\n rate: isCallRate(saved.rate) ? saved.rate : DEFAULT_RATE,\n };\n } catch {\n return off;\n }\n}\n","import { appendFileSync, closeSync, existsSync, fstatSync, openSync, readSync, renameSync, rmSync, writeFileSync } from \"node:fs\";\nimport type { AppFrameEvent } from \"@ghostly/core\";\nimport type { EngineClientSink } from \"@ghostly/browser/engine/server\";\nimport type { EngineEvent, RpcResponse } from \"@ghostly/browser/shared/rpc\";\nimport type { EngineState, GroupView, LinkView, PinView, StoredMessage } from \"@ghostly/browser/shared/types\";\nimport { chatJson, chatMessageJson, groupJson, groupMessageJson } from \"./views\";\nimport { paymentJson } from \"./wallets\";\n\n/**\n * The event stream (WISP 1100 § Event stream): what happened, derived from the engine's own events, one JSON object\n * each, with a `seq` that grows across restarts and an `id` stable for the fact it reports.\n */\nexport interface GhostlyEvent { seq: number; id: string; type: string; at: number; [field: string]: unknown }\n\n/** Events the journal keeps for `listen --since`. */\nexport const JOURNAL_KEEP = 10_000;\n/** Bytes the journal keeps at most: a contact's long texts never make it too large to read. */\nexport const JOURNAL_KEEP_BYTES = 16 * 1024 * 1024;\n/** A journal past this is trimmed, and only this much of its end is ever read. */\nexport const JOURNAL_MAX_BYTES = 2 * JOURNAL_KEEP_BYTES;\nconst SEEN_DB = \"ghostly-cli\";\nconst SEEN = \"seen\";\n\n/**\n * How long the seen set keeps the note of a settled message after its last activity (its arrival, its edit, a reaction):\n * older notes are dropped, and a message that came here longer ago with no note is no news. A daemon kept a note per\n * message of its whole history in memory, for as long as it ran.\n */\nexport const SEEN_KEEP_MS = 30 * 86_400_000;\n/** How often a daemon drops the notes that passed `SEEN_KEEP_MS`. */\nconst SWEEP_EVERY_MS = 6 * 3_600_000;\n\n/** How long a `file.*` event waits for the message that carries its file. */\nexport const FILE_MESSAGE_WAIT_MS = 2_000;\n\n/** The notice an app sends when a chat first goes live (apps/ui/src/hooks/useChat.ts): shown as a line, not a message. */\nexport const JOIN_NOTICE = /^👋 (?:(.+) )?joined$/;\n\ntype Listener = (event: GhostlyEvent) => void;\ntype Seen = Map<string, Map<string, string>>;\n\ninterface ChatShape { stage: string | null; live: boolean; transport: string | null; name: string | null; typing: boolean; typingKind: string; typingStatus: string | null; pin: PinView | null }\ninterface GroupShape { status: string | null; members: string[]; typing: Record<string, { kind: string; status?: string }>; pin: PinView | null }\n\n/**\n * Turns the engine's events into the stream, keeps the journal, and remembers which messages it already reported\n * (a store of its own in the profile's IndexedDB): after a restart, only what is new is reported, and a first\n * start reports nothing of the history that was already there.\n */\nexport class EventHub {\n private seq = 0;\n private journalLines = 0;\n private journalBytes = 0;\n private readonly listeners = new Set<Listener>();\n private readonly stateListeners = new Set<(state: EngineState) => void>();\n private readonly callListeners = new Set<(chat: string, signal: string) => void>();\n private readonly appListeners = new Set<(chat: string, frame: AppFrameEvent) => void>();\n private seen: Seen = new Map();\n private firstRun = false;\n private chats = new Map<string, ChatShape>();\n private groups = new Map<string, GroupShape>();\n private payments = new Map<string, string>();\n private transfers = new Map<string, string>();\n private received = new Map<string, string>();\n /** File id to the message that carries it, for `file.*` events. */\n private fileMessages = new Map<string, string>();\n /** `file.*` events whose message was not seen yet: they wait for it a moment (the engine stores it right after). */\n private pendingFiles = new Map<string, { timer: ReturnType<typeof setTimeout>; emits: ((messageId: string | null) => void)[] }>();\n private baselined = false;\n /** Chats and groups whose history the baseline did not read whole, and how to read it: until a file event needs it. */\n private unread = new Map<string, (chat: string) => Promise<readonly StoredMessage[]>>();\n /**\n * Whether the seen set notes mentions (`+m`): false on a set written before it did, whose mentions were reported with\n * their `group.message` already. Its first baseline then notes them without a `group.mentioned` each.\n */\n private mentionsNoted = true;\n /** Whether the notes carry their time (`@…`): false on a set written before they did, until a whole read stamps them. */\n private agesNoted = true;\n private sweeper: ReturnType<typeof setInterval> | null = null;\n private db!: IDBDatabase;\n state: EngineState | null = null;\n\n /** `seenDb`: the IndexedDB database of the seen set (tests give each hub its own). */\n constructor(private readonly journalPath: string, private readonly now: () => number = Date.now, private readonly seenDb = SEEN_DB) {}\n\n /** Loads the journal's last seq and the seen set; call before `sink` is attached. */\n async open(): Promise<void> {\n // A journal that cannot be read never keeps the profile from opening: it is set aside and a new one begins.\n try {\n if (existsSync(this.journalPath)) {\n const { lines, size } = journalTail(this.journalPath);\n this.journalLines = lines.length;\n this.journalBytes = size;\n for (let i = lines.length - 1; i >= 0; i--) {\n try { this.seq = (JSON.parse(lines[i]) as GhostlyEvent).seq; break; } catch { /* a torn last line */ }\n }\n if (size > JOURNAL_MAX_BYTES || lines.length > JOURNAL_KEEP * 2) this.trimJournal();\n }\n } catch (error) {\n process.stderr.write(`ghostly: the event journal could not be read, a new one begins: ${error instanceof Error ? error.message : String(error)}\\n`);\n try { renameSync(this.journalPath, this.journalPath + \".unreadable\"); } catch { /* best effort */ }\n this.journalLines = 0;\n this.journalBytes = 0;\n }\n this.db = await new Promise<IDBDatabase>((resolve, reject) => {\n const request = indexedDB.open(this.seenDb, 1);\n request.onupgradeneeded = () => { request.result.createObjectStore(SEEN); this.firstRun = true; };\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n });\n // Two requests for the whole set, never a cursor: a cursor costs one tree search per row on Node's IndexedDB.\n const store = this.db.transaction(SEEN).objectStore(SEEN);\n const all = <T>(request: IDBRequest<T[]>) => new Promise<T[]>((resolve, reject) => {\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n });\n const [keys, values] = await Promise.all([all(store.getAllKeys()), all(store.getAll() as IDBRequest<string[]>)]);\n for (let i = 0; i < keys.length; i++) {\n const [chat, id] = keys[i] as [string, string];\n this.seenOf(chat).set(id, values[i]);\n }\n this.mentionsNoted = this.firstRun || this.seen.get(FORMAT)?.get(MENTIONS_NOTED) === \"yes\";\n this.agesNoted = this.firstRun || this.seen.get(FORMAT)?.get(AGES_NOTED) === \"yes\";\n }\n\n /** The sink the engine server posts to. */\n readonly sink: EngineClientSink = {\n post: (message: EngineEvent | RpcResponse) => {\n try { this.handle(message); } catch (error) { process.stderr.write(`ghostly: event error: ${error instanceof Error ? error.stack : String(error)}\\n`); }\n },\n };\n\n /**\n * The engine as it is at start, before the sink is attached: chats and groups are known without events; messages\n * missing from the seen set are news (they came while no process derived events, a crash), except on a first\n * run, which only learns the history that was already there.\n */\n baseline(state: EngineState, histories: Map<string, readonly StoredMessage[]>, rest?: { newest: Map<string, readonly StoredMessage[]>; whole: (chat: string) => Promise<readonly StoredMessage[]> }): void {\n this.stateChanged(state);\n for (const [chat, messages] of histories) this.messages(chat, messages);\n // `rest.newest`: chats and groups in step (`inStep`), only their newest messages read. Nothing in them is news;\n // a file of an older message is looked up when an event needs it (`rest.whole` reads the history then).\n for (const [chat, messages] of rest?.newest ?? []) { this.messages(chat, messages, []); this.unread.set(chat, rest!.whole); }\n this.baselined = true;\n this.firstRun = false;\n // The set's own marks, in one write: it notes mentions, and (every history having been read whole while it did not)\n // its notes carry their time.\n const marks = [MENTIONS_NOTED, AGES_NOTED].filter((mark) => this.seen.get(FORMAT)?.get(mark) !== \"yes\");\n for (const mark of marks) this.seenOf(FORMAT).set(mark, \"yes\");\n if (marks.length) this.persist(FORMAT, marks.map((mark) => [mark, \"yes\"]));\n this.mentionsNoted = true;\n this.agesNoted = true;\n this.sweep();\n if (!this.sweeper) {\n this.sweeper = setInterval(() => this.sweep(), SWEEP_EVERY_MS);\n this.sweeper.unref?.();\n }\n }\n\n /**\n * Drops the notes of settled messages, and of reactions, whose last activity is older than `SEEN_KEEP_MS`: from memory\n * and from the store. A message of mine still on its way (or failed, which a retry sends again) keeps its note.\n */\n sweep(): number {\n const cutoff = this.now() - SEEN_KEEP_MS;\n let dropped = 0;\n for (const [chat, known] of this.seen) {\n if (chat === FORMAT || chat.startsWith(\"said:\")) continue;\n const reactions = chat.startsWith(\"reactions:\");\n const gone: [string, null][] = [];\n for (const [id, note] of known) {\n const at = timeIn(note);\n if (at === undefined || at >= cutoff || (!reactions && !settled(stateIn(note)))) continue;\n known.delete(id);\n gone.push([id, null]);\n }\n if (gone.length) { this.persist(chat, gone); dropped += gone.length; }\n if (!known.size) this.seen.delete(chat);\n }\n return dropped;\n }\n\n /**\n * Whether these messages of a chat or group (its newest) are all as the seen set has them, reactions included: there\n * is then nothing to report of them. Never on a first run, nor on a seen set from before it noted mentions: those\n * take the whole history once.\n */\n inStep(chat: string, messages: readonly StoredMessage[]): boolean {\n if (this.firstRun || !this.mentionsNoted || !this.agesNoted) return false;\n const known = this.seen.get(chat), reactions = this.seen.get(`reactions:${chat}`);\n const cutoff = this.now() - SEEN_KEEP_MS;\n return messages.every((message) => {\n const note = known?.get(message.id), before = note === undefined ? undefined : stateIn(note);\n // Its note dropped, and nothing about it since (`SEEN_KEEP_MS`): nothing to report of it.\n if (note === undefined && message.timestamp < cutoff && activityOf(message) < cutoff) return true;\n let state = stateOf(message);\n // As `messages` reads it: a mention reported stays reported.\n if (before !== undefined && mentionedIn(before) && !mentionedIn(state)) state = withFlag(state, MENTIONED);\n if (before !== state) return false;\n return Object.entries(message.reactions ?? {}).every(([by, r]) => {\n const note = reactions?.get(`${message.id}|${by}`);\n return note === undefined ? r.at < cutoff : stateIn(note) === String(r.n);\n });\n });\n }\n\n onEvent(listener: Listener): () => void { this.listeners.add(listener); return () => this.listeners.delete(listener); }\n onState(listener: (state: EngineState) => void): () => void { this.stateListeners.add(listener); return () => this.stateListeners.delete(listener); }\n /** A call signal from a contact's app, as the engine gives it. */\n onCallSignal(listener: (chat: string, signal: string) => void): () => void { this.callListeners.add(listener); return () => this.callListeners.delete(listener); }\n /** A frame of a contact's mini-app (apps/1), as the engine gives it: the app named by its chat app id. */\n onAppFrame(listener: (chat: string, frame: AppFrameEvent) => void): () => void { this.appListeners.add(listener); return () => this.appListeners.delete(listener); }\n\n /** Events after `since` still in the journal; a gap event first when older ones were dropped. */\n replay(since: number): GhostlyEvent[] {\n if (!existsSync(this.journalPath) || since >= this.seq) return [];\n const out: GhostlyEvent[] = [];\n for (const line of journalTail(this.journalPath).lines) {\n try { const event = JSON.parse(line) as GhostlyEvent; if (event.seq > since) out.push(event); } catch { /* torn */ }\n }\n if (out.length && out[0].seq > since + 1) out.unshift({ seq: since, id: `events.gap:${since}:${out[0].seq}`, type: \"events.gap\", at: this.now(), from: since + 1, to: out[0].seq - 1 });\n return out;\n }\n\n get lastSeq(): number { return this.seq; }\n\n /** Emits an event the host makes itself (`daemon.started`). */\n emit(type: string, id: string, fields: Record<string, unknown> = {}): GhostlyEvent {\n const event: GhostlyEvent = { seq: ++this.seq, id, type, at: this.now(), ...fields };\n // The journal failing never keeps an event from the listeners.\n try {\n const line = JSON.stringify(event) + \"\\n\";\n appendFileSync(this.journalPath, line, { mode: 0o600 });\n this.journalBytes += Buffer.byteLength(line);\n if (++this.journalLines > JOURNAL_KEEP * 2 || this.journalBytes > JOURNAL_MAX_BYTES) this.trimJournal();\n } catch (error) {\n process.stderr.write(`ghostly: event journal error: ${error instanceof Error ? error.message : String(error)}\\n`);\n }\n for (const listener of this.listeners) {\n try { listener(event); } catch { /* one listener's failure is its own */ }\n }\n return event;\n }\n\n /** Keeps the last `JOURNAL_KEEP` events, and of them no more than `JOURNAL_KEEP_BYTES`. */\n private trimJournal(): void {\n const lines = journalTail(this.journalPath).lines.slice(-JOURNAL_KEEP);\n let start = lines.length, bytes = 0;\n while (start > 0 && bytes + Buffer.byteLength(lines[start - 1]) + 1 <= JOURNAL_KEEP_BYTES) bytes += Buffer.byteLength(lines[--start]) + 1;\n const kept = lines.slice(start);\n writeFileSync(this.journalPath + \".tmp\", kept.length ? kept.join(\"\\n\") + \"\\n\" : \"\", { mode: 0o600 });\n renameSync(this.journalPath + \".tmp\", this.journalPath);\n this.journalLines = kept.length;\n this.journalBytes = bytes;\n }\n\n /**\n * What was removed here leaves the journal too: the journal is written again without the events that name it (a\n * line that names it and cannot be read goes as well), and a journal set aside as unreadable, which cannot be sorted,\n * is deleted. `ids`: what was removed (a message's id, a chat's or a group's); `gone`: whether an event is of it.\n */\n private forget(ids: readonly string[], gone: (event: GhostlyEvent) => boolean): void {\n if (!ids.length) return;\n try {\n rmSync(this.journalPath + \".unreadable\", { force: true });\n if (!existsSync(this.journalPath)) return;\n const lines = journalTail(this.journalPath).lines;\n const kept = lines.filter((line) => {\n if (!ids.some((id) => line.includes(JSON.stringify(id)))) return true;\n try { return !gone(JSON.parse(line) as GhostlyEvent); } catch { return false; }\n });\n if (kept.length === lines.length) return;\n writeFileSync(this.journalPath + \".tmp\", kept.length ? kept.join(\"\\n\") + \"\\n\" : \"\", { mode: 0o600 });\n renameSync(this.journalPath + \".tmp\", this.journalPath);\n this.journalLines = kept.length;\n this.journalBytes = kept.reduce((bytes, line) => bytes + Buffer.byteLength(line) + 1, 0);\n } catch (error) {\n process.stderr.write(`ghostly: event journal error: ${error instanceof Error ? error.message : String(error)}\\n`);\n }\n }\n\n private handle(message: EngineEvent | RpcResponse): void {\n switch (message.kind) {\n case \"state\": this.stateChanged(message.state); break;\n case \"messages\": this.messages(message.linkId, message.messages); break;\n // Only what changed: the same events as the whole history would give, and a deletion is named, not missing.\n case \"message-changes\": this.messages(message.linkId, message.messages, message.deleted); break;\n // Calls (WISP 1100 § Calls): the call manager reports them (call.incoming, call.connected, call.ended).\n case \"call-signal\":\n for (const listener of this.callListeners) {\n try { listener(message.linkId, message.signal); } catch (error) { process.stderr.write(`ghostly: call error: ${error instanceof Error ? error.stack : String(error)}\\n`); }\n }\n break;\n // Mini-apps this profile serves as a bot (./appEvents.ts): app.opened, app.closed, app.message.\n case \"app-frame\":\n for (const listener of this.appListeners) {\n try { listener(message.linkId, message.event); } catch (error) { process.stderr.write(`ghostly: app frame error: ${error instanceof Error ? error.stack : String(error)}\\n`); }\n }\n break;\n default: break;\n }\n }\n\n private stateChanged(state: EngineState): void {\n const quiet = !this.baselined;\n const links = new Map(state.links.map((link) => [link.id, link]));\n for (const [id, link] of links) {\n const shape = chatShape(link), before = this.chats.get(id);\n this.chats.set(id, shape);\n if (quiet) continue;\n if (!before) { this.emit(\"chat.created\", `chat.created:${id}`, { chat: id, summary: { ...chatJson(link), invite: null } }); continue; }\n if (shape.stage && shape.stage !== before.stage) {\n const progress = link.pairingProgress;\n this.emit(\"chat.pairing\", `chat.pairing:${id}:${shape.stage}:${progress?.since ?? this.now()}`, {\n chat: id, stage: shape.stage, ...(progress?.detail ? { detail: progress.detail } : {}), ...(progress?.reason ? { reason: progress.reason } : {}), ...(progress?.peerSeen ? { peerSeen: true } : {}),\n });\n }\n if (shape.live !== before.live || shape.transport !== before.transport) {\n const at = this.now();\n this.emit(\"chat.connection\", `chat.connection:${id}:${shape.live ? shape.transport : \"down\"}:${at}`, { chat: id, live: shape.live, transport: shape.transport, text: link.textDelivery ?? null });\n }\n if (shape.name !== before.name) this.emit(\"chat.renamed\", `chat.renamed:${id}:${this.now()}`, { chat: id, name: shape.name });\n this.pinned(\"chat\", id, before.pin, shape.pin);\n // The contact started or stopped writing (WISP 401 § Typing): it stops by itself 6 s after its last word. What\n // it is doing (typing, recording, thinking, a bot's status) goes with the start, and a change while it lasts\n // is a start again (its id then carries the sequence, so two changes in one millisecond stay apart).\n if (shape.typing !== before.typing) {\n this.emit(shape.typing ? \"typing.started\" : \"typing.stopped\", `typing.${shape.typing ? \"started\" : \"stopped\"}:${id}:${this.now()}`, { chat: id, ...(shape.typing ? typingFields(shape) : {}) });\n } else if (shape.typing && (shape.typingKind !== before.typingKind || shape.typingStatus !== before.typingStatus)) {\n this.emit(\"typing.started\", `typing.started:${id}:${this.now()}:${this.seq + 1}`, { chat: id, ...typingFields(shape) });\n }\n }\n const removedChats = [...this.chats.keys()].filter((id) => !links.has(id));\n for (const id of removedChats) {\n this.chats.delete(id);\n if (!quiet) this.emit(\"chat.removed\", `chat.removed:${id}`, { chat: id });\n }\n // A chat removed deletes its history here: its events go too, but for its removal.\n this.forget(removedChats, (event) => removedChats.includes(event.chat as string) && event.type !== \"chat.removed\");\n const groups = new Map(state.groups.map((group) => [group.id, group]));\n for (const [id, group] of groups) {\n const shape = groupShape(group), before = this.groups.get(id);\n this.groups.set(id, shape);\n if (quiet) continue;\n if (!before) { this.emit(\"group.created\", `group.created:${id}`, { group: id, summary: groupJson(group) }); continue; }\n if (shape.status !== before.status) this.emit(\"group.status\", `group.status:${id}:${shape.status}:${this.now()}`, { group: id, status: shape.status });\n const joined = shape.members.filter((key) => !before.members.includes(key));\n const left = before.members.filter((key) => !shape.members.includes(key));\n if (joined.length || left.length) this.emit(\"group.members\", `group.members:${id}:${this.now()}`, { group: id, joined, left });\n this.pinned(\"group\", id, before.pin, shape.pin);\n // A member started or stopped writing in a private group (WISP 902 · Group Mesh § Typing), as `typing.*` in a chat.\n for (const [member, word] of Object.entries(shape.typing)) {\n const was = before.typing[member];\n if (was && was.kind === word.kind && was.status === word.status) continue;\n this.emit(\"group.typing.started\", `group.typing.started:${id}:${member}:${this.now()}${was ? `:${this.seq + 1}` : \"\"}`, { group: id, member, ...word });\n }\n for (const member of Object.keys(before.typing)) {\n if (!shape.typing[member]) this.emit(\"group.typing.stopped\", `group.typing.stopped:${id}:${member}:${this.now()}`, { group: id, member });\n }\n }\n const removedGroups = [...this.groups.keys()].filter((id) => !groups.has(id));\n for (const id of removedGroups) {\n this.groups.delete(id);\n if (!quiet) this.emit(\"group.removed\", `group.removed:${id}`, { group: id });\n }\n // A group left or forgotten: the same.\n this.forget(removedGroups, (event) => removedGroups.includes(event.group as string) && event.type !== \"group.removed\");\n for (const [id, payment] of Object.entries(state.payments ?? {})) {\n const shape = `${payment.state}|${payment.error ?? \"\"}|${payment.closed ? 1 : 0}`, before = this.payments.get(id);\n this.payments.set(id, shape);\n if (quiet || before === shape) continue;\n const json = paymentJson(payment);\n if (before === undefined) this.emit(\"payment.created\", `payment.created:${id}`, { chat: payment.linkId, payment: json });\n else this.emit(\"payment.updated\", `payment.updated:${id}:${payment.state}:${this.now()}`, { chat: payment.linkId, payment: json });\n }\n for (const link of state.links) {\n for (const identity of link.identities?.received ?? []) {\n const key = `${link.id}:${identity.id}`, shape = identity.status, before = this.received.get(key);\n this.received.set(key, shape);\n if (quiet || before === shape) continue;\n this.emit(before === undefined ? \"identity.received\" : \"identity.status\", `identity.${before === undefined ? \"received\" : \"status\"}:${key}:${shape}`, {\n chat: link.id, identity: { id: identity.id, provider: identity.provider, subject: identity.subject, status: identity.status, verified: identity.verified },\n });\n }\n }\n for (const [id, transfer] of Object.entries(state.transfers ?? {})) {\n const shape = `${transfer.state}|${transfer.stage ?? \"\"}`, before = this.transfers.get(id);\n this.transfers.set(id, shape);\n if (quiet || before === shape) continue;\n const chat = state.links.find((l) => id.startsWith(`${l.id}-`))?.id ?? null;\n // A group's file (WISP 503) names its group.\n const group = chat ? undefined : state.groups.find((g) => id.startsWith(`group-${g.id}-`))?.id;\n const fields = { chat, ...(group ? { group } : {}), file: id, direction: transfer.direction ?? (id.includes(\"-in-\") ? \"in\" : \"out\"), size: transfer.size, transferred: transfer.transferred, ...(transfer.error ? { error: transfer.error } : {}) };\n let emit: ((messageId: string | null) => void) | null = null;\n if (transfer.state === \"done\") { const key = `file.done:${id}`; emit = (messageId) => this.emit(\"file.done\", key, { ...fields, messageId }); }\n else if (transfer.state === \"failed\") { const key = `file.failed:${id}:${this.now()}`; emit = (messageId) => this.emit(\"file.failed\", key, { ...fields, messageId, retry: !!transfer.retry }); }\n else if (transfer.stage === \"asking\" && fields.direction === \"in\") { const key = `file.offered:${id}:${this.now()}`; emit = (messageId) => this.emit(\"file.offered\", key, { ...fields, messageId, room: transfer.room ?? null }); }\n else if (transfer.stage) {\n const key = `file.stage:${id}:${transfer.stage}:${this.now()}`, stage = { stage: transfer.stage, ...(transfer.pausedBy ? { pausedBy: transfer.pausedBy } : {}) };\n emit = (messageId) => this.emit(\"file.stage\", key, { ...fields, messageId, ...stage });\n }\n if (emit) this.fileEvent(id, emit, chat ?? (group ? `group:${group}` : null));\n }\n this.state = state;\n for (const listener of this.stateListeners) {\n try { listener(state); } catch { /* its own */ }\n }\n }\n\n /**\n * Someone else pinned a message, or unpinned it (WISP 400 § Pinned message): `chat.pinned` or `group.pinned`, with\n * `messageId` (the row here; null when it is not here, or unpinned), `ref` (the id both sides know), `by` (`peer` or a\n * member key) and `removed`. What this profile pins itself says nothing: its command answered already.\n */\n private pinned(kind: \"chat\" | \"group\", id: string, before: PinView | null, now: PinView | null): void {\n if (!now || now.by === \"me\" || (before?.id === now.id && before.at === now.at)) return;\n const fields = { [kind]: id, messageId: now.messageId ?? null, ref: now.id || null, by: now.by, removed: !now.id };\n this.emit(`${kind}.pinned`, `${kind}.pinned:${id}:${now.id || \"off\"}:${now.at}`, fields);\n }\n\n /**\n * A `file.*` event names the message that carries the file. The engine can say where a transfer stands before it\n * stores that message: the event then waits for it, up to 2 s, and goes with `messageId: null` after that.\n */\n private fileEvent(fileId: string, emit: (messageId: string | null) => void, chat: string | null): void {\n const pending = this.pendingFiles.get(fileId);\n const messageId = this.fileMessages.get(fileId);\n if (messageId && !pending) { emit(messageId); return; }\n if (pending) { pending.emits.push(emit); return; }\n const timer = setTimeout(() => this.flushFile(fileId, null), FILE_MESSAGE_WAIT_MS);\n timer.unref?.();\n this.pendingFiles.set(fileId, { timer, emits: [emit] });\n // A file of a message older than the ones the baseline read: the history is read now, once, for all its files.\n const read = chat === null ? undefined : this.unread.get(chat);\n if (!read) return;\n this.unread.delete(chat!);\n void read(chat!).then((messages) => {\n for (const message of messages) {\n if (!message.file || this.fileMessages.has(message.file.id)) continue;\n this.fileMessages.set(message.file.id, message.id);\n this.flushFile(message.file.id, message.id);\n }\n }).catch(() => { /* the file's event goes without its message, as one whose message never came */ });\n }\n\n private flushFile(fileId: string, messageId: string | null): void {\n const pending = this.pendingFiles.get(fileId);\n if (!pending) return;\n this.pendingFiles.delete(fileId);\n clearTimeout(pending.timer);\n for (const emit of pending.emits) emit(messageId);\n }\n\n /** Whether this side said something once in a chat (the join notice): kept with the seen set. */\n said(chat: string, what: string): boolean {\n return this.seen.get(`said:${chat}`)?.has(what) ?? false;\n }\n markSaid(chat: string, what: string): void {\n this.seenOf(`said:${chat}`).set(what, \"yes\");\n this.persist(`said:${chat}`, [[what, \"yes\"]]);\n }\n\n private seenOf(chat: string): Map<string, string> {\n let map = this.seen.get(chat);\n if (!map) this.seen.set(chat, (map = new Map()));\n return map;\n }\n\n /** A chat's or group's whole history, or with `deleted` only what changed in it (then only those ids are gone). */\n private messages(chat: string, messages: readonly StoredMessage[], deleted?: readonly string[]): void {\n const known = this.seenOf(chat);\n const group = chat.startsWith(\"group:\") ? chat.slice(6) : null;\n // A group's whole history, empty: I left or forgot the group and its history went with it (a group joined again\n // starts from nothing). Nobody deleted a message: its rows are forgotten here without a `group.deleted` each.\n const quiet = (this.firstRun && !this.baselined) || (!!group && !deleted && messages.length === 0);\n const changes: [string, string | null][] = [];\n const present = new Set<string>();\n this.reactions(chat, group, messages, quiet);\n const files: [string, string][] = [];\n const cutoff = this.now() - SEEN_KEEP_MS;\n for (const message of messages) {\n present.add(message.id);\n if (message.file && this.fileMessages.get(message.file.id) !== message.id) { this.fileMessages.set(message.file.id, message.id); files.push([message.file.id, message.id]); }\n let state = stateOf(message);\n const note = known.get(message.id), before = note === undefined ? undefined : stateIn(note);\n const at = activityOf(message);\n const json = () => group ? groupMessageJson(message, this.state?.groups.find((g) => g.id === group)) : chatMessageJson(message, this.state?.links.find((l) => l.id === chat));\n if (before === undefined && message.timestamp < cutoff) {\n // Came here longer ago than `SEEN_KEEP_MS`, with no note: its note was dropped (or it came, that long ago, while\n // nothing ran). No news, and no note again, unless it was active since: then an edit since is reported, once.\n if (at < cutoff) continue;\n const noted = noteOf(state, at);\n known.set(message.id, noted);\n changes.push([message.id, noted]);\n const edit = message.edit;\n if (quiet || !edit || edit.at < cutoff || (message.sender === \"me\" && edit.restore)) continue;\n if (group) this.emit(\"group.message.edited\", `group.message.edited:${group}:${message.id}:${edit.seq}`, { group, messageId: message.id, edits: edit.seq, message: json() });\n else this.emit(\"message.edited\", `message.edited:${chat}:${message.id}:${edit.seq}`, { chat, messageId: message.id, edits: edit.seq, message: json() });\n continue;\n }\n // A mention reported stays reported: an edit that drops it and one that names me again say nothing more.\n if (before !== undefined && mentionedIn(before) && !mentionedIn(state)) state = withFlag(state, MENTIONED);\n if (before === state) {\n // A note from before notes carried their time gets it now, and nothing is reported.\n if (timeIn(note!) === undefined) { const noted = noteOf(state, at); known.set(message.id, noted); changes.push([message.id, noted]); }\n continue;\n }\n const noted = noteOf(state, at);\n known.set(message.id, noted);\n changes.push([message.id, noted]);\n if (quiet) continue;\n if (before === undefined) {\n if (group) {\n const type = message.event ? \"group.event\" : message.sender === \"peer\" ? \"group.message\" : \"group.sent\";\n this.emit(type, `${type}:${group}:${message.id}`, { group, message: json() });\n } else {\n const notice = JOIN_NOTICE.exec(message.text);\n const type = notice ? (message.sender === \"peer\" ? \"chat.joined\" : \"chat.announced\") : message.sender === \"peer\" ? \"message.received\" : \"message.sent\";\n this.emit(type, `${type}:${chat}:${message.id}`, { chat, message: json(), ...(notice ? { name: notice[1] ?? null } : {}) });\n }\n if (message.sender === \"peer\" && message.press) this.pressed(chat, group, message, json());\n continue;\n }\n // A press the engine learned after the reply was first seen here: a group reply first held from a copy a member\n // handed on without its reply box, which its author's whole copy completes (WISP 902 · Group Mesh § Catch-up).\n if (message.sender === \"peer\" && message.press && !pressedIn(before)) this.pressed(chat, group, message, json());\n // A mention of me learned after the message was reported: a group message first held from a copy a member handed\n // on without its author's whole signature, which the whole copy completes (WISP 902 · Group Mesh § Catch-up), or\n // an edit that names me. Its `group.message` said no mention; this says it, once.\n if (group && mentionedIn(state) && !mentionedIn(before) && this.mentionsNoted)\n this.emit(\"group.mentioned\", `group.mentioned:${group}:${message.id}`, { group, messageId: message.id, message: json() });\n if (group) {\n // A new text in a group (WISP 902 § Edits): mine as made here, a member's as it came. Once per edit number.\n const [, edits] = splitState(state), [, edited] = splitState(before);\n if (edits > edited) this.emit(\"group.message.edited\", `group.message.edited:${group}:${message.id}:${edits}`, { group, messageId: message.id, edits, message: json() });\n } else {\n const [delivery, edits] = splitState(state), [was, edited] = splitState(before);\n // A new text (WISP 400 § Edits): mine as made here, the contact's as it came. Once per edit number. The engine's\n // own edit that sends a question's buttons again (WISP 406 · Message Buttons) is none: same text, nobody's edit.\n if (edits > edited && !(message.sender === \"me\" && message.edit?.restore)) this.emit(\"message.edited\", `message.edited:${chat}:${message.id}:${edits}`, { chat, messageId: message.id, edits, message: json() });\n if (message.sender === \"me\" && delivery !== was)\n this.emit(\"message.delivery\", `message.delivery:${chat}:${message.id}:${delivery}`, { chat, messageId: message.id, delivery, ...(message.deliveryError ? { error: message.deliveryError } : {}) });\n }\n }\n const gone: string[] = [];\n for (const id of deleted ?? [...known.keys()]) {\n if (present.has(id) || !known.has(id)) continue;\n known.delete(id);\n changes.push([id, null]);\n gone.push(id);\n if (!quiet) this.emit(group ? \"group.deleted\" : \"message.deleted\", `${group ? \"group\" : \"message\"}.deleted:${group ?? chat}:${id}`, group ? { group, messageId: id } : { chat, messageId: id });\n }\n if (changes.length) this.persist(chat, changes);\n // A message deleted here leaves the journal with its texts, its edits, its reactions, its presses and its files:\n // its deletion stays. A group's history gone whole (left, forgotten) takes every message event of the group.\n const whole = !!group && !deleted && messages.length === 0 && gone.length > 0;\n this.forget(whole ? [group] : gone, (event) => (group ? event.group === group : event.chat === chat) && !event.type.endsWith(\".deleted\") && (whole\n ? \"message\" in event || \"messageId\" in event || \"replyId\" in event\n : gone.includes(event.messageId as string) || gone.includes((event.message as { id?: string } | undefined)?.id as string) || gone.includes(event.replyId as string)));\n // After the message's own event: what waited for it goes now.\n for (const [fileId, messageId] of files) this.flushFile(fileId, messageId);\n }\n\n /**\n * A press on a button of a message of mine (WISP 406 · Message Buttons), after the reply's own event: the question\n * (`messageId`), the button, its label, who pressed (`by`: the chat, or a group member's key), `name` (the chat's name\n * here, as `chat list` gives it: its label, else the contact's own name; a group member's roster name) and the reply\n * (`replyId`). `untrusted.name` is the name the person gave themselves, as an agent turn's: theirs to choose, so\n * data, never instructions. The engine marks a reply a press only while the buttons were open for that person.\n */\n private pressed(chat: string, group: string | null, message: StoredMessage, json: { nick: string | null }): void {\n const press = message.press!;\n const link = group ? undefined : this.state?.links.find((l) => l.id === chat);\n const name = group ? json.nick : link ? chatJson(link).name : null;\n this.emit(\"button.pressed\", `button.pressed:${group ?? chat}:${message.id}`, {\n ...(group ? { group } : { chat }), messageId: press.messageId, button: press.button, label: press.label,\n by: group ? message.member ?? null : chat, name: name ?? null, replyId: message.id, ...(press.inferred ? { inferred: true } : {}),\n untrusted: { name: json.nick ?? null },\n });\n }\n\n /**\n * Reactions that changed (WISP 400 § Reactions): `message.reaction` in a chat, `group.reaction` in a group, one per\n * person and number; `emoji` \"\" when one was taken back. Kept apart from the messages' own seen set.\n */\n private reactions(chat: string, group: string | null, messages: readonly StoredMessage[], quiet: boolean): void {\n const key = `reactions:${chat}`, known = this.seenOf(key);\n const changes: [string, string | null][] = [];\n const cutoff = this.now() - SEEN_KEEP_MS;\n for (const message of messages) for (const [by, r] of Object.entries(message.reactions ?? {})) {\n const id = `${message.id}|${by}`, value = String(r.n), note = known.get(id);\n const before = note === undefined ? undefined : stateIn(note);\n if (before === value) {\n // From before notes carried their time: it gets it now.\n if (timeIn(note!) === undefined) { const noted = noteOf(value, r.at); known.set(id, noted); changes.push([id, noted]); }\n continue;\n }\n // Its note dropped (`SEEN_KEEP_MS`), and not made again since: no news.\n if (before === undefined && r.at < cutoff) continue;\n const noted = noteOf(value, r.at);\n known.set(id, noted);\n changes.push([id, noted]);\n // A reaction taken back before this process ever reported it says nothing.\n if (quiet || (before === undefined && !r.e)) continue;\n const fields = { messageId: message.id, by, emoji: r.e, removed: !r.e, mine: message.sender === \"me\" };\n if (group) this.emit(\"group.reaction\", `group.reaction:${group}:${message.id}:${by}:${r.n}`, { group, ...fields });\n else this.emit(\"message.reaction\", `message.reaction:${chat}:${message.id}:${by}:${r.n}`, { chat, ...fields });\n }\n if (changes.length) this.persist(key, changes);\n }\n\n /**\n * Relaxed: nobody waits for it, and a note lost to a power cut is made again from the history at the next start\n * (its event is then reported a second time, as after a crash between a message's row and its note). A disk sync\n * for each one held the whole daemon for milliseconds, three times per message sent.\n */\n private persist(chat: string, changes: [string, string | null][]): void {\n const tx = this.db.transaction(SEEN, \"readwrite\", { durability: \"relaxed\" });\n const store = tx.objectStore(SEEN);\n for (const [id, state] of changes) {\n if (state === null) store.delete([chat, id]);\n else store.put(state, [chat, id]);\n }\n }\n}\n\nfunction deliveryOf(message: StoredMessage): string {\n return message.sender === \"peer\" ? \"received\" : message.delivery ?? \"sent\";\n}\n\n/** What a received button press adds to its delivery in the seen set (`received+p`): it is reported once. */\nconst PRESSED = \"+p\";\n/** What a group message that mentions me adds (`received+m`): a mention learned late is reported once. */\nconst MENTIONED = \"+m\";\n/** The seen set's own notes: whether it notes mentions (sets from before `+m` do not). */\nconst FORMAT = \"format:\";\nconst MENTIONS_NOTED = \"mentions\";\n/** The seen set's mark that its notes carry their time. */\nconst AGES_NOTED = \"ages\";\n\n/**\n * A note: what the seen set keeps of a message (or a reaction's number), then `@` and the time of its last activity in\n * base 36 (`received+p#e2@mg3k1x2a`), by which `sweep` drops it. A note from before has no time.\n */\nconst noteOf = (state: string, at: number): string => `${state}@${Math.max(0, Math.floor(at)).toString(36)}`;\nfunction stateIn(note: string): string {\n const at = note.lastIndexOf(\"@\");\n return at === -1 ? note : note.slice(0, at);\n}\nfunction timeIn(note: string): number | undefined {\n const at = note.lastIndexOf(\"@\");\n return at === -1 ? undefined : parseInt(note.slice(at + 1), 36);\n}\n/** The last time a message changed here: it came, it was edited, someone reacted. */\nfunction activityOf(message: StoredMessage): number {\n let at = Math.max(message.timestamp, message.edit?.at ?? 0);\n for (const r of Object.values(message.reactions ?? {})) at = Math.max(at, r.at ?? 0);\n return at;\n}\n/** A message that will not change by itself any more: received, or mine and delivered. */\nfunction settled(state: string): boolean {\n const delivery = splitState(state)[0].split(\"+\")[0];\n return delivery === \"received\" || delivery === \"delivered\";\n}\n\n/**\n * What the seen set keeps of a message: its delivery (`+p` once a press on a message of mine was reported, `+m` once a\n * group message's mention of me was), and its edit number once edited (`sent#e3`, `received+p+m#e2`).\n */\nfunction stateOf(message: StoredMessage): string {\n const peer = message.sender === \"peer\";\n const delivery = deliveryOf(message) + (peer && message.press ? PRESSED : \"\") + (peer && message.mentioned && message.linkId.startsWith(\"group:\") ? MENTIONED : \"\");\n return message.edit ? `${delivery}#e${message.edit.seq}` : delivery;\n}\nconst flagsOf = (state: string): string[] => splitState(state)[0].split(\"+\").slice(1).map((flag) => \"+\" + flag);\nconst pressedIn = (state: string): boolean => flagsOf(state).includes(PRESSED);\nconst mentionedIn = (state: string): boolean => flagsOf(state).includes(MENTIONED);\n/** The state with a flag added before its edit number. */\nfunction withFlag(state: string, flag: string): string {\n const at = state.lastIndexOf(\"#e\");\n return at === -1 ? state + flag : state.slice(0, at) + flag + state.slice(at);\n}\nfunction splitState(state: string): [string, number] {\n const at = state.lastIndexOf(\"#e\");\n return at === -1 ? [state, 0] : [state.slice(0, at), Number(state.slice(at + 2)) || 0];\n}\n\nfunction chatShape(link: LinkView): ChatShape {\n const view = chatJson(link);\n return {\n stage: view.stage, live: view.live, transport: view.transport, name: view.name, typing: !!link.peerTyping,\n typingKind: link.peerTyping ? link.peerTypingKind ?? \"typing\" : \"typing\", typingStatus: link.peerTyping ? link.peerTypingStatus ?? null : null, pin: link.pin ?? null,\n };\n}\n\n/** A `typing.started`'s word: the kind, and the contact's status line when it gave one (already cleaned). */\nfunction typingFields(shape: ChatShape): { kind: string; status?: string } {\n return { kind: shape.typingKind, ...(shape.typingStatus ? { status: shape.typingStatus } : {}) };\n}\n\nfunction groupShape(group: GroupView): GroupShape {\n return { status: group.status ?? (group.invitation ? \"invited\" : null), members: group.members.map((m) => m.key).sort(),\n typing: Object.fromEntries((group.typing ?? []).map((t) => [t.key, { kind: t.kind ?? \"typing\", ...(t.status ? { status: t.status } : {}) }])), pin: group.pin ?? null };\n}\n\n/**\n * The journal's whole lines within its last `JOURNAL_MAX_BYTES`, and its size: never the whole file as one string, so\n * a journal grown past what a string can hold (or a trim's bound) is still read, and in bounded time.\n */\nfunction journalTail(path: string): { lines: string[]; size: number } {\n const fd = openSync(path, \"r\");\n try {\n const size = fstatSync(fd).size;\n // From one byte before the cut, so a line that begins exactly at it is kept whole.\n const from = Math.max(0, size - JOURNAL_MAX_BYTES - 1);\n const buffer = Buffer.alloc(size - from);\n let read = 0;\n while (read < buffer.length) {\n const n = readSync(fd, buffer, read, buffer.length - read, from + read);\n if (!n) break;\n read += n;\n }\n const lines = buffer.toString(\"utf8\", 0, read).split(\"\\n\");\n if (from > 0) lines.shift();\n return { lines: lines.filter(Boolean), size };\n } finally {\n closeSync(fd);\n }\n}\n","/**\n * A join through a group's link, as a daemon says it in its log (stderr): the steps of the knock and of the entry\n * session, one short line each, from the engine's join trace (packages/browser/src/engine/joinTrace.ts). Without them a\n * join that stalled left nothing in the admin's log to tell a knock never seen from a session that never opened\n * (2026-10-16). The reads of the knocks, every few seconds, and an edge's own steps are not said.\n */\nconst SAID: Record<string, string> = {\n \"join.start\": \"joining through its link\",\n \"knock.published\": \"knock left\",\n \"knock.seen\": \"a knock seen: opening an entry session\",\n \"link.start\": \"entry session starting\",\n \"link.presence\": \"entry session: the other side's packet seen\",\n \"link.offering\": \"entry session: offering a connection\",\n \"link.answering\": \"entry session: answering a connection\",\n \"link.connecting\": \"entry session: connecting\",\n \"link.open\": \"entry session open\",\n \"link.idle\": \"entry session not open\",\n \"link.native\": \"entry session: WebRTC connected nothing, going on over a native transport\",\n \"invite.sent\": \"invitation sent on the entry session\",\n \"invite.received\": \"invitation received on the entry session: accepting\",\n \"welcome.sent\": \"welcome sent\",\n \"welcome.received\": \"welcome received: in the group\",\n};\n\n/** The log line for one join-trace line, or null when it is not said. */\nexport function joinLogLine(traced: string): string | null {\n let step: { t?: unknown; g?: unknown; step?: unknown; role?: unknown };\n try { step = JSON.parse(traced) as typeof step; } catch { return null; }\n if (typeof step.step !== \"string\" || typeof step.g !== \"string\" || !Object.hasOwn(SAID, step.step)) return null;\n // A link's steps are an entry session's only: an edge's go to the link trace.\n const role = step.role === \"host\" || step.role === \"guest\" ? step.role : undefined;\n if (step.step.startsWith(\"link.\") && !role) return null;\n const at = typeof step.t === \"number\" ? ` at ${new Date(step.t).toISOString()}` : \"\";\n return `ghostly: group ${step.g}: ${SAID[step.step]}${role ? ` (${role === \"host\" ? \"letting in\" : \"joining\"})` : \"\"}${at}`;\n}\n","import { existsSync, mkdirSync, readFileSync, rmSync, statSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { join } from \"node:path\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\nimport { Worker } from \"node:worker_threads\";\nimport { fedimintSdkOn, type FedimintSdk } from \"@ghostly/browser/engine/paymentAdapters/fedimintSdk\";\n\n/**\n * The Fedimint client on Node (WISP 1100 § Wallet SDKs on Node): the app's own SDK and WebAssembly, each database in\n * a `worker_threads` worker (fedimintWorker.mjs) and a file of the profile's `fedimint/` folder, where the browser has\n * a module worker and the origin-private file system. Only the platform differs: the client and what the engine does\n * with it are the app's.\n */\n\nconst WASM_PACKAGE = \"@fedimint/fedimint-client-wasm-bundler\";\nconst DATABASE = /^ghostly-fedimint-[\\w-]{1,80}\\.db$/;\n\n/** The worker's file: beside this chunk wherever the bundler put it (the entry, or assets/), else the workspace's. */\nfunction workerFile(): URL {\n for (const place of [\"./fedimintWorker.mjs\", \"./assets/fedimintWorker.mjs\"]) {\n const url = new URL(place, import.meta.url);\n if (existsSync(fileURLToPath(url))) return url;\n }\n throw new Error(\"The Fedimint worker is missing from this build\");\n}\n\nlet compiling: Promise<WebAssembly.Module> | undefined;\n/** Compiled once per process (11 MB): each worker is handed the module, not the bytes. */\nfunction compiled(): Promise<WebAssembly.Module> {\n return compiling ??= WebAssembly.compile(readFileSync(createRequire(import.meta.url).resolve(`${WASM_PACKAGE}/fedimint_client_wasm_bg.wasm`)))\n .catch((error) => { compiling = undefined; throw error; });\n}\n\nfunction databasePath(dir: string, database: string): string {\n if (!DATABASE.test(database)) throw new Error(\"Invalid Fedimint database name\");\n return join(dir, database);\n}\n\n/** The SDK on this profile's folder `dir`. Loaded on first use, as in the app: a bot that joins no federation compiles nothing. */\nexport function nodeFedimintSdk(dir: string): () => Promise<FedimintSdk> {\n let loading: Promise<FedimintSdk> | undefined;\n return () => loading ??= (async () => {\n const [{ Transport }, module] = await Promise.all([import(\"@fedimint/types\"), compiled()]);\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n const glue = pathToFileURL(createRequire(import.meta.url).resolve(`${WASM_PACKAGE}/fedimint_client_wasm_bg.js`)).href;\n const worker = workerFile();\n type Message = { type: string; payload?: unknown; requestId?: number };\n class ThreadTransport extends Transport {\n readonly logger = { debug() {}, info() {}, warn() {}, error() {} };\n private readonly thread = new Worker(worker, { workerData: { dir, glue } });\n /** Why the thread ended, once it has: later requests fail with it rather than wait on nothing. */\n private ended: string | undefined;\n constructor() {\n super();\n this.thread.on(\"message\", (message) => this.messageHandler(message));\n this.thread.on(\"error\", (error: unknown) => this.end(`The Fedimint client stopped: ${(error instanceof Error && error.message) || \"unknown error\"}`));\n this.thread.on(\"exit\", () => this.end(\"The Fedimint client stopped\"));\n }\n private end(error: string) {\n if (this.ended) return;\n this.ended = error;\n this.messageHandler({ type: \"error\", error });\n }\n postMessage(message: Message) {\n const ended = this.ended;\n if (ended) { queueMicrotask(() => this.messageHandler({ type: \"error\", error: ended, request_id: message.requestId })); return; }\n this.thread.postMessage(message.type === \"init\" ? { ...message, payload: { ...(message.payload as object), module } } : message);\n }\n terminate() { this.ended ??= \"The Fedimint client was closed\"; void this.thread.terminate(); }\n }\n return fedimintSdkOn({\n transport: async () => new ThreadTransport(),\n async exists(database) {\n try { return statSync(databasePath(dir, database)).size > 0; } catch { return false; }\n },\n async remove(database) { rmSync(databasePath(dir, database), { force: true }); },\n });\n })().catch((error) => { loading = undefined; throw error; });\n}\n","import { readFile } from \"node:fs/promises\";\nimport { fileURLToPath } from \"node:url\";\n\n/**\n * `fetch` for `file:` URLs, which Node's does not do. The wallet SDKs' WebAssembly (Bark, BDK, Breez, Iroh) is loaded\n * the way a page loads it, `fetch(new URL(\"…wasm\", import.meta.url))`; on Node that URL is a file beside the bundle.\n * Only `.wasm` files are answered, and nothing else of fetch changes.\n */\nexport function installFileFetch(): void {\n const original = globalThis.fetch;\n const patched: typeof fetch = async (input, init) => {\n const url = typeof input === \"string\" ? input : input instanceof URL ? input.href : input.url;\n if (url.startsWith(\"file:\")) {\n const parsed = new URL(url);\n if (!parsed.pathname.endsWith(\".wasm\") || parsed.search || parsed.hash) throw new TypeError(`Refusing to fetch ${url}`);\n const bytes = await readFile(fileURLToPath(parsed));\n return new Response(bytes, { headers: { \"content-type\": \"application/wasm\" } });\n }\n return original(input, init);\n };\n globalThis.fetch = patched;\n}\n","import { lookup as dnsLookup } from \"node:dns\";\nimport { request } from \"node:https\";\nimport { isIP, type LookupFunction } from \"node:net\";\nimport { checkPushEndpoint, isPublicAddress, type PushRequest } from \"@ghostly/core\";\n\n/** An address as bytes (4 or 16), or null when it is not one. */\nexport function addressBytes(address: string): Uint8Array | null {\n const family = isIP(address);\n if (family === 4) return Uint8Array.from(address.split(\".\").map(Number));\n if (family !== 6) return null;\n let text = address.replace(/%.*$/, \"\");\n // An IPv4 tail (::ffff:1.2.3.4) as two groups.\n const v4 = /(\\d+\\.\\d+\\.\\d+\\.\\d+)$/.exec(text);\n if (v4) {\n const [a, b, c, d] = v4[1]!.split(\".\").map(Number);\n text = text.slice(0, -v4[1]!.length) + ((a! << 8) | b!).toString(16) + \":\" + ((c! << 8) | d!).toString(16);\n }\n const [head, tail] = text.includes(\"::\") ? text.split(\"::\") as [string, string] : [text, null];\n const left = head ? head.split(\":\") : [];\n const right = tail ? tail.split(\":\") : [];\n const groups = tail === null ? left : [...left, ...Array(8 - left.length - right.length).fill(\"0\"), ...right];\n if (groups.length !== 8) return null;\n const bytes = new Uint8Array(16);\n groups.forEach((group, i) => { const n = parseInt(group, 16); bytes[i * 2] = n >> 8; bytes[i * 2 + 1] = n & 0xff; });\n return bytes;\n}\n\n/** True when `address` is on the public Internet (see `isPublicAddress`). */\nexport function isPublicIp(address: string): boolean {\n const bytes = addressBytes(address);\n return bytes !== null && isPublicAddress(bytes);\n}\n\ntype Resolver = (hostname: string, options: { all: true }, callback: (error: NodeJS.ErrnoException | null, found: { address: string; family: number }[]) => void) => void;\n\n/** `lookup` over `resolve` (`dns.lookup`), refusing a name any of whose addresses is not public. */\nexport const publicOnly = (resolve: Resolver): LookupFunction => (hostname, options, callback) => {\n resolve(hostname, { ...options, all: true }, (error, found) => {\n if (error) return callback(error, \"\", 4);\n const addresses = found as unknown as { address: string; family: number }[];\n if (!addresses.length || addresses.some((a) => !isPublicIp(a.address))) {\n return callback(Object.assign(new Error(`${hostname} is not a public address`), { code: \"ENOTFOUND\" }), \"\", 4);\n }\n if ((options as { all?: boolean }).all) return (callback as unknown as (e: null, a: typeof addresses) => void)(null, addresses);\n callback(null, addresses[0]!.address, addresses[0]!.family);\n });\n};\n\nexport const publicOnlyLookup: LookupFunction = publicOnly(dnsLookup as unknown as Resolver);\n\n/**\n * Posts a wake-up (WISP 401 § Wake-up push) the way the Desktop does from Rust: only to a push service's endpoint\n * (`checkPushEndpoint`), whose name must resolve to public addresses only, no redirect followed, no proxy, 10 s at most.\n * Answers the push service's status.\n */\nexport async function nodePushSend(push: PushRequest, lookup: LookupFunction = publicOnlyLookup): Promise<number> {\n const url = checkPushEndpoint(push.url);\n return new Promise((resolve, reject) => {\n const req = request(url, { method: \"POST\", headers: { ...push.headers, \"Content-Length\": String(push.body.length) }, lookup, agent: false, timeout: 10_000 }, (res) => {\n res.resume();\n resolve(res.statusCode ?? 0);\n });\n req.on(\"timeout\", () => req.destroy(new Error(\"The push service did not answer\")));\n req.on(\"error\", reject);\n req.end(push.body);\n });\n}\n","import { deserialize } from \"node:v8\";\nimport type { DatabaseSync, StatementSync } from \"node:sqlite\";\n\n/**\n * `node:sqlite`, and what is left of the store's format 2 (values in a `rows` table of rows.db, keys in the snapshot):\n * read once, at the open that moves such a profile into rows.db's trees (./sqlTree.ts).\n */\nexport interface RowFile {\n /** A row's value. Throws for a row that is not there: a value read as missing would be a message read as nothing. */\n get(id: number): unknown;\n /** How many rows there are. */\n count(): number;\n close(): void;\n}\n\n/** The warning `node:sqlite` gives as it loads (experimental on Node 22 and 23): said once for every command, it is noise. */\nconst SQLITE_WARNING = /^SQLite is an experimental feature/;\n\nlet loaded: Promise<typeof import(\"node:sqlite\") | null> | undefined;\n\n/**\n * `node:sqlite`, or null where Node has none (before 22.13 it needs a flag): the store then keeps every value in\n * memory and in the snapshot, as before rows.db.\n */\nexport function loadSqlite(): Promise<typeof import(\"node:sqlite\") | null> {\n loaded ??= (async () => {\n const emit = process.emitWarning;\n process.emitWarning = function (this: unknown, warning: string | Error, ...rest: unknown[]) {\n if (SQLITE_WARNING.test(typeof warning === \"string\" ? warning : warning?.message ?? \"\")) return;\n return (emit as (...args: unknown[]) => void).call(process, warning, ...rest);\n } as typeof process.emitWarning;\n try { return await import(\"node:sqlite\"); } catch { return null; } finally { process.emitWarning = emit; }\n })();\n return loaded;\n}\n\n/** Format 2's rows, read. */\nexport function openRowFile(sqlite: typeof import(\"node:sqlite\"), path: string): RowFile {\n const db: DatabaseSync = new sqlite.DatabaseSync(path);\n db.exec(\"CREATE TABLE IF NOT EXISTS rows (id INTEGER PRIMARY KEY, value BLOB NOT NULL)\");\n const select: StatementSync = db.prepare(\"SELECT value FROM rows WHERE id = ?\");\n return {\n get(id) {\n const row = select.get(id) as { value: Uint8Array } | undefined;\n if (!row) throw new Error(`The store's rows.db has no row ${id}: the profile's store is damaged`);\n return deserialize(row.value);\n },\n count() {\n return Number((db.prepare(\"SELECT count(*) AS n FROM rows\").get() as { n: number }).n);\n },\n close() {\n db.close();\n },\n };\n}\n\n/** Where a record of format 2 has its value. */\nconst ROW = Symbol(\"ghostly.row\");\ntype RowRecord = { key: unknown; value: unknown; [ROW]?: number };\n\n/** Records of a snapshot of format 2, whose value is read from `rows` (a getter shared by all of them). */\nexport function rowRecords(rows: RowFile) {\n const read = function (this: RowRecord) { return rows.get(this[ROW]!); };\n const proto = Object.defineProperty({}, \"value\", { get: read, enumerable: true, configurable: true });\n return {\n make(key: unknown, row: number): RowRecord {\n const record = Object.create(proto) as RowRecord;\n record.key = key;\n record[ROW] = row;\n return record;\n },\n };\n}\n","/**\n * An IndexedDB key as bytes whose order is the keys' own (W3C IndexedDB § Compare two keys: a number before a date\n * before a string before binary before an array; strings by UTF-16 code unit; arrays item by item, the shorter\n * first), so that SQLite, comparing them as blobs, sorts and ranges them as IndexedDB does. Held as a \"latin1\" string\n * (one char per byte): JavaScript compares those in the same order.\n *\n * Every encoding is prefix-free (numbers and dates are 9 bytes; strings, binary and arrays end with a 0 byte no unit or\n * item begins with), so a key followed by another (an index entry's key, then its record's) sorts as the pair does.\n */\nconst NUMBER = 0x10, DATE = 0x20, STRING = 0x30, BINARY = 0x40, ARRAY = 0x50;\n\n/** The byte after which no encoded key goes on: `key + AFTER` is past every pair that begins with `key`. */\nexport const AFTER = \"\\xff\";\n\nexport function keyBytes(key: unknown): string {\n const out: number[] = [];\n write(key, out, 0);\n let text = \"\";\n for (let at = 0; at < out.length; at += 8192) text += String.fromCharCode(...out.slice(at, at + 8192));\n return text;\n}\n\nfunction write(key: unknown, out: number[], depth: number): void {\n if (depth > 1000) throw new Error(\"A key nested too deep\");\n if (typeof key === \"number\") { out.push(NUMBER); number(key, out); return; }\n if (key instanceof Date) { out.push(DATE); number(key.getTime(), out); return; }\n if (typeof key === \"string\") {\n out.push(STRING);\n // Each code unit from 1 up (0 ends the string, so a shorter string sorts first): one byte below 0x7e (most ids are\n // ASCII), else 0x7f and three bytes, which sort after every one-byte unit as their units do.\n for (let at = 0; at < key.length; at++) {\n const unit = key.charCodeAt(at);\n if (unit < 0x7e) out.push(unit + 1);\n else out.push(0x7f, (unit >> 12) + 1, ((unit >> 6) & 63) + 1, (unit & 63) + 1);\n }\n out.push(0);\n return;\n }\n if (key instanceof ArrayBuffer || ArrayBuffer.isView(key)) {\n const bytes = key instanceof ArrayBuffer ? new Uint8Array(key) : new Uint8Array(key.buffer, key.byteOffset, key.byteLength);\n out.push(BINARY);\n for (const byte of bytes) out.push((byte >> 4) + 1, (byte & 15) + 1);\n out.push(0);\n return;\n }\n if (Array.isArray(key)) {\n out.push(ARRAY);\n for (const item of key) write(item, out, depth + 1);\n out.push(0);\n return;\n }\n throw new Error(\"Not an IndexedDB key\");\n}\n\nconst view = new DataView(new ArrayBuffer(8));\n/** A float64 big-endian, its sign bit flipped (a positive number) or every bit flipped (a negative one): bytes in the numbers' order. */\nfunction number(value: number, out: number[]): void {\n view.setFloat64(0, value === 0 ? 0 : value);\n const negative = (view.getUint8(0) & 0x80) !== 0;\n for (let at = 0; at < 8; at++) {\n const byte = view.getUint8(at);\n out.push(negative ? ~byte & 0xff : at === 0 ? byte ^ 0x80 : byte);\n }\n}\n","import { closeSync, openSync } from \"node:fs\";\nimport type { DatabaseSync, StatementSync } from \"node:sqlite\";\nimport { deserialize, serialize } from \"node:v8\";\nimport { AFTER, keyBytes } from \"./keyBytes\";\n\n/**\n * fake-indexeddb keeps an object store's records, and an index's entries, in a red-black tree in memory. A store kept\n * on disk has a `SqlTree` in its place instead, with the tree's methods (`get`, `put`, `delete`, `getRecords`,\n * `getAllRecords`, `size`): its records are rows of `entries` in rows.db, by their key's bytes in IndexedDB's order\n * (./keyBytes.ts), and nothing of them stays in memory. fake-indexeddb's own code (transactions, cursors, ranges,\n * unique indexes, rollbacks) runs on it unchanged.\n *\n * What a transaction writes stays in a layer in memory until the journal has the transaction (`storage.ts`): then the\n * layer goes to SQLite in one SQLite transaction (`TreeFile.apply`). A transaction that aborts drops its layer. Reads\n * see SQLite with the layers on it. fake-indexeddb never runs a transaction that writes a store beside another one\n * that reads or writes it (`Database.processTransactions`), so the layer of a store is one transaction's.\n */\n\ninterface Entry { key: unknown; value: unknown }\ninterface Range { lower?: unknown; upper?: unknown; lowerOpen: boolean; upperOpen: boolean; includes(key: unknown): boolean }\n\n/**\n * A transaction's writes to one tree: an entry by its id, null for one removed; `cleared`: nothing under it counts. An\n * index's layer also finds its ids by their record key's bytes (`byValue`): a record written over drops its entries by it.\n */\ninterface Layer { owner: object | null; seq: number; cleared: boolean; puts: Map<string, Entry | null>; byValue: Map<string, Set<string>> }\n\nconst buffer = (bytes: string) => Buffer.from(bytes, \"latin1\");\n\n/**\n * Values just read from rows.db, which nothing else holds: fake-indexeddb copies each value it hands out, and for these\n * the copy is a second one (v8 made the first), 7.6 s of a 30 s profile of sends in a chat of 15,000. A value that goes\n * into a layer leaves the set: from then on the store holds it.\n */\nconst fresh = new WeakSet<object>();\n\nfunction thaw(bytes: Uint8Array): unknown {\n const value = deserialize(bytes);\n if (value && typeof value === \"object\") fresh.add(value);\n return value;\n}\n\n/** A value to hand out: one just read from rows.db as it is, once; any other a copy, as fake-indexeddb makes. */\nexport function handOut(value: unknown): unknown {\n return value && typeof value === \"object\" && fresh.delete(value) ? value : structuredClone(value);\n}\n\n/** rows.db's trees: the stores kept on disk and their indexes, each a tree id in `entries`. */\nexport class TreeFile {\n /** The transaction whose writes come now (its rollback log), set by `storage.ts` around each; null for one without. */\n owner: object | null = null;\n private readonly live = new Set<SqlTree>();\n private readonly get: StatementSync;\n private readonly count: StatementSync;\n private readonly put: StatementSync;\n private readonly remove: StatementSync;\n private readonly empty: StatementSync;\n /** `range`'s page reads by their SQL: a few shapes (direction, bounds), each prepared once. */\n private readonly pages = new Map<string, StatementSync>();\n\n constructor(readonly db: DatabaseSync, readonly constraintError: new () => Error) {\n db.exec(`\n CREATE TABLE IF NOT EXISTS trees (id INTEGER PRIMARY KEY, db TEXT NOT NULL, store TEXT NOT NULL, idx TEXT NOT NULL, UNIQUE (db, store, idx));\n CREATE TABLE IF NOT EXISTS entries (tree INTEGER NOT NULL, id BLOB NOT NULL, pv BLOB, key BLOB NOT NULL, value BLOB NOT NULL, PRIMARY KEY (tree, id)) WITHOUT ROWID;\n CREATE INDEX IF NOT EXISTS entries_by_value ON entries (tree, pv) WHERE pv IS NOT NULL;\n CREATE TABLE IF NOT EXISTS meta (name TEXT PRIMARY KEY, value) WITHOUT ROWID;\n `);\n this.get = db.prepare(\"SELECT key, value FROM entries WHERE tree = ? AND id = ?\");\n this.count = db.prepare(\"SELECT count(*) AS n FROM entries WHERE tree = ?\");\n this.put = db.prepare(\"INSERT OR REPLACE INTO entries (tree, id, pv, key, value) VALUES (?, ?, ?, ?, ?)\");\n this.remove = db.prepare(\"DELETE FROM entries WHERE tree = ? AND id = ?\");\n this.empty = db.prepare(\"DELETE FROM entries WHERE tree = ?\");\n }\n\n /** The tree of a store (`index` \"\") or of one of its indexes, made when it is new. */\n treeId(db: string, store: string, index = \"\"): number {\n const found = this.db.prepare(\"SELECT id FROM trees WHERE db = ? AND store = ? AND idx = ?\").get(db, store, index) as { id: number } | undefined;\n if (found) return Number(found.id);\n return Number(this.db.prepare(\"INSERT INTO trees (db, store, idx) VALUES (?, ?, ?)\").run(db, store, index).lastInsertRowid);\n }\n\n tree(id: number, keysAreUnique: boolean, index: boolean): SqlTree {\n const tree = new SqlTree(this, id, keysAreUnique, index);\n this.live.add(tree);\n return tree;\n }\n\n /** The last journal entry whose writes SQLite holds. */\n applied(): number {\n const row = this.db.prepare(\"SELECT value FROM meta WHERE name = 'applied'\").get() as { value: number } | undefined;\n return row ? Number(row.value) : 0;\n }\n\n /** A finished transaction's layers, numbered as its journal entry: they wait for `apply`. */\n complete(owner: object | null, seq: number): void {\n for (const tree of this.live) tree.complete(owner, seq);\n }\n\n /** An aborted transaction's layers go, and what it wrote with them. */\n abort(owner: object | null): void {\n for (const tree of this.live) tree.abort(owner);\n }\n\n /** The layers of the journal's entries up to `upTo` into SQLite, in one transaction that says so (`applied`). */\n apply(upTo: number): void {\n // An entry that wrote no tree (settings, the event set's notes) has nothing to take: no SQLite write for it.\n const due = [...this.live].filter((tree) => tree.due(upTo));\n if (!due.length) return;\n this.db.exec(\"BEGIN\");\n try {\n // The number of the last entry that wrote here: a replay skips what wrote trees up to it.\n let top = 0;\n for (const tree of due) top = Math.max(top, tree.write(upTo));\n this.db.prepare(\"INSERT OR REPLACE INTO meta (name, value) VALUES ('applied', max(?, coalesce((SELECT value FROM meta WHERE name = 'applied'), 0)))\").run(top);\n this.db.exec(\"COMMIT\");\n } catch (error) { this.db.exec(\"ROLLBACK\"); throw error; }\n // Only once SQLite has them: a write that failed leaves the layers where reads still find them, for the next try.\n for (const tree of due) tree.settle(upTo);\n }\n\n /** Whether a store (`index` \"\") or one of its indexes has its tree here (a snapshot of format 3 names ones that must). */\n has(db: string, store: string, index = \"\"): boolean {\n return this.db.prepare(\"SELECT 1 FROM trees WHERE db = ? AND store = ? AND idx = ?\").get(db, store, index) !== undefined;\n }\n\n /** Everything SQLite holds is on disk: before a snapshot takes the place of the journal that had it. */\n sync(): void {\n this.db.exec(\"PRAGMA wal_checkpoint(TRUNCATE)\");\n }\n\n /** Trees whose store or index is gone from the schema (deleted, a database removed) go, with their entries. */\n sweep(keep: ReadonlySet<number>): void {\n const ids = (this.db.prepare(\"SELECT id FROM trees\").all() as { id: number }[]).map((row) => Number(row.id)).filter((id) => !keep.has(id));\n if (!ids.length) return;\n this.db.exec(\"BEGIN\");\n try {\n for (const id of ids) { this.empty.run(id); this.db.prepare(\"DELETE FROM trees WHERE id = ?\").run(id); }\n this.db.exec(\"COMMIT\");\n } catch (error) { this.db.exec(\"ROLLBACK\"); throw error; }\n for (const tree of this.live) if (!keep.has(tree.id)) this.live.delete(tree);\n }\n\n /** Writes a tree's entries as they are, outside any transaction's layer (moving a store from memory). */\n load(id: number, entries: Iterable<Entry>, index: boolean, keysAreUnique: boolean): void {\n this.empty.run(id);\n for (const entry of entries) this.row(id, entry, index, keysAreUnique);\n }\n\n /** @internal */\n row(id: number, entry: Entry, index: boolean, keysAreUnique: boolean): void {\n const key = keyBytes(entry.key);\n const bytes = keysAreUnique ? key : key + keyBytes(entry.value);\n this.put.run(id, buffer(bytes), index ? buffer(keyBytes(entry.value)) : null, serialize(entry.key), serialize(entry.value));\n }\n /** @internal */\n read(id: number, bytes: string): Entry | undefined {\n const row = this.get.get(id, buffer(bytes)) as { key: Uint8Array; value: Uint8Array } | undefined;\n return row && { key: deserialize(row.key), value: thaw(row.value) };\n }\n /** @internal */\n size(id: number): number { return Number((this.count.get(id) as { n: number }).n); }\n /** @internal */\n drop(id: number, bytes: string): void { this.remove.run(id, buffer(bytes)); }\n /** @internal */\n clearRows(id: number): void { this.empty.run(id); }\n /** @internal The index entries whose record key's bytes are from `lower` up to (not with) `upper`, either one open. */\n withValue(id: number, lower: string | undefined, upper: string | undefined): Entry[] {\n const where = [\"tree = ?\", \"pv IS NOT NULL\"], args: (number | Buffer)[] = [id];\n if (lower !== undefined) { where.push(\"pv >= ?\"); args.push(buffer(lower)); }\n if (upper !== undefined) { where.push(\"pv < ?\"); args.push(buffer(upper)); }\n // Named: with no ANALYZE, SQLite plans this on the primary key's `tree = ?` and reads every entry of the index.\n const sql = `SELECT key, value FROM entries INDEXED BY entries_by_value WHERE ${where.join(\" AND \")}`;\n // Prepared once each (four forms): an overwrite asks this of every index, once per record it writes over.\n let statement = this.pages.get(sql);\n if (!statement) this.pages.set(sql, (statement = this.db.prepare(sql)));\n return (statement.all(...args) as { key: Uint8Array; value: Uint8Array }[])\n .map((row) => ({ key: deserialize(row.key), value: thaw(row.value) }));\n }\n /** @internal */\n /**\n * The entries between two ids, a page at a time: no statement stays open between pages, so a read that takes the\n * first entry and stops (fake-indexeddb's `get`) leaves nothing that would lock the table for a later write.\n */\n *range(id: number, lower: string | undefined, upper: string | undefined, descending: boolean): Generator<[string, Entry]> {\n let from = descending ? upper : lower, first = true, size = 8;\n for (;;) {\n const where = [\"tree = ?\"], args: (number | Buffer)[] = [id];\n const low = descending ? lower : from, high = descending ? from : upper;\n if (low !== undefined) { where.push(!descending && !first ? \"id > ?\" : \"id >= ?\"); args.push(buffer(low)); }\n if (high !== undefined) { where.push(\"id < ?\"); args.push(buffer(high)); }\n args.push(size);\n const sql = `SELECT id, key, value FROM entries WHERE ${where.join(\" AND \")} ORDER BY id ${descending ? \"DESC\" : \"ASC\"} LIMIT ?`;\n let statement = this.pages.get(sql);\n if (!statement) this.pages.set(sql, (statement = this.db.prepare(sql)));\n const rows = statement.all(...args) as { id: Uint8Array; key: Uint8Array; value: Uint8Array }[];\n for (const row of rows) yield [Buffer.from(row.id).toString(\"latin1\"), { key: deserialize(row.key), value: thaw(row.value) }];\n if (rows.length < size) return;\n from = Buffer.from(rows.at(-1)!.id).toString(\"latin1\");\n first = false;\n size = Math.min(size * 4, 512);\n }\n }\n}\n\nexport class SqlTree {\n private pending: Layer[] = [];\n private active: Layer | null = null;\n\n constructor(private readonly file: TreeFile, readonly id: number, readonly _keysAreUnique: boolean, private readonly index: boolean) {}\n\n private idOf(entry: Entry): string {\n const key = keyBytes(entry.key);\n return this._keysAreUnique ? key : key + keyBytes(entry.value);\n }\n\n private layer(): Layer {\n const owner = this.file.owner;\n if (this.active) {\n if (owner && this.active.owner && this.active.owner !== owner) throw new Error(\"Two transactions wrote one store kept on disk at once\");\n if (owner && !this.active.owner) this.active.owner = owner;\n return this.active;\n }\n return (this.active = { owner, seq: 0, cleared: false, puts: new Map(), byValue: new Map() });\n }\n\n /** An entry (or its removal, `entry` null) in this transaction's layer; an index's by its record key too. */\n private set(bytes: string, record: Entry, entry: Entry | null): void {\n if (entry?.value && typeof entry.value === \"object\") fresh.delete(entry.value);\n const layer = this.layer();\n layer.puts.set(bytes, entry);\n if (!this.index) return;\n const value = keyBytes(record.value);\n let ids = layer.byValue.get(value);\n if (!ids) layer.byValue.set(value, (ids = new Set()));\n ids.add(bytes);\n }\n\n /** Newest first: the layer that has the entry says what it is; one cleared hides everything older. */\n private lookup(bytes: string): Entry | null | undefined {\n const layers = this.active ? [...this.pending, this.active] : this.pending;\n for (let at = layers.length - 1; at >= 0; at--) {\n const layer = layers[at]!;\n if (layer.puts.has(bytes)) return layer.puts.get(bytes) ?? null;\n if (layer.cleared) return null;\n }\n return undefined;\n }\n\n size(): number {\n let hidden = false;\n const map = new Map<string, Entry | null>();\n for (const layer of this.active ? [...this.pending, this.active] : this.pending) {\n if (layer.cleared) { hidden = true; map.clear(); }\n for (const [bytes, entry] of layer.puts) map.set(bytes, entry);\n }\n let size = hidden ? 0 : this.file.size(this.id);\n for (const [bytes, entry] of map) {\n const below = !hidden && this.file.read(this.id, bytes) !== undefined;\n if (entry && !below) size++;\n else if (!entry && below) size--;\n }\n return size;\n }\n\n get(record: Entry): Entry | undefined {\n const bytes = this.idOf(record);\n const found = this.lookup(bytes);\n if (found !== undefined) return found ?? undefined;\n return this.file.read(this.id, bytes);\n }\n\n contains(record: Entry): boolean { return this.get(record) !== undefined; }\n\n put(record: Entry, noOverwrite = false): Entry | undefined {\n // A Blob cannot go to SQLite as it is (no message holds one): refused here, at once, not when the layer goes there.\n if (holdsBlob(record.value)) throw new Error(\"A store kept in rows.db takes no Blob\");\n const existing = this.get(record);\n if (existing !== undefined && noOverwrite) throw new this.file.constraintError();\n this.set(this.idOf(record), record, record);\n return existing;\n }\n\n delete(record: Entry): void {\n if (this.get(record) === undefined) return;\n this.set(this.idOf(record), record, null);\n }\n\n clear(): void {\n const layer = this.layer();\n layer.puts.clear();\n layer.byValue.clear();\n layer.cleared = true;\n }\n\n *getAllRecords(descending = false): Generator<Entry> { yield* this.getRecords(undefined, descending); }\n\n *getRecords(range: Range | undefined, descending = false): Generator<Entry> {\n // One key of a store (fake-indexeddb's `get`, before every record a transaction writes over): looked up, not\n // every entry of the layers merged, which made such a transaction quadratic.\n if (this._keysAreUnique && range && onlyBytes(range) !== undefined) {\n const found = this.get({ key: range.lower, value: undefined });\n if (found) yield found;\n return;\n }\n const [lower, upper] = bounds(range);\n let hidden = false;\n const map = new Map<string, Entry | null>();\n for (const layer of this.active ? [...this.pending, this.active] : this.pending) {\n if (layer.cleared) { hidden = true; map.clear(); }\n for (const [bytes, entry] of layer.puts) map.set(bytes, entry);\n }\n const over = [...map].filter(([bytes]) => (lower === undefined || bytes >= lower) && (upper === undefined || bytes < upper)).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));\n if (descending) over.reverse();\n const base = hidden ? undefined : this.file.range(this.id, lower, upper, descending);\n let next = base?.next();\n let at = 0;\n for (;;) {\n const below = next && !next.done ? next.value : undefined;\n const above = over[at];\n if (!below && !above) return;\n const first = above && (!below || (descending ? above[0] >= below[0] : above[0] <= below[0]));\n if (first) {\n if (below && above[0] === below[0]) next = base!.next();\n at++;\n if (above[1]) yield above[1];\n } else {\n yield below![1];\n next = base!.next();\n }\n }\n }\n\n /**\n * The index entries whose record key (`value`) is in `range`, looked up by its bytes, not walked: removing a chat\n * (its messages' keys from [chat] to [chat, []]) read every message of the profile, 1.5 s at 100,000.\n */\n deleteByValue(range: Range): Entry[] {\n const [lower, upper] = bounds(range);\n // One record key (an overwrite's, IDBKeyRange.only): the layers' entries for it looked up, not every one walked.\n const only = onlyBytes(range);\n const candidates = this.index\n ? [...this.file.withValue(this.id, lower, upper), ...this.overlayWithValue(lower, upper, only)]\n : [...this.getAllRecords()];\n const deleted: Entry[] = [], seen = new Set<string>();\n for (const candidate of candidates) {\n const bytes = this.idOf(candidate);\n if (seen.has(bytes) || !range.includes(candidate.value)) continue;\n seen.add(bytes);\n const current = this.get(candidate);\n if (!current) continue;\n this.set(bytes, current, null);\n deleted.push(current);\n }\n return deleted;\n }\n\n /**\n * The entries of the layers whose record key's bytes are between these (as `withValue`), or are `only`'s. A\n * transaction that wrote over k records walked every entry of its layer k times: 16,000 took 6.6 s.\n */\n private overlayWithValue(lower: string | undefined, upper: string | undefined, only?: string): Entry[] {\n const out: Entry[] = [];\n const take = (layer: Layer, ids: Set<string>) => { for (const bytes of ids) { const entry = layer.puts.get(bytes); if (entry) out.push(entry); } };\n for (const layer of this.active ? [...this.pending, this.active] : this.pending) {\n if (only !== undefined) { const ids = layer.byValue.get(only); if (ids) take(layer, ids); continue; }\n for (const [value, ids] of layer.byValue) {\n if ((lower !== undefined && value < lower) || (upper !== undefined && value >= upper)) continue;\n take(layer, ids);\n }\n }\n return out;\n }\n\n /** @internal */\n complete(owner: object | null, seq: number): void {\n if (!this.active || (this.active.owner !== owner && this.active.owner !== null)) return;\n this.active.seq = seq;\n this.pending.push(this.active);\n this.active = null;\n }\n /** @internal */\n abort(owner: object | null): void {\n if (this.active && (this.active.owner === owner || this.active.owner === null)) this.active = null;\n }\n /** @internal */\n due(upTo: number): boolean { return this.pending.length > 0 && this.pending[0]!.seq <= upTo; }\n /** @internal */\n write(upTo: number): number {\n let top = 0;\n for (const layer of this.pending) {\n if (layer.seq > upTo) break;\n top = layer.seq;\n if (layer.cleared) this.file.clearRows(this.id);\n for (const [bytes, entry] of layer.puts) {\n if (entry) this.file.row(this.id, entry, this.index, this._keysAreUnique);\n else this.file.drop(this.id, bytes);\n }\n }\n return top;\n }\n /** @internal */\n settle(upTo: number): void {\n while (this.pending.length && this.pending[0]!.seq <= upTo) this.pending.shift();\n }\n /** Whether a transaction's writes are still waiting here (a snapshot then waits for them). */\n get busy(): boolean { return this.active !== null || this.pending.length > 0; }\n}\n\n/**\n * rows.db, owner-only (SQLite makes its WAL files with the database's mode), in WAL with NORMAL: the journal makes a\n * transaction durable, and `sync` runs before a snapshot takes the journal's place. With `secure_delete` SQLite zeroes\n * what a delete frees: a message or chat deleted is not left readable in the file's free pages after the fold.\n */\nexport function openTreeFile(sqlite: typeof import(\"node:sqlite\"), path: string, constraintError: new () => Error): TreeFile {\n closeSync(openSync(path, \"a\", 0o600));\n const db = new sqlite.DatabaseSync(path);\n db.exec(\"PRAGMA journal_mode = WAL; PRAGMA synchronous = NORMAL; PRAGMA secure_delete = ON\");\n return new TreeFile(db, constraintError);\n}\n\n/** A range's keys as bytes: from the first (in) up to the second (out), undefined where it is open. */\nfunction bounds(range: Range | undefined): [string | undefined, string | undefined] {\n return [\n range?.lower === undefined ? undefined : keyBytes(range.lower) + (range.lowerOpen ? AFTER : \"\"),\n range?.upper === undefined ? undefined : keyBytes(range.upper) + (range.upperOpen ? \"\" : AFTER),\n ];\n}\n\n/** A range of one key (IDBKeyRange.only, or a bound with the same key at both ends, both in): that key's bytes. */\nfunction onlyBytes(range: Range): string | undefined {\n if (range.lower === undefined || range.upper === undefined || range.lowerOpen || range.upperOpen) return undefined;\n const lower = keyBytes(range.lower);\n return keyBytes(range.upper) === lower ? lower : undefined;\n}\n\nfunction holdsBlob(value: unknown, depth = 0): boolean {\n if (value instanceof Blob) return true;\n if (!value || typeof value !== \"object\" || depth > 32 || ArrayBuffer.isView(value) || value instanceof ArrayBuffer) return false;\n if (value instanceof Map) { for (const [k, v] of value) if (holdsBlob(k, depth + 1) || holdsBlob(v, depth + 1)) return true; return false; }\n for (const item of Object.values(value)) if (holdsBlob(item, depth + 1)) return true;\n return false;\n}\n","import { randomBytes } from \"node:crypto\";\nimport { closeSync, existsSync, fstatSync, fsyncSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, statSync, writeSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { deserialize, serialize } from \"node:v8\";\nimport { IDBFactory, IDBKeyRange, IDBTransaction } from \"fake-indexeddb\";\nimport { loadSqlite, openRowFile, rowRecords, type RowFile } from \"./rows\";\nimport { handOut, openTreeFile, SqlTree, type TreeFile } from \"./sqlTree\";\n\n/**\n * IndexedDB for the engine on Node, kept on disk.\n *\n * The engine (and the wallet SDKs under it) speak IndexedDB. fake-indexeddb gives the exact semantics in memory;\n * this module makes it durable with a snapshot and an append-only journal in the profile's folder:\n *\n * - every committed read-write transaction appends its puts, deletes and clears to `journal.bin` and is fsynced\n * before the transaction's `complete` event reaches the engine (a Cashu proof saved is a proof on disk); one opened\n * with `durability: \"relaxed\"` is appended at once and synced with the next one, or within a second;\n * - a schema change (an upgrade, a deleted database) writes a whole new `snapshot.bin` instead;\n * - at start the snapshot is loaded and the journal replayed. A short journal that is whole stays as it is, and so\n * does the snapshot; a long one, or one whose last entry was cut short, is folded into a fresh snapshot first;\n * - a close leaves a short journal too, with a mark at its end that says the store was closed: the rows are on disk\n * already, and writing the whole snapshot again for them cost a second or more of every command on a large profile;\n * - a journal begins by naming the snapshot it follows: one found beside another snapshot is in that snapshot already\n * (the process died between writing the snapshot and removing the journal), and is not replayed over it;\n * - the engine's messages and file states, and their indexes, are kept in `rows.db` (SQLite: ./sqlTree.ts), not in memory: their\n * store's tree and its indexes' are SQL trees in place of fake-indexeddb's, and the snapshot (format 3) says only that\n * they are there. A transaction's writes go to SQLite once the journal has them (each entry is numbered, and SQLite\n * says which it holds: a replay skips those). A profile's snapshot of format 1 (every value inline) or 2 (values in\n * rows) is moved at its first open; a Node without `node:sqlite` keeps format 1.\n *\n * Only one process may hold a profile's store: the caller takes the profile lock first (see `profiles.ts`).\n * fake-indexeddb is pinned: this reaches into its raw classes, and `storage.test.ts` checks them.\n */\n\ntype Key = unknown;\ntype Op =\n | { db: string; store: string; kind: \"put\"; key: Key; value: unknown }\n | { db: string; store: string; kind: \"delete\"; key: Key }\n | { db: string; store: string; kind: \"clear\"; seq?: number };\n\ninterface RawRecord { key: Key; value: unknown }\ninterface RawIndex {\n name: string; rawObjectStore: RawStore; initialize(transaction: unknown): void; keyPath: string | string[]; multiEntry: boolean; unique: boolean; initialized: boolean; deleted: boolean;\n records: RawRecordStore; storeRecord(record: RawRecord): void;\n}\ninterface RawNode { record: RawRecord; left: RawNode | undefined; right: RawNode | undefined; parent: RawNode | undefined; deleted: boolean; red: boolean }\ninterface RawRecordStore {\n values(range?: unknown, direction?: string): Iterable<RawRecord>; clear(): unknown;\n get(key: Key): RawRecord | undefined;\n put(record: RawRecord, noOverwrite?: boolean): RawRecord | undefined;\n delete(key: Key): RawRecord[];\n deleteByValue(key: Key): RawRecord[];\n records: { _root: RawNode | undefined; _numNodes: number; _numTombstones: number; delete(record: RawRecord): void };\n keysAreUnique: boolean;\n}\ninterface RawStore {\n name: string; keyPath: string | string[] | null; autoIncrement: boolean; deleted: boolean;\n keyGenerator: { num: number } | null; rawIndexes: Map<string, RawIndex>; records: RawRecordStore; rawDatabase: RawDatabase;\n storeRecord(record: { key: Key; value: unknown }, noOverwrite: boolean, rollbackLog?: unknown[]): Key;\n deleteRecord(key: Key, rollbackLog?: unknown[]): void;\n clear(rollbackLog?: unknown[]): void;\n getValue(key: Key): unknown;\n getAllValues(range: unknown, count: number | undefined, direction: string | undefined): unknown[];\n}\ninterface RawDatabase { name: string; version: number; rawObjectStores: Map<string, RawStore>; transactions: unknown[] }\ntype RawTransaction = IDBTransaction & { durability?: string; _rollbackLog: unknown[]; _state: string; db: { _rawDatabase: RawDatabase } };\ninterface RawClasses {\n Database: new (name: string, version: number) => RawDatabase;\n ObjectStore: new (db: RawDatabase, name: string, keyPath: string | string[] | null, autoIncrement: boolean) => RawStore;\n Index: new (store: RawStore, name: string, keyPath: string | string[], multiEntry: boolean, unique: boolean) => RawIndex;\n RecordStore: new (keysAreUnique: boolean) => RawRecordStore;\n /** fake-indexeddb's ConstraintError, which a tree throws for a key already there. */\n constraintError: new () => Error;\n}\n\ninterface SnapshotIndex { name: string; keyPath: string | string[]; multiEntry: boolean; unique: boolean }\n/**\n * A store kept on disk (format 2): its records' keys in key order, each one's row (-1 for one kept inline: a value with\n * a Blob), the inline values by position, and each index's entries in its own order, as [index key, record position].\n */\ninterface SnapshotRows { keys: Key[]; ids: number[]; inline: [number, unknown][]; indexes: Record<string, [Key, number][]> }\n/** `onDisk`: its records and its indexes' entries are in rows.db (format 3). */\ninterface SnapshotStore { name: string; keyPath: string | string[] | null; autoIncrement: boolean; keyGenerator: number | null; indexes: SnapshotIndex[]; records: [Key, unknown][]; rows?: SnapshotRows; onDisk?: true }\n/**\n * `journal`: the name a journal that follows this snapshot begins with (absent in one an older version wrote).\n * `format`: 1, every value inline; 2, the stores kept on disk have their values in `rows.db`; 3, their records and index\n * entries too. An older ghostly refuses 2 and 3.\n */\ninterface Snapshot { format: 1 | 2 | 3; journal?: string; databases: { name: string; version: number; stores: SnapshotStore[] }[] }\n\nconst SNAPSHOT = \"snapshot.bin\";\nconst JOURNAL = \"journal.bin\";\nconst ROWS = \"rows.db\";\n/**\n * The stores kept in `rows.db` by default: the engine's messages, and each file's state (its transfer and files/3\n * record), which grew the heap by every file ever sent or received. Not the files store: a file small enough to hold\n * whole is a Blob in its record, which SQLite does not take.\n */\nconst ON_DISK = new Set([\"messages\", \"fileState\"]);\n/** A journal past this is folded into the snapshot at the next quiet moment. */\nconst COMPACT_BYTES = 32 * 1024 * 1024;\n/** A journal up to this is kept at close and at start: replaying it costs less than writing the snapshot again. */\nconst KEEP_BYTES = 1024 * 1024;\n/**\n * The database a journal's close mark names. No profile has it, so a replay skips the mark (an older version's too:\n * it skips an op whose store it does not find).\n */\nconst CLOSED = \"__ghostly_closed__\";\n/** The database a journal's first entry names, with the snapshot the journal follows: skipped by a replay as the mark is. */\nconst FOLLOWS = \"__ghostly_journal__\";\n/** The database an entry's first op names, with the entry's number (`seq`): SQLite's trees hold the entries up to theirs. */\nconst SEQ = \"__ghostly_entry__\";\n/** How long `close` waits for the transactions still running: one left open for good does not keep the process. */\nconst CLOSE_WAIT_MS = 5_000;\n/** How long a relaxed transaction's entry may wait for a sync when no other transaction brings one. */\nconst RELAXED_SYNC_MS = 1_000;\nconst BLOB = \"__ghostlyBlob\";\nconst RANGE = \"__ghostlyRange\";\nconst PROBE = \"__ghostly_storage_probe__\";\n\nexport interface PersistentIndexedDb {\n readonly factory: IDBFactory;\n /** The journal found at open did not end with a close mark: the process before this one did not close the store (killed, a power cut). */\n readonly recovered: boolean;\n /** Waits for writes that carried a Blob (read asynchronously) to reach the disk. */\n flush(): Promise<void>;\n /** Folds the journal into a fresh snapshot. */\n compact(): Promise<void>;\n /** Flushes, marks the journal closed (or folds it, once it is long), and stops writing: the process is about to leave. */\n close(): Promise<void>;\n /** Bytes of the journal now, for tests and `ghostly status`. */\n journalBytes(): number;\n /**\n * The whole store as one snapshot of format 1 (every value inline, `rows.db` read), written to `path`: a backup's\n * store, which any ghostly can restore.\n */\n exportSnapshot(path: string): Promise<void>;\n}\n\nexport interface StoreOptions {\n /** The stores kept in `rows.db`, records and indexes: by default the engine's messages and file states (`ON_DISK`). */\n rowsOnDisk?: (db: string, store: string) => boolean;\n}\n\n/**\n * Opens the store in `dir` (created 0700; files 0600) and installs it as this process's `indexedDB` and\n * `IDBKeyRange`. Call once, before anything touches IndexedDB.\n */\nexport async function openPersistentIndexedDb(dir: string, options: StoreOptions = {}): Promise<PersistentIndexedDb> {\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n const factory = new IDBFactory();\n const raw = factory as unknown as { _databases: Map<string, RawDatabase> };\n const classes = await probeClasses(factory);\n dropIndexEntriesByRecord(classes, (a, b) => factory.cmp(a, b));\n readWithoutCopy(classes);\n const order = keyOrder((a, b) => factory.cmp(a, b));\n\n // Found before it is opened: opening makes it, and a snapshot that names it needs the file it named.\n const rowsPath = join(dir, ROWS);\n const rowsFound = existsSync(rowsPath);\n const sqlite = await loadSqlite();\n const trees: TreeFile | null = sqlite ? openTreeFile(sqlite, rowsPath, classes.constraintError) : null;\n const onDisk = options.rowsOnDisk ?? ((_db: string, store: string) => ON_DISK.has(store));\n // This store's own databases only: the hooks are on fake-indexeddb's shared classes, and another store opened in the\n // process (a test's) must not move this one's records into its rows.db.\n const disk = (store: RawStore) => trees !== null && raw._databases.get(store.rawDatabase.name) === store.rawDatabase && onDisk(store.rawDatabase.name, store.name);\n /** The number of the last journal entry made: SQLite says which ones its trees hold (`TreeFile.applied`). */\n let seq = trees?.applied() ?? 0;\n\n let writing = true;\n let restoring = true;\n let journalFd: number | null = null;\n let queue: Promise<void> = Promise.resolve();\n let queued = 0;\n let schemaDirty = false;\n /** A write to the journal or the snapshot failed: what is held is not all on disk, and only a whole snapshot makes it so. */\n let unsaved = false;\n /** Set while the journal holds relaxed entries no sync has covered yet. */\n let relaxed: ReturnType<typeof setTimeout> | null = null;\n const pending = new WeakMap<unknown[], Op[]>();\n\n const snapshotPath = join(dir, SNAPSHOT);\n const journalPath = join(dir, JOURNAL);\n\n // Load what the disk holds before any connection opens; a snapshot with no journal after it is already what is held.\n const snapshotFound = existsSync(snapshotPath), journalFound = existsSync(journalPath);\n const saved = snapshotFound ? (deserialize(readFileSync(snapshotPath)) as Snapshot) : null;\n // A snapshot of format 2 reads its values from the rows it names, once: they move into the trees at this open.\n const rows: RowFile | null = sqlite && saved?.format === 2 && rowsFound ? openRowFile(sqlite, rowsPath) : null;\n /** Stores kept on disk that a snapshot of format 1 had inline, written straight into rows.db by `restore`. */\n let restoredToDisk = false;\n try {\n if (saved) restoredToDisk = restore(classes, raw._databases, saved, order, { trees, rows, made: rows ? rowRecords(rows) : null, rowsFound, attach: attachTrees, onDisk });\n } catch (error) {\n rows?.close();\n trees?.db.close();\n // Made by this open: a second one must refuse as this one did, not find an empty file and read nothing.\n if (!rowsFound) for (const file of [rowsPath, `${rowsPath}-wal`, `${rowsPath}-shm`]) rmSync(file, { force: true });\n throw error;\n }\n /** The name of the snapshot on disk, which its journal begins with. */\n let snapshotName = saved?.journal;\n let journal = journalFound ? readFileSync(journalPath) : null;\n const named = journal ? follows(journal) : undefined;\n // A journal that names another snapshot is in this one already: replayed, it would put back rows as they were before\n // what came between (an upgrade that rewrote them). The process that left it died while it folded.\n const folded = named !== undefined && snapshotName !== undefined && named !== snapshotName;\n if (folded) { rmSync(journalPath, { force: true }); journal = null; }\n // Trees just written from a snapshot of format 1 hold that snapshot and nothing after it: the whole journal replays.\n const replayed = journal ? replay(raw._databases, journal, order, { applied: restoredToDisk ? 0 : seq, onTrees }) : { end: 0, closed: !folded, seq: 0 };\n // What the journal had for the trees and SQLite did not: written now, numbered as its last entry.\n seq = Math.max(seq, replayed.seq);\n if (trees) { trees.complete(null, seq); trees.apply(seq); }\n // Stores kept on disk still in memory (a snapshot of format 1 or 2, and the journal replayed over it): moved now.\n let moved = restoredToDisk;\n if (trees) for (const db of raw._databases.values()) for (const store of db.rawObjectStores.values()) if (!store.deleted && disk(store) && !onTrees(store)) { toDisk(store); moved = true; }\n restoring = false;\n /** Whether the journal on disk says the store is closed: nothing was written since its mark, or there is none. */\n let marked = replayed.closed;\n // A journal cut short is never appended to: what came after the torn bytes could not be read back. Nor one that\n // does not name this snapshot (an older version's).\n const kept = !!journal && named !== undefined && named === snapshotName && replayed.end === journal.length && journal.length <= KEEP_BYTES;\n const rewrite = (journal && !kept) || !snapshotFound || moved;\n if (rewrite) await writeSnapshot();\n rows?.close();\n // Format 2's rows: in the trees now, and the snapshot that named them is gone.\n if (rows && rewrite) trees!.db.exec(\"DROP TABLE IF EXISTS rows\");\n\n // Record what each read-write transaction does, keyed by its rollback log (one array per transaction).\n const storeProto = classes.ObjectStore.prototype as RawStore;\n const storeRecord = storeProto.storeRecord;\n const deleteRecord = storeProto.deleteRecord;\n const clear = storeProto.clear;\n const opsOf = (log: unknown[]) => {\n let ops = pending.get(log);\n if (!ops) pending.set(log, (ops = []));\n return ops;\n };\n /**\n * A write to a store kept on disk goes to its trees' layer of this transaction (its rollback log names it): a store\n * made since the open (an upgrade) moves to the trees at its first write.\n */\n function owned<T>(store: RawStore, rollbackLog: unknown[] | undefined, work: () => T): T {\n if (!disk(store)) return work();\n if (!onTrees(store)) toDisk(store);\n if (!rollbackLog) return work();\n trees!.owner = rollbackLog;\n try { return work(); } finally { trees!.owner = null; }\n }\n storeProto.storeRecord = function (this: RawStore, record, noOverwrite, rollbackLog) {\n const key = owned(this, rollbackLog, () => storeRecord.call(this, record, noOverwrite, rollbackLog));\n if (rollbackLog && !restoring) opsOf(rollbackLog).push({ db: this.rawDatabase.name, store: this.name, kind: \"put\", key, value: record.value });\n return key;\n };\n storeProto.deleteRecord = function (this: RawStore, key, rollbackLog) {\n owned(this, rollbackLog, () => deleteRecord.call(this, key, rollbackLog));\n if (rollbackLog && !restoring) opsOf(rollbackLog).push({ db: this.rawDatabase.name, store: this.name, kind: \"delete\", key: keyToDisk(key) });\n };\n storeProto.clear = function (this: RawStore, rollbackLog) {\n owned(this, rollbackLog, () => clear.call(this, rollbackLog));\n if (rollbackLog && !restoring) opsOf(rollbackLog).push({ db: this.rawDatabase.name, store: this.name, kind: \"clear\" });\n };\n // An index made on a store kept on disk (an upgrade) is a tree there too, emptied of what an earlier one of its name left.\n const indexProto = classes.Index.prototype as RawIndex;\n const initialize = indexProto.initialize;\n indexProto.initialize = function (this: RawIndex, transaction: unknown) {\n const store = this.rawObjectStore;\n if (disk(store) && onTrees(store) && !(this.records.records instanceof SqlTree)) {\n const tree = trees!.tree(trees!.treeId(store.rawDatabase.name, store.name, this.name), this.records.keysAreUnique, true);\n tree.clear();\n attachTree(this.records, tree, true);\n }\n return initialize.call(this, transaction);\n };\n\n // The transaction's `complete` is where it becomes durable: written before the engine hears of it.\n const txProto = IDBTransaction.prototype as unknown as { dispatchEvent(event: Event): boolean };\n const dispatch = txProto.dispatchEvent;\n txProto.dispatchEvent = function (this: RawTransaction, event: Event) {\n if (event.type === \"complete\" && writing) {\n if (this.mode === \"versionchange\") {\n schemaDirty = true;\n // A schema change goes to a snapshot, not the journal: what it wrote in the trees (an index filled) goes now.\n if (trees) { trees.complete(this._rollbackLog, ++seq); written(seq); }\n } else {\n const ops = pending.get(this._rollbackLog);\n if (ops?.length) {\n const entry = ++seq;\n trees?.complete(this._rollbackLog, entry);\n append(ops, this.durability === \"relaxed\", entry);\n }\n }\n pending.delete(this._rollbackLog);\n if (schemaDirty) void enqueue(writeSnapshot);\n } else if (event.type === \"abort\") {\n pending.delete(this._rollbackLog);\n trees?.abort(this._rollbackLog);\n }\n const dispatched = dispatch.call(this, event);\n // fake-indexeddb keeps every transaction a database ever ran in its list (its scheduler only skips the finished\n // ones), each with its requests, results and rollback log: a daemon's heap grew by about 100 KB a message, without\n // end. Nothing looks for a finished one there again, so it goes once its last event is out.\n if ((event.type === \"complete\" || event.type === \"abort\") && this._state === \"finished\") {\n const list = this.db._rawDatabase.transactions;\n const at = list.indexOf(this);\n if (at >= 0) list.splice(at, 1);\n }\n return dispatched;\n };\n const deleteDatabase = factory.deleteDatabase.bind(factory);\n factory.deleteDatabase = (name: string) => {\n const request = deleteDatabase(name);\n request.addEventListener(\"success\", () => { schemaDirty = true; void enqueue(writeSnapshot); });\n return request;\n };\n\n /** A committed transaction's entry, numbered `entry` (its first op says so, where there are trees). */\n function append(ops: Op[], lazy: boolean, entry: number) {\n if (trees) ops = [{ db: SEQ, store: \"\", kind: \"clear\", seq: entry }, ...ops];\n if (queued === 0 && !ops.some((op) => op.kind === \"put\" && hasBlob(op.value))) {\n if (writeEntry(serialize(ops), lazy)) written(entry);\n return;\n }\n void enqueue(async () => { if (writeEntry(serialize(await Promise.all(ops.map(async (op) => (op.kind === \"put\" ? { ...op, value: await blobsToDisk(op.value) } : op)))), lazy)) written(entry); });\n }\n\n /**\n * The journal has entry `entry`: the trees' layers up to it go to SQLite. One that fails stays in memory, where reads\n * find it, and goes with the next entry, or before the next snapshot (which fails without it).\n */\n function written(entry: number) {\n if (!trees || !writing) return;\n try { trees.apply(entry); } catch (error) { failed(error); }\n }\n\n /** Whether a store's records are in the trees already. */\n function onTrees(store: RawStore): boolean {\n return store.records.records instanceof SqlTree;\n }\n\n /** A tree in place of fake-indexeddb's in a record store; its `clear` and (an index's) `deleteByValue` with it. */\n function attachTree(records: RawRecordStore, tree: SqlTree, index: boolean) {\n (records as unknown as { records: unknown }).records = tree;\n records.clear = () => { const deleted = [...tree.getAllRecords()]; tree.clear(); return deleted; };\n if (index) records.deleteByValue = (key: Key) => tree.deleteByValue((key instanceof IDBKeyRange ? key : IDBKeyRange.only(key)) as never);\n }\n\n /** A store whose records and index entries are in rows.db already (a snapshot of format 3): its trees attached. */\n function attachTrees(store: RawStore) {\n const db = store.rawDatabase.name;\n attachTree(store.records, trees!.tree(trees!.treeId(db, store.name), true, false), false);\n for (const index of store.rawIndexes.values()) if (!index.deleted) attachTree(index.records, trees!.tree(trees!.treeId(db, store.name, index.name), index.records.keysAreUnique, true), true);\n }\n\n /** A store kept on disk, held in memory: its records and index entries written to rows.db, its trees in place. */\n function toDisk(store: RawStore) {\n const db = store.rawDatabase.name;\n const indexes = [...store.rawIndexes.values()].filter((index) => !index.deleted);\n const id = trees!.treeId(db, store.name), ids = indexes.map((index) => trees!.treeId(db, store.name, index.name));\n trees!.db.exec(\"BEGIN\");\n try {\n trees!.load(id, store.records.values(), false, true);\n indexes.forEach((index, at) => trees!.load(ids[at]!, index.records.values(), true, index.records.keysAreUnique));\n trees!.db.exec(\"COMMIT\");\n } catch (error) { trees!.db.exec(\"ROLLBACK\"); throw error; }\n attachTree(store.records, trees!.tree(id, true, false), false);\n indexes.forEach((index, at) => attachTree(index.records, trees!.tree(ids[at]!, index.records.keysAreUnique, true), true));\n }\n\n /** The trees of the stores and indexes the schema has now: the others are swept after a snapshot. */\n function liveTrees(): Set<number> {\n const keep = new Set<number>();\n for (const db of raw._databases.values()) for (const store of db.rawObjectStores.values()) {\n if (store.deleted || !onTrees(store)) continue;\n keep.add((store.records.records as unknown as SqlTree).id);\n for (const index of store.rawIndexes.values()) if (!index.deleted && index.records.records instanceof SqlTree) keep.add((index.records.records as unknown as SqlTree).id);\n }\n return keep;\n }\n\n function enqueue(work: () => Promise<void> | void): Promise<void> {\n queued++;\n queue = queue.then(work).catch(failed).finally(() => { queued--; });\n return queue;\n }\n\n function failed(error: unknown) {\n unsaved = true;\n process.stderr.write(`ghostly: storage write failed: ${error instanceof Error ? error.message : String(error)}\\n`);\n }\n\n /**\n * `lazy`: a relaxed transaction's entry. A sync blocks the whole process for milliseconds, and what such a\n * transaction wrote can be made again (the event set's note of a message it already reported): written, it outlives\n * the process; a power cut may take its last second.\n */\n function writeEntry(entry: Buffer, lazy = false): boolean {\n if (!writing) return false;\n try {\n if (journalFd === null) {\n journalFd = openSync(journalPath, \"a\", 0o600);\n if (snapshotName !== undefined && fstatSync(journalFd).size === 0) writeAll(journalFd, framed(serialize([{ db: FOLLOWS, store: \"\", kind: \"clear\", snapshot: snapshotName }])));\n }\n marked = false;\n writeAll(journalFd, framed(entry));\n } catch (error) {\n // What this entry said is held but not on disk, and what is appended behind a cut entry is not read back.\n unsaved = true;\n throw error;\n }\n if (lazy) relaxed ??= setTimeout(() => { try { sync(); } catch (error) { failed(error); } }, RELAXED_SYNC_MS).unref();\n else sync();\n if (statSync(journalPath).size > COMPACT_BYTES) void enqueue(writeSnapshot);\n return true;\n }\n\n function sync() {\n if (relaxed) { clearTimeout(relaxed); relaxed = null; }\n if (journalFd !== null) fsyncSync(journalFd);\n }\n\n async function writeSnapshot() {\n const dirty = schemaDirty;\n schemaDirty = false;\n try { await snapshotToDisk(); } catch (error) {\n schemaDirty ||= dirty;\n throw error;\n }\n }\n\n /**\n * What is held, as a snapshot. `inline`: every value in it (format 1, `exportSnapshot`); otherwise the stores kept on\n * disk say only that they are (format 3).\n */\n async function capture(journalName: string | undefined, inline: boolean): Promise<Snapshot> {\n const snapshot: Snapshot = { format: 1, ...(journalName !== undefined && { journal: journalName }), databases: [] };\n for (const db of raw._databases.values()) {\n if (db.name === PROBE) continue;\n const stores: SnapshotStore[] = [];\n for (const store of db.rawObjectStores.values()) {\n if (store.deleted) continue;\n const live = [...store.rawIndexes.values()].filter((index) => !index.deleted);\n const meta = {\n name: store.name, keyPath: store.keyPath, autoIncrement: store.autoIncrement, keyGenerator: store.keyGenerator?.num ?? null,\n indexes: live.map(({ name, keyPath, multiEntry, unique }) => ({ name, keyPath, multiEntry, unique })),\n };\n if (!inline && disk(store)) {\n if (!onTrees(store)) toDisk(store);\n stores.push({ ...meta, records: [], onDisk: true });\n snapshot.format = 3;\n continue;\n }\n const records: [Key, unknown][] = [];\n for (const record of store.records.values()) records.push([record.key, await blobsToDisk(record.value)]);\n stores.push({ ...meta, records });\n }\n snapshot.databases.push({ name: db.name, version: db.version, stores });\n }\n return snapshot;\n }\n\n async function snapshotToDisk() {\n const name = randomBytes(8).toString(\"hex\");\n const snapshot = await capture(name, false);\n if (trees) {\n // Every finished transaction goes to the trees, and to the disk, before the snapshot takes the journal's place:\n // also one whose entry waits behind this snapshot in the queue (the snapshot may hold what it did elsewhere). Its\n // entry, written after, is then one SQLite holds, which a replay skips.\n trees.apply(seq);\n trees.sync();\n }\n const temporary = snapshotPath + \".tmp\";\n const fd = openSync(temporary, \"w\", 0o600);\n try { writeAll(fd, serialize(snapshot)); fsyncSync(fd); } finally { closeSync(fd); }\n renameSync(temporary, snapshotPath);\n // Trees of stores and indexes gone from the schema: once no snapshot on disk names them.\n try { trees?.sweep(liveTrees()); } catch (error) { process.stderr.write(`ghostly: storage: old trees not removed: ${error instanceof Error ? error.message : String(error)}\\n`); }\n snapshotName = name;\n unsaved = false;\n // The snapshot holds what the journal's entries said, the relaxed ones too.\n if (relaxed) { clearTimeout(relaxed); relaxed = null; }\n if (journalFd !== null) { closeSync(journalFd); journalFd = null; }\n rmSync(journalPath, { force: true });\n marked = true;\n }\n\n /** What `close` leaves: a short journal with the mark at its end, synced (its relaxed entries too), or a fresh snapshot. */\n async function leave() {\n if (schemaDirty || unsaved || journalBytes() > KEEP_BYTES) return writeSnapshot();\n if (marked) return;\n writeEntry(serialize([{ db: CLOSED, store: \"\", kind: \"clear\" }] satisfies Op[]));\n marked = true;\n }\n\n function journalBytes() {\n return existsSync(journalPath) ? statSync(journalPath).size : 0;\n }\n\n /**\n * Resolves once no transaction is left to run. The engine does not wait for every write (`void db.deleteLink(…)`),\n * and fake-indexeddb runs a transaction on a later turn of the event loop: a command that closed in the turn it wrote\n * in (no chat to say goodbye to) took its last snapshot before the write, and answered done for what was not on disk.\n * Two quiet turns in a row: a write that begins when another ends (a chat's messages after its row) is seen too.\n */\n async function settled() {\n const until = Date.now() + CLOSE_WAIT_MS;\n for (let quiet = 0; quiet < 2 && Date.now() < until; ) {\n await new Promise((resolve) => setImmediate(resolve));\n quiet = busy() ? 0 : quiet + 1;\n }\n }\n\n /** Whether a transaction is running or waiting to. */\n function busy() {\n return [...raw._databases.values()].some((db) => db.transactions.some((transaction) => (transaction as RawTransaction)._state !== \"finished\"));\n }\n\n const installed = globalThis as unknown as Record<string, unknown>;\n installed.indexedDB = factory;\n installed.IDBKeyRange = IDBKeyRange;\n\n return {\n factory,\n recovered: !replayed.closed,\n flush: () => queue,\n async compact() {\n // After the transactions running now: the fold is whole.\n await settled();\n // The caller reads the snapshot next (a backup): a fold that failed is its failure, not a line on stderr.\n let thrown: { error: unknown } | null = null;\n await enqueue(() => writeSnapshot().catch((error: unknown) => { thrown = { error }; throw error; }));\n if (thrown) throw (thrown as { error: unknown }).error;\n },\n async close() {\n // Once: what comes after (a second close) finds the rows closed, and nothing left to write.\n if (!writing) return;\n await settled();\n await enqueue(leave);\n writing = false;\n if (journalFd !== null) { closeSync(journalFd); journalFd = null; }\n trees?.db.close();\n },\n journalBytes,\n async exportSnapshot(path: string) {\n await settled();\n const snapshot = await capture(undefined, true);\n const fd = openSync(path, \"w\", 0o600);\n try { writeAll(fd, serialize(snapshot)); fsyncSync(fd); } finally { closeSync(fd); }\n },\n };\n}\n\n/** Writes every byte: one write may take only a part (a disk nearly full). */\nfunction writeAll(fd: number, bytes: Buffer) {\n for (let at = 0; at < bytes.length; ) at += writeSync(fd, bytes, at);\n}\n\n/** A journal entry as it is on disk: its length, then its bytes. */\nfunction framed(entry: Buffer): Buffer {\n const header = Buffer.alloc(4);\n header.writeUInt32BE(entry.length);\n return Buffer.concat([header, entry]);\n}\n\n/** The snapshot a journal says it follows, in its first entry; none in an older version's journal, or an empty one. */\nfunction follows(journal: Buffer): string | undefined {\n if (journal.length < 4 || 4 + journal.readUInt32BE(0) > journal.length) return undefined;\n try {\n const [op] = deserialize(journal.subarray(4, 4 + journal.readUInt32BE(0))) as (Op & { snapshot?: string })[];\n return op?.db === FOLLOWS ? op.snapshot : undefined;\n } catch { return undefined; }\n}\n\n/** fake-indexeddb's raw classes, from a throwaway database (they are not exported). */\nasync function probeClasses(factory: IDBFactory): Promise<RawClasses> {\n const db = await new Promise<IDBDatabase>((resolve, reject) => {\n const request = factory.open(PROBE, 1);\n request.onupgradeneeded = () => request.result.createObjectStore(\"s\", { autoIncrement: true }).createIndex(\"i\", \"x\");\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n });\n const rawDb = (db as unknown as { _rawDatabase: RawDatabase })._rawDatabase;\n const rawStore = rawDb.rawObjectStores.get(\"s\")!;\n const classes: RawClasses = {\n Database: rawDb.constructor as RawClasses[\"Database\"],\n ObjectStore: rawStore.constructor as RawClasses[\"ObjectStore\"],\n Index: rawStore.rawIndexes.get(\"i\")!.constructor as RawClasses[\"Index\"],\n RecordStore: rawStore.records.constructor as RawClasses[\"RecordStore\"],\n constraintError: Error,\n };\n // The error a tree throws for a put with no overwrite on a key it has: fake-indexeddb's own (not exported).\n const probe = new classes.RecordStore(true);\n probe.put({ key: 1, value: 1 });\n try { probe.put({ key: 1, value: 2 }, true); } catch (error) { classes.constraintError = (error as Error).constructor as new () => Error; }\n db.close();\n await new Promise<void>((resolve) => { const request = factory.deleteDatabase(PROBE); request.onsuccess = request.onerror = () => resolve(); });\n return classes;\n}\n\n/** The classes whose index upkeep is already by record: they are fake-indexeddb's own, shared by every store opened. */\nconst byRecord = new WeakSet<object>();\n\n/**\n * fake-indexeddb drops an overwritten or deleted record from an index by walking every entry of it (an index is in\n * its own key's order, not the record's): each delivery mark, edit or reaction cost time with every message of the\n * profile, 170 ms at 10,000, and a long chat slowed every other one. The store has the records it just replaced or\n * removed in hand, and they say which entries to drop; a call that comes any other way still walks.\n */\nfunction dropIndexEntriesByRecord(classes: RawClasses, cmp: (a: Key, b: Key) => number) {\n const storeProto = classes.ObjectStore.prototype as RawStore;\n const recordsProto = classes.RecordStore.prototype as RawRecordStore;\n if (byRecord.has(storeProto)) return;\n byRecord.add(storeProto);\n const { storeRecord, deleteRecord } = storeProto;\n const { put, delete: remove, deleteByValue } = recordsProto;\n let gone: { indexes: Map<string, RawIndex>; records: RawRecord[] } | null = null;\n\n storeProto.storeRecord = function (this: RawStore, record, noOverwrite, rollbackLog) {\n if (!this.rawIndexes.size) return storeRecord.call(this, record, noOverwrite, rollbackLog);\n const records: Partial<RawRecordStore> = this.records, indexes = this.rawIndexes, outer = gone;\n records.put = (next, keep) => {\n const replaced = put.call(this.records, next, keep);\n gone = { indexes, records: replaced ? [replaced] : [] };\n return replaced;\n };\n try { return storeRecord.call(this, record, noOverwrite, rollbackLog); } finally { delete records.put; gone = outer; }\n };\n storeProto.deleteRecord = function (this: RawStore, key, rollbackLog) {\n if (!this.rawIndexes.size) return deleteRecord.call(this, key, rollbackLog);\n const records: Partial<RawRecordStore> = this.records, indexes = this.rawIndexes, outer = gone;\n records.delete = (range) => {\n const removed = remove.call(this.records, range);\n gone = { indexes, records: removed };\n return removed;\n };\n try { deleteRecord.call(this, key, rollbackLog); } finally { delete records.delete; gone = outer; }\n };\n recordsProto.deleteByValue = function (this: RawRecordStore, key) {\n let index: RawIndex | undefined;\n if (gone) for (const candidate of gone.indexes.values()) if (candidate.records === this) index = candidate;\n if (!gone || !index) return deleteByValue.call(this, key);\n return dropEntries(index, gone.records, cmp);\n };\n}\n\n/** The classes whose reads of a store kept on disk hand out what rows.db gave: shared by every store opened. */\nconst uncopied = new WeakSet<object>();\n\n/**\n * fake-indexeddb copies every value a read hands out (`structuredClone`), as it must for its tree, which keeps the\n * values. A store kept on disk read its values from rows.db just now (v8 made them), and copying them again made a\n * whole-chat read 1.6 times the cost of one in memory. Its `get` and `getAll`, and an index's (which read through the\n * store's `getValue`), hand those out as they are (`handOut`).\n */\nfunction readWithoutCopy(classes: RawClasses) {\n const storeProto = classes.ObjectStore.prototype as RawStore;\n if (uncopied.has(storeProto)) return;\n uncopied.add(storeProto);\n const { getValue, getAllValues } = storeProto;\n storeProto.getValue = function (this: RawStore, key) {\n if (!(this.records.records instanceof SqlTree)) return getValue.call(this, key);\n const record = this.records.get(key);\n return record === undefined ? undefined : handOut(record.value);\n };\n storeProto.getAllValues = function (this: RawStore, range, count, direction) {\n if (!(this.records.records instanceof SqlTree)) return getAllValues.call(this, range, count, direction);\n const values: unknown[] = [], most = count || Infinity;\n for (const record of this.records.values(range, direction)) {\n values.push(handOut(record.value));\n if (values.length >= most) break;\n }\n return values;\n };\n}\n\n/** Drops from an index the entries of records that left its store, and gives back the ones it dropped. */\nfunction dropEntries(index: RawIndex, records: RawRecord[], cmp: (a: Key, b: Key) => number): RawRecord[] {\n const dropped: RawRecord[] = [];\n for (const entry of indexEntries(index, records, cmp)) {\n // A unique index finds an entry by its key alone: it may be another record's (a put the index refused).\n if (index.records.keysAreUnique) {\n const held = index.records.get(entry.key);\n if (!held || cmp(held.value, entry.value) !== 0) continue;\n }\n index.records.records.delete(entry);\n dropped.push(entry);\n }\n return dropped;\n}\n\n/**\n * `cmp` for keys that are stored already (a record's, an index entry's): numbers, strings and arrays of them are\n * ordered here, as IndexedDB orders them (a number before a string before an array; strings by code unit; arrays item\n * by item, then the shorter first). Anything else (a date, bytes, a value that is no key) is left to `cmp`, which\n * checks and copies both keys at every call: on the way back from a snapshot that was a third of a large store's time.\n */\nexport function keyOrder(cmp: (a: Key, b: Key) => number): (a: Key, b: Key) => number {\n const order = (a: Key, b: Key): number => {\n const first = typeof a, second = typeof b;\n if (first === \"number\" && second === \"number\") return a === b ? 0 : (a as number) > (b as number) ? 1 : (a as number) < (b as number) ? -1 : cmp(a, b);\n if (first === \"string\" && second === \"string\") return a === b ? 0 : (a as string) > (b as string) ? 1 : -1;\n if (first === \"number\" && second === \"string\") return -1;\n if (first === \"string\" && second === \"number\") return 1;\n if (Array.isArray(a)) {\n if (second === \"number\" || second === \"string\") return 1;\n if (!Array.isArray(b)) return cmp(a, b);\n const length = Math.min(a.length, b.length);\n for (let at = 0; at < length; at++) {\n const item = order(a[at], b[at]);\n if (item !== 0) return item;\n }\n return a.length === b.length ? 0 : a.length > b.length ? 1 : -1;\n }\n if (Array.isArray(b) && (first === \"number\" || first === \"string\")) return -1;\n return cmp(a, b);\n };\n return order;\n}\n\n/**\n * The snapshot's records come in key order (as `writeSnapshot` walks them), so each store's tree and each index's are\n * built whole from sorted arrays: storing record by record compared every key down the tree, a 70,000-message profile\n * took seconds. A snapshot not in order (never written so) is stored record by record.\n */\ninterface RestoreDisk {\n trees: TreeFile | null; rows: RowFile | null; made: ReturnType<typeof rowRecords> | null; rowsFound: boolean; attach: (store: RawStore) => void;\n /** The stores kept in rows.db: one a snapshot of format 1 has inline is written there at once, never into memory. */\n onDisk: (db: string, store: string) => boolean;\n}\n\n/** Whether it wrote a store of format 1 into rows.db (the snapshot is to be written again, of format 3). */\nfunction restore(classes: RawClasses, databases: Map<string, RawDatabase>, snapshot: Snapshot, cmp: (a: Key, b: Key) => number, disk: RestoreDisk): boolean {\n if (snapshot?.format !== 1 && snapshot?.format !== 2 && snapshot?.format !== 3) throw new Error(\"This profile was last used by a newer version of ghostly (its store has a format this version does not read). Update ghostly to open it.\");\n if (snapshot.format !== 1 && !disk.trees) throw new Error(`This profile keeps its messages in rows.db, which needs Node 22.13 or newer (this is ${process.version}). Update Node to open it.`);\n // Gone, or without what the snapshot names (made empty by an open that failed): the same, and never read as no messages.\n const missing = new Error(\"This profile's store is missing rows.db (beside snapshot.bin): its messages cannot be read. Restore the profile from a backup.\");\n const named = snapshot.format === 2 && snapshot.databases.some((db) => db.stores.some((store) => store.rows?.ids.some((id) => id >= 0)));\n if (snapshot.format === 2 && (!disk.rowsFound || !disk.rows || (named && disk.rows.count() === 0))) throw missing;\n const kept = (db: string, store: SnapshotStore) => disk.trees!.has(db, store.name) && store.indexes.every((index) => disk.trees!.has(db, store.name, index.name));\n if (snapshot.format === 3 && (!disk.rowsFound || snapshot.databases.some((db) => db.stores.some((store) => store.onDisk && !kept(db.name, store))))) throw missing;\n const intern = interner();\n let written = false;\n for (const saved of snapshot.databases) {\n const db = new classes.Database(saved.name, saved.version);\n for (const s of saved.stores) {\n const store = new classes.ObjectStore(db, s.name, s.keyPath, s.autoIncrement);\n if (store.keyGenerator && s.keyGenerator !== null) store.keyGenerator.num = s.keyGenerator;\n for (const i of s.indexes) {\n const index = new classes.Index(store, i.name, i.keyPath, i.multiEntry, i.unique);\n index.initialized = true;\n store.rawIndexes.set(i.name, index);\n }\n db.rawObjectStores.set(s.name, store);\n if (s.onDisk) { disk.attach(store); continue; }\n if (s.rows) {\n const kept = s.rows;\n const records: RawRecord[] = kept.keys.map((key, at) => (kept.ids[at] >= 0 ? disk.made!.make(intern(key), kept.ids[at]) : { key: intern(key), value: undefined }));\n for (const [at, value] of kept.inline) records[at] = { key: records[at].key, value: blobsFromDisk(value) };\n fill(store.records, records);\n for (const index of store.rawIndexes.values()) {\n const entries = kept.indexes[index.name];\n fill(index.records, entries ? entries.map(([key, at]): RawRecord => ({ key: intern(key), value: records[at].key })) : indexEntries(index, records, cmp));\n }\n continue;\n }\n const ordered = s.records.every(([key], at) => at === 0 || cmp(s.records[at - 1][0], key) < 0);\n if (ordered && disk.trees && disk.onDisk(saved.name, s.name)) {\n recordsToDisk(disk.trees, saved.name, store, s.records, cmp);\n s.records = [];\n disk.attach(store);\n written = true;\n continue;\n }\n const records = s.records.map(([key, value]): RawRecord => ({ key, value: blobsFromDisk(value) }));\n if (!ordered) {\n for (const record of records) store.storeRecord(record, false);\n continue;\n }\n fill(store.records, records);\n for (const index of store.rawIndexes.values()) fill(index.records, indexEntries(index, records, cmp));\n }\n databases.set(saved.name, db);\n }\n return written;\n}\n\n/** How many of a snapshot's records go to rows.db at a time, each with its index entries. */\nconst MOVE_BATCH = 2048;\n\n/**\n * A store of a snapshot of format 1 that is kept on disk, written into rows.db from the snapshot's records (in key\n * order), a batch at a time with its index entries, in one SQLite transaction: building fake-indexeddb's trees first and\n * copying them out held every message in those trees beside the snapshot at the update's first start (1.1 GB at\n * 175,000, where the released start took 720 MB). Each batch's values are let go of in the snapshot once written.\n */\nfunction recordsToDisk(trees: TreeFile, db: string, store: RawStore, saved: [Key, unknown][], cmp: (a: Key, b: Key) => number) {\n const indexes = [...store.rawIndexes.values()];\n const id = trees.treeId(db, store.name), ids = indexes.map((index) => trees.treeId(db, store.name, index.name));\n trees.db.exec(\"BEGIN\");\n try {\n for (const tree of [id, ...ids]) trees.clearRows(tree);\n for (let from = 0; from < saved.length; from += MOVE_BATCH) {\n const batch = saved.slice(from, from + MOVE_BATCH).map(([key, value]): RawRecord => ({ key, value: blobsFromDisk(value) }));\n saved.fill(undefined as never, from, from + MOVE_BATCH);\n for (const record of batch) trees.row(id, record, false, true);\n indexes.forEach((index, at) => { for (const entry of indexEntries(index, batch, cmp)) trees.row(ids[at]!, entry, true, index.records.keysAreUnique); });\n }\n trees.db.exec(\"COMMIT\");\n } catch (error) { trees.db.exec(\"ROLLBACK\"); throw error; }\n}\n\n/**\n * One string for every copy of a chat's id among the keys (a message's key and its index entries each name its chat):\n * a snapshot read back gives each its own.\n */\nfunction interner(): (key: Key) => Key {\n const pool = new Map<string, string>();\n const one = (text: string) => { const held = pool.get(text); if (held !== undefined) return held; pool.set(text, text); return text; };\n return (key) => {\n if (typeof key === \"string\") return key.length <= 64 ? one(key) : key;\n if (Array.isArray(key) && typeof key[0] === \"string\" && key[0].length <= 64) key[0] = one(key[0]);\n return key;\n };\n}\n\n/**\n * An index's entries for records in key order, sorted by the index's key: the index derives them itself (key path,\n * multi-entry, invalid keys skipped) into a list instead of its tree. A stable sort keeps equal index keys in the\n * records' key order, the tree's own tie-break.\n */\nfunction indexEntries(index: RawIndex, records: RawRecord[], cmp: (a: Key, b: Key) => number): RawRecord[] {\n const entries: RawRecord[] = [];\n const tree = index.records;\n index.records = { get: () => undefined, put: (entry: RawRecord) => { entries.push(entry); } } as unknown as RawRecordStore;\n try {\n for (const record of records) if (!absent(index.keyPath, record.value)) index.storeRecord(record);\n } finally { index.records = tree; }\n return entries.sort((a, b) => cmp(a.key, b.key));\n}\n\n/**\n * Whether a key path surely finds nothing in a value: a plain object along it lacks the step (most messages have no\n * `card` for the card index). The index would throw and catch a DataError for each of those; anything else it judges.\n */\nfunction absent(keyPath: string | string[], value: unknown): boolean {\n if (typeof keyPath !== \"string\" || keyPath === \"\") return false;\n let object = value;\n for (const step of keyPath.split(\".\")) {\n if (object === null || typeof object !== \"object\" || Array.isArray(object) || object instanceof Blob) return false;\n if (!Object.hasOwn(object, step)) return true;\n object = (object as Record<string, unknown>)[step];\n }\n return false;\n}\n\n/**\n * fake-indexeddb's red-black tree, built balanced from sorted records: every level full but the last, whose nodes\n * are red, so each path holds the same number of black nodes and later inserts rebalance as usual.\n */\nfunction fill(store: RawRecordStore, sorted: RawRecord[]) {\n const deepest = sorted.length ? Math.floor(Math.log2(sorted.length)) : 0;\n const build = (low: number, high: number, parent: RawNode | undefined, depth: number): RawNode | undefined => {\n if (low > high) return undefined;\n const middle = (low + high) >>> 1;\n const node: RawNode = { record: sorted[middle], left: undefined, right: undefined, parent, deleted: false, red: depth > 0 && depth === deepest };\n node.left = build(low, middle - 1, node, depth + 1);\n node.right = build(middle + 1, high, node, depth + 1);\n return node;\n };\n store.records._root = build(0, sorted.length - 1, undefined, 0);\n store.records._numNodes = sorted.length;\n store.records._numTombstones = 0;\n}\n\n/**\n * What a journal's replay changed in a store: its indexes, set aside, the records that left it (the ones the indexes\n * know; `null` once it was cleared: all of them) and the records that came and stayed.\n */\ninterface Touched { indexes: Map<string, RawIndex>; was: Set<RawRecord> | null; now: Set<RawRecord> }\n\n/** Where the journal's whole entries end, whether the last thing in it is a close mark, and its last entry's number. */\ninterface Replayed { end: number; closed: boolean; seq: number }\n\n/** The trees' part of a replay: entries up to `applied` are in SQLite already, and a store on trees keeps its indexes. */\ninterface ReplayTrees { applied: number; onTrees: (store: RawStore) => boolean }\n\n/**\n * Replays whole entries; a torn last entry (the process died mid-write) was never acknowledged and is dropped.\n *\n * A store's indexes are set aside while its ops replay and brought in step once at the end: fake-indexeddb drops an\n * overwritten or deleted record from an index by walking every record of it, so a crash after a busy chat took\n * tens of seconds to minutes to start, one write at a time against the whole profile. Only the rows the journal\n * touched change in an index (what each was before goes, what it is now comes in): a journal of one message costs one\n * message, not the profile's history once per index. A store the journal cleared has its indexes built again whole.\n */\nfunction replay(databases: Map<string, RawDatabase>, journal: Buffer, cmp: (a: Key, b: Key) => number, trees: ReplayTrees): Replayed {\n const touched = new Map<RawStore, Touched>();\n try { return replayEntries(databases, journal, touched, trees); } finally {\n for (const [store, { indexes, was, now }] of touched) {\n const records: Partial<RawRecordStore> = store.records;\n delete records.put;\n delete records.delete;\n store.rawIndexes = indexes;\n for (const index of indexes.values()) {\n if (!was) index.records.clear();\n if (!index.initialized) continue;\n if (was) dropEntries(index, [...was], cmp);\n for (const record of was ? now : store.records.values()) if (!absent(index.keyPath, record.value)) index.storeRecord(record);\n }\n }\n }\n}\n\nfunction replayEntries(databases: Map<string, RawDatabase>, journal: Buffer, touched: Map<RawStore, Touched>, trees: ReplayTrees): Replayed {\n let offset = 0, closed = false, last = 0;\n while (offset + 4 <= journal.length) {\n const length = journal.readUInt32BE(offset);\n if (offset + 4 + length > journal.length) break;\n let ops: Op[];\n try { ops = deserialize(journal.subarray(offset + 4, offset + 4 + length)) as Op[]; } catch { break; }\n offset += 4 + length;\n closed = ops.some((op) => op.db === CLOSED);\n const marker = ops.find((op) => op.db === SEQ);\n const entry = marker?.kind === \"clear\" ? marker.seq : undefined;\n if (entry !== undefined) last = Math.max(last, entry);\n for (const op of ops) {\n const store = databases.get(op.db)?.rawObjectStores.get(op.store);\n if (!store) continue;\n const onTrees = trees.onTrees(store);\n // In SQLite already: the trees took this entry before the process stopped.\n if (onTrees && entry !== undefined && entry <= trees.applied) continue;\n // A store on trees keeps its indexes in step as it goes: each write costs a few rows, never a walk.\n let change = onTrees ? undefined : touched.get(store);\n if (!onTrees && !change) touched.set(store, (change = watch(store)));\n if (op.kind === \"put\") store.storeRecord({ key: op.key, value: blobsFromDisk(op.value) }, false);\n else if (op.kind === \"delete\") store.deleteRecord(keyFromDisk(op.key));\n else {\n if (change) change.was = null;\n store.clear();\n }\n }\n }\n return { end: offset, closed: closed && offset === journal.length, seq: last };\n}\n\n/**\n * Sets a store's indexes aside and notes, as its records are written over and removed, which ones left and which\n * ones came: the record store hands back what a put replaced and what a delete removed, so a row costs no lookup.\n * A record that came and left again was never in an index.\n */\nfunction watch(store: RawStore): Touched {\n const change: Touched = { indexes: store.rawIndexes, was: store.rawIndexes.size ? new Set() : null, now: new Set() };\n store.rawIndexes = new Map();\n if (!change.was) return change;\n const { put, delete: remove } = store.records;\n const left = (record: RawRecord) => { if (!change.now.delete(record)) change.was?.add(record); };\n store.records.put = (next, keep) => {\n const replaced = put.call(store.records, next, keep);\n if (replaced) left(replaced);\n change.now.add(next);\n return replaced;\n };\n store.records.delete = (key) => {\n const removed = remove.call(store.records, key);\n removed.forEach(left);\n return removed;\n };\n return change;\n}\n\nfunction keyToDisk(key: Key): Key {\n if (key instanceof IDBKeyRange) return { [RANGE]: { lower: key.lower, upper: key.upper, lowerOpen: key.lowerOpen, upperOpen: key.upperOpen } };\n return key;\n}\nfunction keyFromDisk(key: Key): Key {\n const range = (key as Record<string, { lower: unknown; upper: unknown; lowerOpen: boolean; upperOpen: boolean }> | null)?.[RANGE];\n if (!range) return key;\n if (range.lower === undefined) return IDBKeyRange.upperBound(range.upper, range.upperOpen);\n if (range.upper === undefined) return IDBKeyRange.lowerBound(range.lower, range.lowerOpen);\n return IDBKeyRange.bound(range.lower, range.upper, range.lowerOpen, range.upperOpen);\n}\n\nfunction hasBlob(value: unknown, depth = 0): boolean {\n if (value instanceof Blob) return true;\n if (!value || typeof value !== \"object\" || depth > 32 || ArrayBuffer.isView(value) || value instanceof ArrayBuffer) return false;\n if (value instanceof Map) { for (const [k, v] of value) if (hasBlob(k, depth + 1) || hasBlob(v, depth + 1)) return true; return false; }\n for (const item of Object.values(value)) if (hasBlob(item, depth + 1)) return true;\n return false;\n}\n\n/** v8 cannot serialize a Blob: it goes to disk as its bytes and type, and comes back a Blob. */\nasync function blobsToDisk(value: unknown, depth = 0): Promise<unknown> {\n if (!hasBlob(value, depth)) return value;\n if (value instanceof Blob) return { [BLOB]: { type: value.type, bytes: new Uint8Array(await value.arrayBuffer()) } };\n if (Array.isArray(value)) return Promise.all(value.map((item) => blobsToDisk(item, depth + 1)));\n if (value instanceof Map) return new Map(await Promise.all([...value].map(async ([k, v]) => [k, await blobsToDisk(v, depth + 1)] as const)));\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value as object)) out[k] = await blobsToDisk(v, depth + 1);\n return out;\n}\nfunction blobsFromDisk(value: unknown, depth = 0): unknown {\n if (!value || typeof value !== \"object\" || depth > 32 || ArrayBuffer.isView(value) || value instanceof ArrayBuffer) return value;\n const blob = (value as Record<string, { type: string; bytes: Uint8Array }>)[BLOB];\n if (blob) return new Blob([blob.bytes as BlobPart], { type: blob.type });\n if (Array.isArray(value)) return value.map((item) => blobsFromDisk(item, depth + 1));\n if (value instanceof Map) return new Map([...value].map(([k, v]) => [k, blobsFromDisk(v, depth + 1)]));\n if (Object.getPrototypeOf(value) !== Object.prototype) return value;\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value)) out[k] = blobsFromDisk(v, depth + 1);\n return out;\n}\n","import { appendFileSync, existsSync, readFileSync, rmSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { fromBase64Url, RTC_CONFIG, setLinkTraceSink } from \"@ghostly/core\";\nimport { irohRelayProblem } from \"@ghostly/browser/platform/irohWeb\";\nimport type { EngineServer } from \"@ghostly/browser/engine/server\";\nimport { loadCallStack } from \"../calls/media\";\nimport { CliError } from \"../errors\";\nimport { joinLogLine } from \"../joinLog\";\nimport { nodeLocalFetch } from \"../services\";\nimport { nodeFedimintSdk } from \"./fedimint\";\nimport { installFileFetch } from \"./fileFetch\";\nimport { nodePushSend } from \"./pushSend\";\nimport { openPersistentIndexedDb, type PersistentIndexedDb } from \"./storage\";\nimport { RESTORED_MARK, type ProfilePaths } from \"../profiles\";\nimport { servedApps } from \"../appServe\";\n\n/** What runs a profile: its store, the engine and the host around it. */\nexport interface Runtime {\n readonly paths: ProfilePaths;\n /** WebRTC runs here (node-datachannel loaded). */\n readonly webrtc: boolean;\n /** Why voice calls cannot run here, or null when they can (node-datachannel's media and Opus loaded). */\n readonly callsUnavailable: string | null;\n /** apps/1 is offered to contacts: the profile served an app when this engine started (src/appServe.ts). */\n readonly apps: boolean;\n readonly server: EngineServer;\n readonly store: PersistentIndexedDb;\n /** Says goodbye to peers, then folds the store. */\n close(): Promise<void>;\n}\n\n/**\n * The HyperDHT network: `GHOSTLY_HYPERDHT_BOOTSTRAP` (\"host:port,…\") replaces the public bootstrap nodes, as it does\n * for the Desktop's sidecar; a network all on loopback (the tests') is announced on loopback too.\n */\nexport function hyperdhtNetwork(env = process.env.GHOSTLY_HYPERDHT_BOOTSTRAP): { bootstrap?: string[]; host?: string } {\n const bootstrap = (env ?? \"\").split(\",\").map((node) => node.trim()).filter(Boolean);\n if (!bootstrap.length) return {};\n const loopback = bootstrap.every((node) => /^(127\\.0\\.0\\.1|localhost):\\d+$/.test(node));\n return { bootstrap, ...(loopback ? { host: \"127.0.0.1\" } : {}) };\n}\n\n/**\n * The Pkarr relays of a private network or a test, `GHOSTLY_PKARR_RELAYS` (\"http://…,…\"), the Desktop's name for it:\n * the only relays this process uses, whatever the profile's `relays` setting says, from its very first publish. Null\n * when unset.\n */\nexport function pkarrRelays(env = process.env): string[] | null {\n const relays = (env.GHOSTLY_PKARR_RELAYS ?? \"\").split(\",\").map((url) => url.trim()).filter(Boolean);\n if (!relays.length) return null;\n const bad = relays.find((url) => !/^https?:\\/\\/[^/\\s]/i.test(url));\n if (bad) throw new CliError(\"usage\", `GHOSTLY_PKARR_RELAYS takes http:// or https:// URLs, comma-separated, not ${JSON.stringify(bad)}`);\n return relays;\n}\n\n/**\n * The Iroh relays of a private network or a test, `GHOSTLY_IROH_RELAYS` (\"https://…,…\"; http:// on loopback): the only\n * relays this process's Iroh homes on, whatever the profile's `irohRelays` setting says. Null when unset (the setting,\n * else n0's public relays).\n */\nexport function irohRelays(env = process.env): string[] | null {\n const relays = (env.GHOSTLY_IROH_RELAYS ?? \"\").split(\",\").map((url) => url.trim()).filter(Boolean);\n if (!relays.length) return null;\n const bad = relays.map((url) => irohRelayProblem(url)).find(Boolean);\n if (bad) throw new CliError(\"usage\", `GHOSTLY_IROH_RELAYS: ${bad}`);\n return relays;\n}\n\n/**\n * The apps' public STUN servers (Google's, `RTC_CONFIG`), which every WebRTC connection asks for its public address:\n * `GHOSTLY_STUN=0` leaves them out (a private network, or tests on one machine, where host candidates connect). The\n * profile's own `iceServers` stay.\n */\nexport function publicStun(env = process.env): boolean { return env.GHOSTLY_STUN !== \"0\"; }\n\n/** An RTC configuration without the apps' public STUN servers. */\nexport function withoutPublicStun(config: RTCConfiguration | undefined): RTCConfiguration | undefined {\n if (!config?.iceServers) return config;\n const ours = new Set((RTC_CONFIG.iceServers ?? []).flatMap((server) => [server.urls].flat()));\n const iceServers = config.iceServers\n .map((server) => ({ ...server, urls: [server.urls].flat().filter((url) => !ours.has(url)) }))\n .filter((server) => server.urls.length);\n return { ...config, iceServers };\n}\n\n/**\n * The Mainline DHT, for Pkarr beside the relays (`RelaysAndDht`): `GHOSTLY_DHT=0` leaves it out (relays only, as the web\n * app), `GHOSTLY_DHT_BOOTSTRAP` (\"host:port,…\") replaces the public routers (a testnet; one all on loopback binds there).\n * `GHOSTLY_PKARR_RELAYS` without a bootstrap leaves it out too, as on the Desktop: a private network stays off the\n * public DHT.\n */\nexport function mainlineNetwork(env = process.env): { off: true } | { off: false; bootstrap?: string[]; host?: string } {\n if (env.GHOSTLY_DHT === \"0\") return { off: true };\n const bootstrap = (env.GHOSTLY_DHT_BOOTSTRAP ?? \"\").split(\",\").map((node) => node.trim()).filter(Boolean);\n if (!bootstrap.length && pkarrRelays(env)) return { off: true };\n if (!bootstrap.length) return { off: false };\n const loopback = bootstrap.every((node) => /^(127\\.0\\.0\\.1|localhost):\\d+$/.test(node));\n return { off: false, bootstrap, ...(loopback ? { host: \"127.0.0.1\" } : {}) };\n}\n\n/**\n * WebRTC for the engine (WISP 101): libdatachannel through node-datachannel's W3C polyfill. Chats go live over it with\n * browsers directly, and groups need it (their links use WebRTC alone). A native module: without it, or with\n * `GHOSTLY_WEBRTC=0`, the CLI runs without WebRTC and says so.\n */\nasync function installWebRtc(): Promise<boolean> {\n if (process.env.GHOSTLY_WEBRTC === \"0\") return false;\n try {\n const polyfill = await import(\"node-datachannel/polyfill\");\n const scope = globalThis as unknown as Record<string, unknown>;\n for (const name of [\"RTCPeerConnection\", \"RTCSessionDescription\", \"RTCIceCandidate\", \"RTCDataChannel\", \"RTCDataChannelEvent\", \"RTCPeerConnectionIceEvent\", \"RTCCertificate\"] as const) {\n scope[name] ??= (polyfill as unknown as Record<string, unknown>)[name];\n }\n refuseDataAnswers(scope);\n if (!publicStun()) leaveOutPublicStun(scope);\n return true;\n } catch (error) {\n process.stderr.write(`ghostly: WebRTC is unavailable (${error instanceof Error ? error.message : String(error)}); chats use HyperDHT, Iroh or the DHT\\n`);\n return false;\n }\n}\n\n/**\n * For tests: `GHOSTLY_TEST_REFUSE_DATA_ANSWERS=<n>` refuses one in every n answers this process applies to its offers\n * (the first, the n+1st, and so on), as libdatachannel 0.24.5 refuses one in its race (`ANSWER_REFUSED_REDIALS` in\n * packages/core/src/datalink.ts): the connection closes, and setRemoteDescription throws.\n */\nfunction refuseDataAnswers(scope: Record<string, unknown>): void {\n const every = Number(process.env.GHOSTLY_TEST_REFUSE_DATA_ANSWERS) || 0;\n if (every <= 0) return;\n let answers = 0;\n const Base = scope.RTCPeerConnection as typeof RTCPeerConnection;\n scope.RTCPeerConnection = class extends Base {\n override async setRemoteDescription(description: RTCSessionDescriptionInit): Promise<void> {\n if (description.type === \"answer\" && answers++ % every === 0) {\n this.close();\n throw new Error(\"libdatachannel error while adding remote description: Got a remote candidate without ICE transport (GHOSTLY_TEST_REFUSE_DATA_ANSWERS)\");\n }\n return super.setRemoteDescription(description);\n }\n };\n}\n\n/** `GHOSTLY_STUN=0`: every connection the engine makes leaves the apps' public STUN servers out of its configuration. */\nfunction leaveOutPublicStun(scope: Record<string, unknown>): void {\n const Base = scope.RTCPeerConnection as typeof RTCPeerConnection;\n scope.RTCPeerConnection = class extends Base {\n constructor(config?: RTCConfiguration) { super(withoutPublicStun(config)); }\n };\n}\n\n/** Server-sent events, which browsers have and Node does not: Arkade follows its server with them. */\nasync function installEventSource(): Promise<void> {\n const scope = globalThis as unknown as Record<string, unknown>;\n if (scope.EventSource) return;\n scope.EventSource = (await import(\"eventsource\")).EventSource;\n}\n\n/**\n * Arkade's descriptor library is CommonJS and `require`s `@scure/bip32`, which is ESM. When the engine's own imports\n * are still evaluating that module, Node's require(esm) hands the library a namespace without `HDKey` yet (\"reading\n * 'fromMasterSeed'\"). Loading it whole first avoids the race.\n */\nasync function preloadWalletModules(): Promise<void> {\n await import(\"@scure/bip32\").catch(() => {});\n await import(\"@bitcoinerlab/descriptors-scure\").catch(() => {});\n}\n\n/** Iroh's wasm, from next to the bundle (a package) or from the workspace (development and tests). */\nfunction irohWasmBytes(): Buffer {\n // Beside this chunk, wherever the bundler put it (the entry, or assets/); else the workspace's.\n for (const place of [\"./ghostly_iroh_web_bg.wasm\", \"./assets/ghostly_iroh_web_bg.wasm\"]) {\n const path = fileURLToPath(new URL(place, import.meta.url));\n if (existsSync(path)) return readFileSync(path);\n }\n return readFileSync(createRequire(import.meta.url).resolve(\"@ghostly/iroh-web/wasm\"));\n}\n\n/**\n * How a runtime starts: `deferGroups` leaves the groups' sessions unstarted (a one-shot for one chat); `offline` starts\n * no chat or group and publishes nothing (a one-shot that only reads); `logJoins` says the steps of a join through a\n * group's link on stderr (a daemon, whose log that is: `joinLog.ts`).\n */\nexport interface RuntimeOptions { deferGroups?: boolean; offline?: boolean; logJoins?: boolean }\n\n/**\n * Starts the app's engine on this profile (WISP 1100 § Runtime): IndexedDB on disk, WebRTC through libdatachannel,\n * HyperDHT native in this process, Iroh's wasm build (relay only, as the web app), Pkarr through the relays in the\n * settings and the Mainline DHT directly (read when the relays fail, written always).\n * The caller holds the profile's lock.\n */\n/** The files a light backup left out, as the restore wrote them in the mark (an empty mark: none). */\nfunction leftOutIn(mark: string): string[] {\n try {\n const ids = (JSON.parse(readFileSync(mark, \"utf8\") || \"{}\") as { leftOut?: unknown }).leftOut;\n return Array.isArray(ids) ? ids.filter((id): id is string => typeof id === \"string\") : [];\n } catch { return []; }\n}\n\nexport async function startRuntime(paths: ProfilePaths, options: RuntimeOptions = {}): Promise<Runtime> {\n // `GHOSTLY_LINK_TRACE=<file>`: each step of each chat's way to live, one JSON line (packages/core/src/linkTrace.ts),\n // as the Desktop writes to its log. For measuring, not needed to run.\n // Read first: a mistyped one stops here, before anything opens.\n const pinned = pkarrRelays();\n const pinnedIroh = irohRelays();\n const trace = process.env.GHOSTLY_LINK_TRACE;\n if (trace) setLinkTraceSink(line => appendFileSync(trace, line + \"\\n\", { mode: 0o600 }));\n if (trace || options.logJoins) {\n // A join through a group's link, step by step (what the door read, whom it let in), in the same file; a daemon\n // also says its knocks and entry sessions in its log.\n (await import(\"@ghostly/browser/engine/joinTrace\")).setJoinTraceSink(line => {\n if (trace) appendFileSync(trace, line + \"\\n\", { mode: 0o600 });\n const said = options.logJoins ? joinLogLine(line) : null;\n if (said) process.stderr.write(said + \"\\n\");\n });\n }\n const store = await openPersistentIndexedDb(paths.db);\n // A profile a restore made, started for the first time: its store is the backup's, as old as the backup. Its ecash\n // is checked with the mints and its unfinished payment attempts authorize nothing, as in the app's restore. Done\n // before the engine reads any of it; the mark goes only once that is on disk, so a start cut short does it again.\n const restored = join(paths.dir, RESTORED_MARK);\n if (existsSync(restored)) {\n try {\n const rows = await import(\"@ghostly/browser/shared/restoredRows\");\n await rows.markRestoredWallet();\n // The files a light backup left out: their records say so, as in the app's restore.\n await rows.markLeftOutFiles(leftOutIn(restored));\n await store.compact();\n } catch (error) {\n await store.close().catch(() => {});\n throw error;\n }\n rmSync(restored, { force: true });\n }\n const webrtc = await installWebRtc();\n // Voice calls (WISP 1100 § Calls): offered to contacts (calls/1) only where their media can run.\n const stack = webrtc ? await loadCallStack() : \"Calls need WebRTC, which is off on this headless Ghostly\";\n const callsUnavailable = typeof stack === \"string\" ? stack : null;\n installFileFetch();\n // `GHOSTLY_TEST_MINTS=<url,…>`: mints on this machine that are test servers with a fake Lightning backend (a regtest\n // or e2e stack). Only through them does a Testnet wallet pay a Bitcoin (lnbc) invoice, as through the public test mint.\n const testMints = process.env.GHOSTLY_TEST_MINTS;\n if (testMints) (await import(\"@ghostly/browser/shared/mints\")).declareTestMints(testMints.split(\",\"));\n await installEventSource();\n await preloadWalletModules();\n // Loaded after IndexedDB is in place: nothing of the engine may open its database first.\n const iroh = await import(\"@ghostly/iroh-web\");\n iroh.initSync({ module: irohWasmBytes() });\n // Files sent and received are real files in the profile's folder, as on the Desktop.\n const [{ registerFileBytes }, { NodeFileBytes }] = await Promise.all([import(\"@ghostly/browser/shared/fileBytes\"), import(\"./fileBytes\")]);\n registerFileBytes(\"native\", async () => new NodeFileBytes(paths.files), true);\n const [{ EngineServer }, { createHyperEndpoint }] = await Promise.all([\n import(\"@ghostly/browser/engine/server\"),\n import(\"../../../../native/transports/hyperdht/endpoint.mjs\"),\n ]);\n const network = hyperdhtNetwork();\n const dht = mainlineNetwork();\n const mainline = dht.off ? null : new (await import(\"./mainline\")).Mainline({ bootstrap: dht.bootstrap, host: dht.host });\n // `GHOSTLY_PKARR_RELAYS`: those relays only, in place before the engine publishes anything (the setting is not read).\n const transport = (mainline || pinned) ? new (await import(\"./mainline\")).RelaysAndDht(mainline, pinned ? { relays: pinned } : {}, !!pinned) : null;\n const apps = servedApps(paths.dir).length > 0;\n const server = new EngineServer({\n ...(transport ? { transport } : {}),\n irohWeb: true,\n // `GHOSTLY_IROH_RELAYS`: those relays only (the setting is not read).\n ...(pinnedIroh ? { irohRelays: pinnedIroh } : {}),\n // A CLI profile is one device's (WISP 06): no device state is kept for it.\n singleDevice: true,\n nativeTransports: { \"hyperdht/1\": (seedB64: string) => createHyperEndpoint(fromBase64Url(seedB64), network) },\n // No wallet starts by itself: a bot has the wallets it made (WISP 1100 § Wallet SDKs on Node).\n automaticWallets: false,\n // Fedimint's client databases are files of the profile, each in a worker thread (./fedimint.ts).\n fedimintSdk: nodeFedimintSdk(join(paths.dir, \"fedimint\")),\n // Local web apps may be shared with a contact, reached on loopback only (src/services.ts).\n servicesSupport: true,\n // No mini-app runs here (WISP 1200 § Per client: CLI). A profile that serves an app (`app serve`, src/appServe.ts)\n // speaks apps/1 for it as a bot, with no store; any other offers no apps/1 and refuses the app calls, whatever the\n // build's APPS_ENABLED says.\n apps: apps ? \"wire\" : false,\n // A daemon stays online: a hub of the large private groups it is in (WISP 902 · Group Mesh § Hubs), unless GHOSTLY_HUB=0.\n staysOnline: process.env.GHOSTLY_HUB !== \"0\",\n localFetch: nodeLocalFetch,\n // A wake-up goes to a push service only, on public addresses only (./pushSend.ts), as the Desktop's does.\n pushSend: (request) => nodePushSend(request),\n callsSupport: callsUnavailable === null,\n ...(callsUnavailable ? { callsUnavailable } : {}),\n ...(options.deferGroups ? { deferGroups: true } : {}),\n ...(options.offline ? { offline: true } : {}),\n // Tests only: group links as an app from before native ones (test/groupCompat.test.ts).\n ...(process.env.GHOSTLY_TEST_WEBRTC_GROUP_LINKS === \"1\" ? { webrtcGroupLinks: true } : {}),\n });\n try {\n await server.ready;\n } catch (error) {\n await mainline?.destroy().catch(() => {});\n await store.close().catch(() => {});\n throw error;\n }\n let closing: Promise<void> | null = null;\n return {\n paths, server, store, webrtc, callsUnavailable, apps,\n close: () => (closing ??= (async () => {\n await server.node.shutdown().catch(() => {});\n await mainline?.destroy().catch(() => {});\n await store.close();\n })()),\n };\n}\n","import { createServer, type Server, type Socket } from \"node:net\";\nimport { chmodSync, existsSync, rmSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { createInterface } from \"node:readline\";\nimport type { StoredMessage } from \"@ghostly/browser/shared/types\";\nimport { announceJoins } from \"./announce\";\nimport { reportAppFrames } from \"./appEvents\";\nimport { callApi, type ApiContext } from \"./api\";\nimport { CallManager } from \"./calls/manager\";\nimport { asCliError, CliError } from \"./errors\";\nimport { EventHub, type GhostlyEvent } from \"./events\";\nimport { resumeHolds } from \"./holds\";\nimport { ownFolder, privateFolder } from \"./privateFolder\";\nimport { acquireLock, type ProfilePaths } from \"./profiles\";\nimport { startRuntime, type RuntimeOptions } from \"./runtime/engine\";\nimport { flushCardUpdates } from \"./statusCards\";\n\n/** The longest request line the daemon reads (WISP 1100 § Framing). */\nexport const MAX_LINE = 16 * 1024 * 1024;\n\n/**\n * The most a connection may leave unread (answers and events the daemon wrote that its client has not taken): past\n * it, the connection ends. A `listen` stopped with Ctrl+Z, or a client that stops reading, kept every later event in\n * the daemon's memory until it read again (155 MB after 6,000 long texts); `listen` reconnects after the last seq it\n * took, and the journal still has the rest. A replay is the journal's size at most and does not count against it.\n */\nexport const MAX_UNREAD = 8 * 1024 * 1024;\n\n/**\n * How many of a chat's newest messages the start reads to see whether the events are in step with its history: the\n * whole history of every chat and group, read at every start, took a daemon's start and its memory with every message\n * the profile ever stored (1 s and 70 MB at 100,000).\n */\nexport const BASELINE_NEWEST = 100;\n\nexport interface Host {\n ctx: ApiContext;\n close(): Promise<void>;\n}\n\n/**\n * Runs a profile in this process: takes its lock, starts the engine, and derives events from it. A daemon then\n * serves the socket (`serve`); a one-shot command calls the API directly and closes. A one-shot for one chat passes\n * `deferGroups`: the groups' sessions would spend the relays' budget its message needs.\n */\nexport async function openHost(paths: ProfilePaths, mode: ApiContext[\"mode\"], version: string, options: RuntimeOptions = {}): Promise<Host> {\n const release = acquireLock(paths);\n let runtime;\n try {\n runtime = await startRuntime(paths, mode === \"daemon\" ? { ...options, logJoins: true } : options);\n } catch (error) {\n release();\n throw error;\n }\n const hub = new EventHub(paths.events);\n let calls!: CallManager;\n try {\n await hub.open();\n const node = runtime.server.node;\n const now = node.getState();\n // The histories read here are the attach's too: one that changes before the hub is attached is read again then.\n const mark = runtime.server.historyMark;\n const histories = new Map<string, readonly StoredMessage[]>(), newest = new Map<string, readonly StoredMessage[]>();\n const whole = (id: string) => (id.startsWith(\"group:\") ? node.groupMessages({ groupId: id.slice(6) }) : node.getMessages(id));\n // A store that was closed holds every note the hub made, and a history the engine has not touched since is as it\n // was closed: its newest messages say whether the hub is in step with it. Otherwise the whole history is read.\n const closed = !runtime.store.recovered;\n for (const id of [...now.links.map((link) => link.id), ...now.groups.map((group) => `group:${group.id}`)]) {\n const page = closed && !runtime.server.historyChanged(id) ? await node.messagePage({ linkId: id, limit: BASELINE_NEWEST }) : null;\n if (page && !page.more) histories.set(id, page.messages);\n else if (page && hub.inStep(id, page.messages)) newest.set(id, page.messages);\n else histories.set(id, await whole(id));\n }\n hub.baseline(now, histories, { newest, whole });\n announceJoins(hub, node);\n // Calls (WISP 1100 § Calls): only a daemon answers and places them; a one-shot still reports one that rings.\n calls = new CallManager({ engine: node, emit: (type, id, fields) => hub.emit(type, id, fields), profileDir: paths.dir, answers: mode === \"daemon\" });\n const manager = calls;\n hub.onCallSignal((chat, signal) => manager.onSignal(chat, signal));\n reportAppFrames(hub, node, paths.dir);\n runtime.server.attach(hub.sink, { mark, ids: [...histories.keys(), ...newest.keys()] });\n } catch (error) {\n await runtime.close().catch(() => {});\n release();\n throw error;\n }\n const ctx: ApiContext = { runtime, hub, mode, version, calls };\n resumeHolds(ctx);\n let closing: Promise<void> | null = null;\n return {\n ctx,\n close: () => (closing ??= (async () => {\n // Every call is hung up, and every card update still waiting for its time sent, while the engine can still tell the contact.\n await Promise.all([calls.stopAll(), flushCardUpdates(ctx)]).catch(() => {});\n runtime.server.detach(hub.sink);\n await runtime.close().catch(() => {});\n release();\n })()),\n };\n}\n\ninterface Request { id?: unknown; method?: unknown; params?: unknown }\n\n/**\n * The local control API over the profile's Unix socket (0600 in the 0700 profile folder): one JSON object per line\n * each way. `events.subscribe` turns a connection into a stream of `{\"event\": …}` lines.\n */\nexport interface Served {\n server: Server;\n /** Stops listening and ends every connection (subscribers too: they reconnect to the next daemon). */\n close(): Promise<void>;\n}\n\nexport async function serve(host: Host): Promise<Served> {\n const { ctx } = host;\n const path = ctx.runtime.paths.socket;\n // The folder is owner-only before the socket is in it: nobody else can reach the socket in the moment before its chmod.\n if (process.platform !== \"win32\") {\n if (dirname(path) !== ctx.runtime.paths.dir) privateFolder(dirname(path), \"the daemon will not put its socket there\");\n else ownFolder(ctx.runtime.paths.dir, \"the daemon will not put its socket there\");\n }\n // A socket file left by a daemon that died: the lock says nobody holds the profile, so it is stale.\n if (existsSync(path)) rmSync(path, { force: true });\n const sockets = new Set<Socket>();\n const server = createServer((socket) => {\n sockets.add(socket);\n socket.on(\"close\", () => sockets.delete(socket));\n connection(ctx, socket);\n });\n await new Promise<void>((resolve, reject) => {\n server.once(\"error\", reject);\n // The socket file is made owner-only (the bind happens within listen), then chmod'ed below as well.\n const umask = ownerOnlyUmask();\n try { server.listen(path, () => { server.off(\"error\", reject); resolve(); }); } finally { umask?.(); }\n });\n if (process.platform !== \"win32\") chmodSync(path, 0o600);\n let closing: Promise<void> | null = null;\n return {\n server,\n close: () => (closing ??= new Promise<void>((resolve) => {\n server.close(() => resolve());\n for (const socket of sockets) socket.end();\n setTimeout(() => { for (const socket of sockets) socket.destroy(); }, 500).unref();\n })),\n };\n}\n\nlet guarded = false;\n\n/**\n * A daemon says an error nobody caught (with its stack, on stderr: the daemon's log when detached) and keeps serving:\n * one request gone wrong must not take the profile offline. One-shot commands do not call this; they still fail loudly.\n *\n * `outputs`: what it writes to. A write there that fails (a pipe whose reader is gone: the process that started the\n * daemon died) is dropped, not an error nobody caught: saying it would be one more failed write, and the next, with\n * no turn left for anything else (a full core, and no stop on SIGTERM).\n */\nexport function keepServing(\n proc: Pick<NodeJS.Process, \"on\"> = process,\n write: (text: string) => void = (text) => { process.stderr.write(text); },\n outputs: Pick<NodeJS.WriteStream, \"on\">[] = proc === process ? [process.stdout, process.stderr] : [],\n): void {\n if (proc === process) { if (guarded) return; guarded = true; }\n const log = (what: string) => (error: unknown) => {\n try { write(`ghostly: ${what}, the daemon keeps serving: ${error instanceof Error ? error.stack ?? error.message : String(error)}\\n`); } catch { /* stderr is gone */ }\n };\n for (const output of outputs) output.on(\"error\", () => { /* nobody reads it any more */ });\n proc.on(\"unhandledRejection\", log(\"unhandled rejection\"));\n proc.on(\"uncaughtException\", log(\"uncaught exception\"));\n}\n\n/** Sets a 077 umask; the function it returns puts the old one back. Null where there is none (Windows, a worker). */\nfunction ownerOnlyUmask(): (() => void) | null {\n if (process.platform === \"win32\") return null;\n try {\n const old = process.umask(0o077);\n return () => { process.umask(old); };\n } catch { return null; }\n}\n\nfunction connection(ctx: ApiContext, socket: Socket): void {\n const unsubscribes: (() => void)[] = [];\n // A replay raises the limit by what it wrote; once the client has read everything, it is MAX_UNREAD again.\n let limit = MAX_UNREAD;\n const unsubscribe = () => { for (const off of unsubscribes.splice(0)) off(); };\n // Ended, not destroyed: the client still reads whole lines up to the last one written, then the end.\n const write = (value: unknown, replayed = false) => {\n if (socket.destroyed || socket.writableEnded) return;\n if (!replayed && socket.writableLength > limit) { unsubscribe(); socket.end(); return; }\n socket.write(JSON.stringify(value) + \"\\n\");\n };\n socket.on(\"drain\", () => { limit = MAX_UNREAD; });\n socket.on(\"close\", unsubscribe);\n socket.on(\"error\", () => {});\n const lines = createInterface({ input: socket, crlfDelay: Infinity });\n let buffered = 0;\n socket.on(\"data\", (chunk) => { buffered += chunk.length; if (buffered > MAX_LINE) socket.destroy(); });\n lines.on(\"line\", (line) => {\n buffered = 0;\n if (!line.trim()) return;\n let request: Request;\n try { request = JSON.parse(line) as Request; } catch { write({ id: null, error: new CliError(\"bad_request\", \"Not JSON\").toJSON() }); return; }\n const id = typeof request.id === \"number\" || typeof request.id === \"string\" ? request.id : null;\n if (typeof request.method !== \"string\") { write({ id, error: new CliError(\"bad_request\", \"method is required\").toJSON() }); return; }\n if (request.method === \"events.subscribe\") {\n // One stream per connection: a second would get every event again, and its replay would pass the bound once more.\n if (unsubscribes.length) { write({ id, error: new CliError(\"bad_request\", \"This connection is subscribed already: open another one for a new stream\").toJSON() }); return; }\n const params = (request.params ?? {}) as { since?: unknown };\n const since = typeof params.since === \"number\" ? params.since : ctx.hub.lastSeq;\n // A journal that cannot be replayed is the answer, never a subscription that then says nothing.\n let replay: GhostlyEvent[];\n try { replay = ctx.hub.replay(since); } catch (error) { write({ id, error: asCliError(error).toJSON() }); return; }\n // Live events queue while the journal replays, so none falls between the two.\n const queued: GhostlyEvent[] = [];\n let replaying = true;\n unsubscribes.push(ctx.hub.onEvent((event) => { if (replaying) queued.push(event); else write({ event }); }));\n write({ id, result: { subscribed: true, lastSeq: ctx.hub.lastSeq } });\n let last = since;\n for (const event of replay) { write({ event }, true); last = event.seq; }\n limit = Math.max(limit, socket.writableLength + MAX_UNREAD);\n replaying = false;\n for (const event of queued) if (event.seq > last) write({ event });\n return;\n }\n void callApi(ctx, request.method, request.params ?? {}).then(\n (result) => write({ id, result: result ?? null }),\n (error) => write({ id, error: asCliError(error).toJSON() }),\n );\n });\n}\n"],"mappings":"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"}