MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 1m16s
Phase 4 of the collaboration plan:
- The sync engine listens for DOC_SEQ in the note's realtime room and syncs
right away. Without WebSocket/SSE it long-polls /docs/{id}/changes (25 s);
after three failures it falls back to polling every 3 s and retries long
polling when the browser comes back online.
- With push or long polling the engine only syncs every 7 s while holding
leases (renewal), otherwise every 30 s as a safety net. Focusing a block
announces its lease immediately (otherwise it could take up to 30 s).
- Realtime connection: SSE counts as connected only after its first event;
a stream that stays silent for 5 s (buffering proxy) switches SSE off for
the tab. App-level PING/PONG detects WebSockets that are accepted but
forward nothing and switches to SSE after 35 s.
- The editor shows the active channel for shared notes.
- Tests: engine channel tests (push, heartbeat, lease on focus, long
polling, interval fallback, switching), realtime fallback tests with fake
WebSocket/EventSource, Go tests for PONG and DOC_SEQ delivery, and
e2e/channels.e2e.mjs measuring latency behind six simulated proxy setups
(0.2-0.3 s on all live channels, ~3 s with plain polling).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
26 lines
1.0 KiB
TypeScript
26 lines
1.0 KiB
TypeScript
// Push notifications for note sync: the server sends DOC_SEQ into the note's room whenever the note, its
|
|
// leases or its presence change. Delivery uses the app-wide realtime connection (WebSocket, else SSE).
|
|
import { createRealtimeConnection, getRealtimeTransport, isRealtimeConnected } from '../api';
|
|
|
|
export type PushKind = 'ws' | 'sse' | 'none';
|
|
|
|
export interface PushChannel {
|
|
/** Calls onPoke for changes of docId and onStatus whenever the channel goes up or down. */
|
|
subscribe(docId: string, onPoke: () => void, onStatus: (kind: PushKind) => void): () => void;
|
|
}
|
|
|
|
export const realtimePush: PushChannel = {
|
|
subscribe(docId, onPoke, onStatus) {
|
|
const current = (): PushKind => (isRealtimeConnected() ? (getRealtimeTransport() as PushKind) : 'none');
|
|
const conn = createRealtimeConnection({
|
|
docId,
|
|
onMessage: (msg: any) => {
|
|
if (msg?.event === 'DOC_SEQ' && msg.payload?.docId === docId) onPoke();
|
|
},
|
|
onStatusChange: () => onStatus(current()),
|
|
});
|
|
onStatus(current());
|
|
return () => conn.close();
|
|
},
|
|
};
|