惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

WordPress大学
WordPress大学
Engineering at Meta
Engineering at Meta
D
DataBreaches.Net
月光博客
月光博客
Recent Announcements
Recent Announcements
Google DeepMind News
Google DeepMind News
U
Unit 42
腾讯CDC
爱范儿
爱范儿
J
Java Code Geeks
有赞技术团队
有赞技术团队
Blog — PlanetScale
Blog — PlanetScale
N
Netflix TechBlog - Medium
B
Blog
Stack Overflow Blog
Stack Overflow Blog
GbyAI
GbyAI
T
The Blog of Author Tim Ferriss
小众软件
小众软件
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Y
Y Combinator Blog
大猫的无限游戏
大猫的无限游戏
Microsoft Azure Blog
Microsoft Azure Blog
T
Tailwind CSS Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知

Hacker News: Show HN

PurrrrrFocus: Pomodoro Timer App - App Store Workflow Engine — Multi-Step Orchestration for Bun RapidPhoto: Pro Photo Editor App - App Store GitHub - DheerG/swarms: Achieve extraordinary results with claude code across a variety of tasks SPICE simulation → oscilloscope → verification with Claude Code — Lucas Gerads Show HN: VCoding – A 5 MB native Windows IDE with no dynamic dependencies Show HN: LLMs don't hallucinate because they're bad at math, it's the format GitHub - Agent-FM/agentfm-core: AgentFM is a peer-to-peer network that turns everyday computers into a decentralized AI supercomputer. AgentFM lets you run massive AI workloads directly across a global mesh of idle CPUs and GPUs. Show HN: Tracking Top US Science Olympiad Alumni over Last 25 Years GitHub - Potarix/agent-hub: One place to talk to all your agents Show HN: Runtime security for AI agents(injection,tool abuse, data exfiltration) GitHub - dubeyKartikay/lazyspotify: Terminal Spotify client for macOS and Linux GitHub - the-banana-tool/king-louie: Easy to use GUI Personal AI Assistant. Win/Linux/Mac. Show HN I made my vacation rental bookable by AI agents–no Airbnb, 0% commission GitHub - basteez/jsf-autoreload: maven plugin to enable hot reload on jsf projects uvm32/hosts/host-gdbstub at main · ringtailsoftware/uvm32 GitHub - labsai/EDDI: Config-driven engine that turns JSON into production-grade AI agents. Multi-agent orchestration, 12+ LLM providers, MCP/A2A protocols, RAG, persistent memory, and enterprise compliance (EU AI Act, GDPR, HIPAA). Built on Quarkus. GitHub - glitchnsec/fortyone-oss: AI Executive Assistant Platform Quickstart | Alien GitHub - muxshed/shed: One stream in, or many. Every destination, simultaneously. No cloud middleman, no per-channel fees, no limits. GitHub - ocrbase-hq/ocrbase: 📄 PDF/IMG ->.MD/JSON Document OCR API for PaddleOCR and GLMOCR. Self-hostable. GitHub - impactjo/home-memory: MCP server that lets your AI assistant remember everything about your home. GitHub - Sets88/dbcls: DbCls is a powerful terminal database client that supports various databases GitHub - neptun2000/heor-agent-mcp GitHub - SeanFDZ/macmind: Single-layer transformer in HyperTalk for the classic Macintosh RollQuation: Math Puzzles - Apps on Google Play GitHub - dropbox/witchcraft Show HN: Agent-cache – Multi-tier LLM/tool/session caching for Valkey and Redis GitHub - opentalon/opentalon: OpenTalon is an open-source platform built from the ground up in Go as a robust alternative to OpenClaw LinkedIn™ 职位抓取工具 - Chrome 应用商店
GitHub - arnold-graf/cross-tab-worker: A drop-in coordina...
ch_sm · 2026-05-02 · via Hacker News: Show HN

A drop-in coordination wrapper that keeps exactly one Worker alive across all same-origin browser tabs. Useful for OPFS access from Workers when using multiple tabs.

Exactly one tab owns the real Worker at a time (the leader). All other tabs (followers) send messages through a direct MessagePort to the leader, which forwards them to the worker. When the leader tab closes, one follower is automatically elected as the new leader. The application sees the same postMessage / onmessage interface regardless of role.

npm install @arnoldgraf/cross-tab-worker --save

Why not SharedWorker?

SharedWorker doesn't support FileSystemSyncAccessHandle — the synchronous OPFS API that high-performance SQLite/WASM VFS implementations require. This library achieves the same multi-tab sharing using a regular dedicated Worker owned by one tab, with coordination through Web Locks and a tiny broker SharedWorker. The approach is generally informed and inspired by this discussion on wa-sqlite.


Usage

import { CrossTabWorker } from '@arnoldgraf/cross-tab-worker';

const worker = new CrossTabWorker(
  'my-db-worker',                              // stable name — used as the lock key
  () => new Worker(new URL('./db.worker.ts', import.meta.url), { type: 'module' }),
);

worker.onmessage = (e) => console.log('from worker:', e.data);

// Direct transfer if this tab is the leader; relayed through a direct 
// MessagePort if follower. Zero-copy in both cases.
const buffer = new ArrayBuffer(4096);
worker.postMessage({ op: 'write', buf: buffer }, [buffer]);

