{"id":13559093,"url":"https://github.com/automerge/hypermerge","last_synced_at":"2025-09-28T23:32:20.257Z","repository":{"id":41207603,"uuid":"116171172","full_name":"automerge/hypermerge","owner":"automerge","description":"Build p2p collaborative applications without any server infrastructure in Node.js","archived":true,"fork":false,"pushed_at":"2023-01-30T18:21:04.000Z","size":3228,"stargazers_count":1279,"open_issues_count":0,"forks_count":66,"subscribers_count":34,"default_branch":"master","last_synced_at":"2025-01-09T13:09:04.791Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/automerge.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-01-03T18:56:11.000Z","updated_at":"2024-12-31T23:37:41.000Z","dependencies_parsed_at":"2023-02-16T10:16:13.375Z","dependency_job_id":null,"html_url":"https://github.com/automerge/hypermerge","commit_stats":null,"previous_names":["inkandswitch/hypermerge"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/automerge%2Fhypermerge","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/automerge%2Fhypermerge/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/automerge%2Fhypermerge/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/automerge%2Fhypermerge/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/automerge","download_url":"https://codeload.github.com/automerge/hypermerge/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":234575214,"owners_count":18854924,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T12:05:20.578Z","updated_at":"2025-09-28T23:32:19.698Z","avatar_url":"https://github.com/automerge.png","language":"TypeScript","funding_links":[],"categories":["TypeScript","others","[🎓 research](https://github.com/stars/ketsapiwiq/lists/research)","Server","Implementations"],"sub_categories":["JavaScript","Databases and Logs"],"readme":"# Hypermerge\n\n\u003e **Warning**\n\u003e Hypermerge is deprecated.\n\u003e This library is no longer maintained and uses an ancient and slow version of Automerge.\n\u003e We strongly recommend you adopt https://github.com/automerge/automerge-repo instead.\n\nHypermerge is a Node.js library for building p2p collaborative applications\nwithout any server infrastructure. It combines [Automerge](https://github.com/automerge/automerge), \na CRDT, with [hypercore](https://github.com/mafintosh/hypercore), a distributed append-only log.\n\nThis project provides a way to have apps data sets that are\nconflict free and offline first (thanks to CRDT's) and serverless (thanks to\nhypercore/DAT).\n\nWhile the DAT community has done a lot of work to secure their tool set, zero\neffort has been made with hypermerge itself to deal with security and privacy\nconcerns. Due to the secure nature of the tools its built upon a properly\naudited and secure version of this library would be possible in the future.\n\n## How it works\n\nHypermerge stores an automerge document as a set of separate Hypercores, one per actor ID. Each\nactor ID in the document is the discovery key of a Hypercore, which allows recursive lookup.\n\n## Critique\n\nThis strategy works for a small number of documents, but is very slow to synchronize due \nto the vast number of Hypercores required for a large collection. It also doesn't work well with\nthe new Automerge file format which consolidates sequential changes during storage: each change\nhas its own Hypercore entry with all the attendant metadata. To put this in context, a single\nkeystroke in a textfield will result in hundreds of bytes of data if you use Hypermerge.\n\n## Examples\n\nThere are several example repos in the `/examples` directory, including a very simple two-repo \ncode demo and a simple CLI-based chat application.\n\nThe best demonstration of Hypermerge is PushPin, which shows Hypermerge in \"full flight\", including \ntaking advantage of splitting the fast, simple front-end from the more expensive, slower back-end. \n\n## Concepts\n\nThe base object you make with hypermerge is a Repo. A repo is responsible for\nmanaging documents and replicating to peers.\n\n### Basic Setup (Serverless or with a Server)\n\n```ts\nimport { Repo } from 'hypermerge'\nimport Hyperswarm from 'hyperswarm'\n\nconst path = '.data'\n\nconst repo = new Repo({ path })\n\nrepo.setSwarm(Hyperswarm())\n```\n\n### Create / Edit / View / Delete a document\n\n```ts\nconst url = repo.create({ hello: 'world' })\n\nrepo.doc\u003cany\u003e(url, (doc) =\u003e {\n  console.log(doc) // { hello: \"world\" }\n})\n\n// this is an automerge change function - see automerge for more info\n// basically you get to treat the state as a plain old javacript object\n// operations you make will be added to an internal append only log and\n// replicated to peers\n\nrepo.change(url, (state: any) =\u003e {\n  state.foo = 'bar'\n})\n\nrepo.doc\u003cany\u003e(url, (doc) =\u003e {\n  console.log(doc) // { hello: \"world\", foo: \"bar\" }\n})\n\n// to watch a document that changes over time ...\nconst handle = repo.watch(url, (doc: any) =\u003e {\n  console.log(doc)\n  if (doc.foo === 'bar') {\n    handle.close()\n  }\n})\n```\n\n_NOTE_: If you're familiar with Automerge: the `change` function in Hypermerge\nis asynchronous, while the `Automerge.change` function is synchronous. What this\nmeans is that although `Automerge.change` returns an object representing the new\nstate of your document, `repo.change` (or `handle.change`) does NOT. So:\n\n```ts\n// ok in Automerge!\ndoc1 = Automerge.change(doc1, 'msg', (doc) =\u003e {\n  doc.foo = 'bar'\n})\n\n// NOT ok in Hypermerge!\ndoc1 = repo.change(url1, (doc) =\u003e {\n  doc.foo = 'bar'\n})\n```\n\nInstead, you should expect to get document state updates via `repo.watch`\n(or `handle.subscribe`) as shown in the example above.\n\n### Two repos on different machines\n\n```ts\nconst docUrl = repoA.create({ numbers: [2, 3, 4] })\n// this will block until the state has replicated to machine B\n\nrepoA.watch\u003cMyDoc\u003e(docUrl, (state) =\u003e {\n  console.log('RepoA', state)\n  // { numbers: [2,3,4] }\n  // { numbers: [2,3,4,5], foo: \"bar\" }\n  // { numbers: [2,3,4,5], foo: \"bar\" } // (local changes repeat)\n  // { numbers: [1,2,3,4,5], foo: \"bar\", bar: \"foo\" }\n})\n\nrepoB.watch\u003cMyDoc\u003e(docUrl, (state) =\u003e {\n  console.log('RepoB', state)\n  // { numbers: [1,2,3,4,5], foo: \"bar\", bar: \"foo\" }\n})\n\nrepoA.change\u003cMyDoc\u003e(docUrl, (state) =\u003e {\n  state.numbers.push(5)\n  state.foo = 'bar'\n})\n\nrepoB.change\u003cMyDoc\u003e(docUrl, (state) =\u003e {\n  state.numbers.unshift(1)\n  state.bar = 'foo'\n})\n```\n\n### Accessing Files\n\nHypermerge supports a special kind of core called a hyperfile. Hyperfiles are\nunchanging, written-once hypercores that store binary data.\n\nHere's a simple example of reading and writing files.\n\n```ts\n// Write an hyperfile\nconst fileStream = fs.createReadStream('image.png')\nconst { url } = await repo.files.write(fileStream, 'image/png')\n\n// Read an hyperfile\nconst fileStream = fs.createWriteStream('image.png')\nconst hyperfileStream = await repo.files.read(url)\n\nhyperfileStream.pipe(fileStream)\n```\n\nNote that hyperfiles are conveniently well-suited to treatment as a native\nprotocol for Electron applications. This allows you to refer to them throughout\nyour application directly as though they were regular files for images and other\nassets without any special treatment. Here's how to register that:\n\n```js\nprotocol.registerStreamProtocol(\n  'hyperfile',\n  (request, callback) =\u003e {\n    try {\n      const stream = await repo.files.read(request.url)\n      callback(stream)\n    } catch (e) {\n      log(e)\n    }\n  },\n  (error) =\u003e {\n    if (error) {\n      log('Failed to register protocol')\n    }\n  }\n)\n```\n\n### Splitting the Front-end and Back-end\n\nBoth Hypermerge and Automerge supports separating the front-end (where materialized documents live and changes are made) from the backend (where CRDT computations are handled as well as networking and compression.) This is useful for maintaining application performance by moving expensive computation operations off of the render thread to another location where they don't block user input.\n\nThe communication between front-end and back-end is all done via simple Javascript objects and can be serialized/deserialized through JSON if required.\n\n```js\n  // establish a back-end\n  const back = new RepoBackend({ path: HYPERMERGE_PATH, port: 0 })\n  const swarm = Hyperswarm({ /* your config here */ })\n  back.setSwarm(swarm)\n\n  // elsewhere, create a front-end (you'll probably want to do this in different threads)\n  const front = new RepoFrontend()\n\n  // the `subscribe` method sends a message stream, the `receive` receives it\n  // for demonstration here we simply output JSON and parse it back in the same location\n  // note that front-end and back-end must each subscribe to each other's streams\n  back.subscribe((msg) =\u003e front.receive(JSON.parse(JSON.stringify(msg))))\n  front.subscribe((msg) =\u003e back.receive(JSON.parse(JSON.stringify(msg))))\n\n}\n```\n\n_Note: each back-end only supports a single front-end today._\n\n### Related libraries\n\n[automerge]: https://github.com/automerge/automerge\n[hypercore]: https://github.com/mafintosh/hypercore\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fautomerge%2Fhypermerge","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fautomerge%2Fhypermerge","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fautomerge%2Fhypermerge/lists"}