CI / Node 24 (push) Successful in 15s
CI / Node 26 (push) Successful in 16s
CI / Node 22 (pull_request) Successful in 13s
CI / Node 24 (pull_request) Failing after 3s
CI / Node 26 (pull_request) Failing after 4s
CI / Chromium (push) Failing after 1m8s
CI / Publish to staging registry (push) Skipped
CI / Chromium (pull_request) Failing after 1m3s
CI / Publish to staging registry (pull_request) Skipped
CI / Node 22 (push) Failing after 4s
- KVStorageEncrypted: AES-GCM per value (fresh IV, storage key as AAD), keys stay readable; decrypts and re-emits pushed changes and passes subscriptions through, so it can be a composite source. generateKey / exportKey / importKey / deriveKey (PBKDF2). - KVStorageHttpServer: allowOrigins (CORS), including cache-control for SSE preflights. - Chromium tests (Vitest browser mode + Playwright) against a real cross-origin server: conformance for IndexedDB, encrypted, HTTP and composite stores; signed requests and SSE; two devices syncing, offline writes surviving a reload. New CI job. - Composite fixes found by those tests: sync() during a running sync schedules one more (changes between the first scan and the stream connecting were missed); a queued write the source refuses is dropped, restored and reported (onError) instead of blocking the queue forever. - README with examples, mirrored in test/readme.test.ts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Qxbkp4tA7G5SCWjzWuxmE
29 lines
1.4 KiB
TypeScript
29 lines
1.4 KiB
TypeScript
import { createServer } from 'node:http'
|
|
import type { AddressInfo } from 'node:net'
|
|
|
|
/** Serves a `Request → Response` handler over a real socket (test helper). */
|
|
export async function serve(handle: (request: Request) => Promise<Response>): Promise<{ url: string; close: () => Promise<void> }> {
|
|
const server = createServer(async (req, res) => {
|
|
const chunks: Buffer[] = []
|
|
for await (const chunk of req) chunks.push(chunk as Buffer)
|
|
const response = await handle(
|
|
new Request(`http://localhost${req.url}`, {
|
|
method: req.method,
|
|
headers: Object.entries(req.headers).flatMap(([name, value]) => (typeof value === 'string' ? [[name, value] as [string, string]] : [])),
|
|
body: req.method === 'POST' ? Buffer.concat(chunks) : undefined,
|
|
}),
|
|
)
|
|
res.writeHead(response.status, Object.fromEntries(response.headers))
|
|
if (!response.body) return void res.end()
|
|
const reader = response.body.getReader()
|
|
res.on('close', () => void reader.cancel().catch(() => {}))
|
|
for (let next = await reader.read(); !next.done; next = await reader.read()) res.write(next.value)
|
|
res.end()
|
|
})
|
|
await new Promise<void>((resolve) => server.listen(0, '127.0.0.1', resolve))
|
|
return {
|
|
url: `http://127.0.0.1:${(server.address() as AddressInfo).port}`,
|
|
close: () => new Promise((resolve) => (server.closeAllConnections(), server.close(() => resolve()))),
|
|
}
|
|
}
|