with EventSource; the app publishes with a POST to // $RC_PUBLISH/pub/counter/. That is the whole of realtime. return [ "n" => 10, "try" => "press the shared counter", "wing" => "Realtime", "title" => "Live for everyone", "promise" => "A change one person makes appears on everyone's open page within a second, without a reload.", "block" => "Declare a channel in ratcloud.conf; browsers listen with EventSource at /rc/sub//, the app POSTs to \$RC_PUBLISH/pub//.", "show" => function (?array $me): string { return '
' . (int)meta("counter") . '
' . '

Open this page in two tabs and press in one.

'; }, "api" => function (string $do, ?array $me, array $in, bool $post): ?array { if ($do !== "press" || !$post) return null; db()->exec("BEGIN IMMEDIATE"); $n = (int)meta("counter") + 1; meta_set("counter", (string)$n); db()->exec("COMMIT"); $ok = publish("counter", "main", ["n" => $n, "at" => microtime(true)]); return ["n" => $n, "published" => $ok]; }, // Two listeners, one press: both must see the new number within a second. "check" => function (): array { $host = "https://" . env("RC_HOST"); $sub = "$host/rc/sub/counter/main"; $b = new Browser(); $n = null; [$status, $msgs, $ms] = $b->listen([$sub, $sub], function () use ($b, $host, &$n) { $n = $b->post("$host/api/10/press")[2]["n"] ?? null; }, function ($m) use (&$n) { return $n !== null && count(array_filter($m, fn($l) => in_array($n, array_column(array_filter($l, "is_array"), "n"), true))) === 2; }, 2000); if ($n === null) return [false, "pressing the counter failed"]; if ($status !== [200, 200]) return [false, "the listeners were answered " . implode(", ", $status)]; $seen = count(array_filter($msgs, fn($l) => in_array($n, array_column(array_filter($l, "is_array"), "n"), true))); if ($seen < 2) return [false, "pressed to $n; only $seen of 2 listeners saw it within 2 seconds"]; if ($ms > 1000) return [false, "both listeners saw $n, but after {$ms}ms"]; return [true, "pressed to $n; both listeners saw it {$ms}ms later (press included)"]; }, "script" => <<<'JS' (() => { const el = document.getElementById("counter"), note = document.getElementById("counter-note"); if (!el) return; const es = new EventSource("/rc/sub/counter/main"); es.onmessage = (m) => { const j = JSON.parse(m.data); el.textContent = j.n; }; es.onerror = () => { note.textContent = "Lost the live connection; retrying…"; }; es.onopen = () => { note.textContent = "Live. Open this page in another tab and press in one."; }; document.getElementById("press").addEventListener("click", () => zoo.call("/api/10/press", {})); })(); JS, ];