Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .changeset/subduction-forty-eight.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@inkandswitch/patchwork-bootloader": patch
"@inkandswitch/patchwork-filesystem": patch
"@inkandswitch/patchwork": patch
"@inkandswitch/patchwork-plugins": patch
---

`@automerge/automerge` goes to `3.4.1`, and `@automerge/automerge-repo-network-broadcastchannel` joins the automerge-repo family at `2.6.0-subduction.48`.
20 changes: 20 additions & 0 deletions .changeset/tab-worker-subduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
"@inkandswitch/patchwork-bootloader": minor
"@inkandswitch/patchwork": minor
---

Every Repo on the origin is its own Subduction node. A tab holds this origin's IndexedDB, keeps its own WebSocket to the sync server, and meets the other tabs over a BroadcastChannel carrying Subduction (`siblingAdapters()` in `@inkandswitch/patchwork-bootloader/siblings`, passed to `new Repo({ subductionAdapters })`). The automerge SharedWorker no longer sits between tabs and storage — it is one more such node, kept only to resolve `automerge:` URLs for the service worker, which can't own a Repo itself. The websocket proxy worker is gone with it.

Benchmarked against the shared-worker arrangement (`sites/bench`): boot and memory are a wash or better, cross-tab propagation matches, and two shared-worker failures go away — a `find()` racing a sibling's `create()` settled as unavailable, and edits made just before a tab closed were lost, since a storageless tab had nothing to flush to. Each tab flushing its own IndexedDB closes both.

Keyhive sites use the subduction-backed hive in both the tab and the worker, each talking to the sync server directly.

`patchwork.sw.subscribeSyncState(documentId, listener)` stays, now a filter over the tab's own Repo's `subduction-remote-heads` event, replaying the server's current heads from `handle.getSyncInfo()` on subscribe; `SyncStateDocMessage` is exported from `@inkandswitch/patchwork`. Removed from `setupServiceWorker()`'s result: `subscribeToRepoChannel`, `getRepoChannel`, `subscribeSyncState`. The `@patchwork/syncstate` BroadcastChannel and the other `SyncState*` message types are gone too; a tab's own Repo has what they carried — `repo.isSubductionConnected()` and the `subduction-connection` event for the link, `repo.connectedSubductionPeerIds()` for which peers are the server, and `patchwork.signerIdentity` for this tab's peer id. `createRepo` in `@inkandswitch/patchwork` takes no arguments.

`@inkandswitch/patchwork-bootloader` depends on `@automerge/automerge-repo-network-broadcastchannel`, which is also on the importmap.

That worker is renamed for what it now does: `@inkandswitch/patchwork-bootloader/automerge-worker` is now `@inkandswitch/patchwork-bootloader/automerge-protocol-handler-worker`, emitted as `automerge-protocol-handler-worker.js` (the `workerPath` option on `setupServiceWorker` still overrides it), and `getAutomergeWorker()` is now `getAutomergeProtocolHandlerWorker()`.

Inter-tab sync is Subduction, not classic automerge sync. `connectSiblings(repo, hive)` is replaced by `siblingAdapters()`, which returns the `subductionAdapters` entries for a Repo rather than mutating one after the fact, so it is passed to the `Repo` constructor. The frames on the siblings BroadcastChannel are Subduction transport frames authenticated by each node's own signer; the keyhive network adapter no longer wraps that channel, since keyhive material reaches siblings the same way it reaches the sync server. The one remaining classic-sync path is the opt-in classic sync server the protocol handler worker connects to on request.

Subduction's handshake has an initiator and a responder, and a BroadcastChannel is a mesh, so the siblings adapter is passed with `role: "mesh"`, added to the automerge-repo fork's `subductionAdapters` by this repo's pnpm patch: for each pair of peers on the adapter, the one whose peer id sorts lower initiates the handshake and the other accepts.
11 changes: 8 additions & 3 deletions core/bootloader/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@
"import": "./dist/externals-list.js",
"types": "./dist/externals-list.d.ts"
},
"./siblings": {
"import": "./dist/siblings.js",
"types": "./dist/siblings.d.ts"
},
"./storage": {
"import": "./dist/storage.js",
"types": "./dist/storage.d.ts"
Expand All @@ -34,9 +38,9 @@
"import": "./dist/service-worker.js",
"types": "./dist/service-worker.d.ts"
},
"./automerge-worker": {
"import": "./dist/automerge-worker.js",
"types": "./dist/automerge-worker.d.ts"
"./automerge-protocol-handler-worker": {
"import": "./dist/automerge-protocol-handler-worker.js",
"types": "./dist/automerge-protocol-handler-worker.d.ts"
},
"./module-loader": {
"import": "./dist/module-loader.js",
Expand All @@ -56,6 +60,7 @@
"@automerge/automerge": "catalog:",
"@automerge/automerge-repo": "catalog:",
"@automerge/automerge-repo-keyhive": "catalog:",
"@automerge/automerge-repo-network-broadcastchannel": "catalog:",
"@automerge/automerge-repo-network-messagechannel": "catalog:",
"@automerge/automerge-repo-network-websocket": "catalog:",
"@automerge/automerge-repo-storage-indexeddb": "catalog:",
Expand Down
Loading
Loading