- Every KV storage emits change events (base class owns writeMany/removeMany/ writeIfAbsent; implementations provide protected apply* methods) - KVStorageHttpServer streams changes over SSE (GET /events) with a resumable history; KVStorageHttp.watch() subscribes and resumes via Last-Event-ID - KVStorageIndexedDB for local persistence - KVStorageComposite keeps replicas of a source in sync: local reads, resume instead of full sync, offline outbox replayed in order, echo tracking - Values may contain bigint/Uint8Array (Extended JSON over the network) - Vendor sse-session, event-emitter and extended-json (no runtime deps) - Freeze child nodes so shadowed state on children fails loudly - CI on Node 22, 24 and 26 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Qxbkp4tA7G5SCWjzWuxmE
33 lines
1.3 KiB
TypeScript
33 lines
1.3 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import { SSEEventParser } from '../../src/utils/sse-session/sse-event-parser.js'
|
|
import { SSESession } from '../../src/utils/sse-session/sse-session.js'
|
|
|
|
const encode = (text: string) => new TextEncoder().encode(text)
|
|
|
|
describe('vendored sse-session', () => {
|
|
it('parses events across arbitrary chunk boundaries', () => {
|
|
const parser = new SSEEventParser()
|
|
const text = 'id: 1\nevent: change\ndata: {"a":1}\n\n: comment\ndata: line1\ndata: line2\n\n'
|
|
const events = []
|
|
for (let i = 0; i < text.length; i += 3) events.push(...parser.parseEvents(encode(text.slice(i, i + 3))))
|
|
expect(events).toEqual([{ id: '1', event: 'change', data: '{"a":1}' }, { data: 'line1\nline2' }])
|
|
})
|
|
|
|
it('connects, delivers messages and reconnects when persistent', async () => {
|
|
let connections = 0
|
|
const session = new SSESession('http://test/events', {
|
|
persistent: true,
|
|
fetch: async () => {
|
|
connections++
|
|
return new Response(encode(`data: hello ${connections}\n\n`))
|
|
},
|
|
})
|
|
const received: string[] = []
|
|
session.on('message', (m) => received.push(m.data))
|
|
await session.connect()
|
|
await session.waitFor('message', (m) => m.data === 'hello 2')
|
|
await session.disconnect()
|
|
expect(received.slice(0, 2)).toEqual(['hello 1', 'hello 2'])
|
|
})
|
|
})
|