API

new CrossTabWorker(name, factory)
Parameter Type Description
name string Unique identifier for this worker type. Used as the Web Lock key and SharedWorker name. Must be stable across page loads.
factory () => Worker Called only in the leader tab to instantiate the real Worker.

Instance

Member Description
postMessage(message, transfer?) Send a message to the worker. Zero-copy in the leader; transferred via a direct MessagePort in followers.
onmessage Callback for messages from the worker.
onmessageerror Callback for deserialization errors.
addEventListener(type, handler) 'message' or 'messageerror'.
removeEventListener(type, handler)
destroy() Leader: terminates the underlying Worker and releases the lock. Follower: closes ports and cancels the queued lock request. Safe to call in either role.
isLeader boolean getter — true if this tab currently owns the Worker.

Architecture

Follower tab (B)                               Leader tab (A)
┌─────────────────────────────┐                 ┌────────────────────────────┐
│ CrossTabWorker (follower)   │                 │ CrossTabWorker (leader)    │
│  app.postMessage(msg, xfer) │                 │  ┌──────────────────────┐  │
│      │                      │                 │  │ Dedicated Worker     │  │
│      ▼                      │ direct channel  │  │ (real worker owner)  │  │
│  MessagePort (port1) ───────┼────────────────►│  └──────────────────────┘  │
└─────────────────────────────┘  relay msg/xfer │            ▲               │
                                                │            │ worker events │
                                                └────────────┼───────────────┘
                                                             │
                                                             ▼
                           ┌─────────────────────────────────────────────────────┐
                           │ port-broker SharedWorker                            │
                           │ - tab registry                                      │
                           │ - current leader tracking                           │
                           │ - forwards handshake ports (port2)                  │
                           │ - fans out coordination messages                    │
                           │   (`leader-ready`, `worker-msg`, `worker-msg-error`)│
                           └─────────────────────────────────────────────────────┘

Data path (zero-copy)

When a follower calls postMessage(msg, [transfer]):

  1. The message and its transfer list travel through the follower's direct MessagePort to the leader.
  2. Transferable objects (ArrayBuffer, etc.) are transferred (ownership moves) — no copy.
  3. The leader forwards them to the Worker with worker.postMessage(msg, transfer) — no copy again.

Two ownership transfers, zero copies.

Directed zero-copy responses

For bulk response data (e.g. query results), the worker can reply zero-copy to the specific tab that sent the request using the reply port attached to every relayed message:

// Inside the worker
self.onmessage = (e) => {
  const replyPort = e.ports[0]; // present when the message came from a follower tab
  const result = new ArrayBuffer(1024 * 1024);
  // ... fill result ...

  if (replyPort) {
    // Zero-copy: result is transferred directly to the requesting tab.
    replyPort.postMessage({ payload: { result }, transfer: [result] }, [result]);
  } else {
    // Broadcast fallback for leader-local callers (no relay port).
    self.postMessage({ result });
  }
};

The library unwraps the { payload, transfer } envelope and delivers payload directly to the follower's onmessage — no structured clone, no broadcast to other tabs.

When a message originates from the leader tab itself, e.ports[0] is absent (the leader posts directly to the worker without a relay). The self.postMessage(result) fallback handles that case and broadcasts to all tabs via the broker fan-out path.

Port handshake

On startup (or after failover), each follower:

  1. Creates a MessageChannel, keeps port1 for sending.
  2. Sends port2 to the broker, which forwards it to the leader tab.
  3. The leader holds port2 and listens for relay messages on it.

All subsequent data flows directly through the MessageChannel — the broker is not involved after the handshake.

No BroadcastChannel, no heartbeat

Leader death detection is handled entirely by the Web Locks API — when a leader tab closes or calls destroy(), the browser automatically releases the lock and wakes up the next follower. No periodic heartbeat is needed.

All other coordination — leader-ready and worker-msg — travels through the broker SharedWorker, which fans messages out to all registered tab ports. No BroadcastChannel is used anywhere.

Late-joining tabs

A tab that opens after the leader is established receives a leader-info message from the broker immediately on registration, so it can connect to the leader right away.

Failover sequence

  1. Leader tab closes → browser releases the Web Lock automatically.
  2. Every follower is already blocking on navigator.locks.request — exactly one wakes up and wins.
  3. Winner calls factory(), broadcasts leader-ready, drains its outbound buffer.
  4. Other followers receive leader-ready and re-establish direct ports to the new leader.
  5. Messages in-flight when the old leader closed are lost. Applications that require exactly-once delivery must implement their own sequence numbers.

Requirements

API Required
Web Locks (navigator.locks)
SharedWorker
MessageChannel / MessagePort

Throws a clear error on construction if either Web Locks or SharedWorker is unavailable.


Response delivery modes

Scenario Path Copy?
Worker → leader tab Direct onmessage Zero-copy (no transfer needed)
Worker → all tabs (broadcast) Broker fan-out via worker-msg Structured clone
Worker → specific follower (directed reply) e.ports[0] reply port Zero-copy

Use the directed reply pattern for bulk response payloads. Use self.postMessage(data) (broadcast) for notifications or results that every tab needs to receive.