feat: F5.1 server F keeps the shared documents

A new package, packages/server: Hocuspocus 3.4.4 on /sync, a project a
document, refused with a reason when the store has not got it; every update
stored in Postgres as it comes and compacted after 500 updates, a minute of
quiet and when the last person leaves (FOR UPDATE on the row written when
the project is made); the janitor runs only here, at loading and 250 ms
after a change that is not its own; readers change nothing; /healthz; JSON
log. On closing it takes no one new and closes the sockets too (Hocuspocus'
close is a message after which a client signs in again on the same socket).
The tests use real clients against a throwaway Postgres in Docker.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-04 13:30:43 +00:00
co-authored by Claude Opus 5.5
parent bd2e725791
commit 88db6eff7e
17 changed files with 1245 additions and 3 deletions
+42
View File
@@ -0,0 +1,42 @@
// A Postgres of its own for the server's tests: FILIUS_TEST_DATABASE_URL
// when it is set, else a throwaway container of the postgres image (Docker),
// stopped and gone after the tests. Never a database of the dashboard.
import { execFileSync } from 'node:child_process';
import pg from 'pg';
export interface TestDatabase {
readonly url: string;
stop(): Promise<void>;
}
export async function testDatabase(): Promise<TestDatabase> {
const given = process.env.FILIUS_TEST_DATABASE_URL;
if (given !== undefined) return { url: given, stop: async () => {} };
const name = `filius-test-pg-${process.pid}-${Date.now()}`;
execFileSync('docker', ['run', '-d', '--rm', '--name', name, '-e', 'POSTGRES_PASSWORD=filius', '-e', 'POSTGRES_DB=filius', '-p', '127.0.0.1::5432', 'postgres:16-alpine'], { stdio: 'pipe' });
const stop = async () => {
execFileSync('docker', ['stop', name], { stdio: 'pipe' });
};
try {
const port = execFileSync('docker', ['port', name, '5432/tcp']).toString().trim().split('\n')[0]!.split(':').pop();
const url = `postgres://postgres:filius@127.0.0.1:${port}/filius`;
// The image's first start runs a server on its socket only; the TCP port answers once the real one is up.
for (let tries = 0; ; tries++) {
const client = new pg.Client({ connectionString: url });
try {
await client.connect();
await client.query('SELECT 1');
await client.end();
return { url, stop };
} catch (error) {
await client.end().catch(() => {});
if (tries > 120) throw error;
await new Promise((resolve) => setTimeout(resolve, 250));
}
}
} catch (error) {
await stop();
throw error;
}
}