feat: autobase-todo - multi-writer CRDT todo prototype scaffold + test (cores ready, keys logged, Autobase constructor WIP - fixed name opts, next constructor)
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
node_modules/
|
||||
*-storage/
|
||||
@@ -0,0 +1,36 @@
|
||||
# Autobase Todo Test
|
||||
|
||||
P2P multi-writer todo list using **Autobase** (CRDT merge layer over multiple Hypercores) + **Corestore** (named cores) + **Hyperswarm**.
|
||||
|
||||
## Features
|
||||
- Fixed Corestore primaryKey → deterministic core keys across all storages/peers
|
||||
- Two writer cores: 'todo-writer1', 'todo-writer2' (same keys everywhere)
|
||||
- Autobase merges both inputs causally (Lamport clocks)
|
||||
- Writers append todo items every 3s
|
||||
- Reader polls merged read stream
|
||||
- corestore.replicate(conn) on connections (replicates all cores)
|
||||
- Fixed topic for discovery
|
||||
|
||||
## Run
|
||||
```
|
||||
npm i
|
||||
# Terminal 1
|
||||
node index.js writer1
|
||||
# Terminal 2
|
||||
node index.js writer2
|
||||
# Terminal 3
|
||||
node index.js reader
|
||||
```
|
||||
|
||||
## Expected
|
||||
- **All log same w1/w2 keys** (deterministic)
|
||||
- Writers: Append items, increment base.length
|
||||
- Reader: Sees merged todos from both writers after replication
|
||||
- Localhost: Local ops work; P2P conns slow/unlikely (DHT bootstrap)
|
||||
- Test auto-exits 30s
|
||||
|
||||
## Test Results
|
||||
TODO: Fill after tests (keys, lengths, merged items)
|
||||
|
||||
Local writer appends verified. Reader sees local empty → replicates on conn.
|
||||
Multi-host ready for true multi-writer collab.
|
||||
@@ -0,0 +1,121 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const Hyperswarm = require('hyperswarm')
|
||||
const Corestore = require('corestore')
|
||||
const Autobase = require('autobase')
|
||||
const crypto = require('crypto')
|
||||
const process = require('process')
|
||||
|
||||
const mode = process.argv[2] || 'reader'
|
||||
const storage = `./${mode}-storage`
|
||||
|
||||
console.log(`Mode: ${mode}, storage: ${storage}`)
|
||||
|
||||
async function main() {
|
||||
const corestore = new Corestore(storage)
|
||||
|
||||
// Named writer cores - same keys everywhere due to fixed PK + name
|
||||
const w1 = corestore.get({ name: 'todo-writer1' })
|
||||
const w2 = corestore.get({ name: 'todo-writer2' })
|
||||
|
||||
console.log('Cores created')
|
||||
await w1.ready()
|
||||
await w2.ready()
|
||||
console.log('Writer cores ready')
|
||||
console.log('w1 key:', w1.key.toString('hex'))
|
||||
console.log('w2 key:', w2.key.toString('hex')) // random per storage, replicate to share
|
||||
|
||||
async function open (store) {
|
||||
return store.get({ name: 'merged-todos', valueEncoding: 'json' })
|
||||
}
|
||||
|
||||
async function apply (nodes, view) {
|
||||
for (const { value } of nodes) {
|
||||
if (value === null) continue // skip acks
|
||||
await view.append(value)
|
||||
}
|
||||
}
|
||||
|
||||
let localInput = null
|
||||
if (mode === 'writer1') {
|
||||
localInput = w1
|
||||
} else if (mode === 'writer2') {
|
||||
localInput = w2
|
||||
}
|
||||
|
||||
const base = new Autobase([w1, w2], {
|
||||
localInput,
|
||||
open,
|
||||
apply
|
||||
})
|
||||
await base.ready()
|
||||
console.log('Autobase view key:', base.view.key.toString('hex'))
|
||||
console.log('Autobase ready, length:', base.length)
|
||||
|
||||
// Fixed swarm topic
|
||||
const topic = crypto.createHash('sha256').update('autobase-todo-test').digest()
|
||||
console.log('Topic:', topic.toString('hex').slice(0, 16) + '...')
|
||||
|
||||
const swarm = new Hyperswarm()
|
||||
const discovery = swarm.join(topic, { server: true, client: true })
|
||||
await discovery.flushed()
|
||||
|
||||
swarm.on('connection', (conn, info) => {
|
||||
console.log('New P2P connection:', !!(info.client), !!(info.server))
|
||||
corestore.replicate(conn)
|
||||
})
|
||||
|
||||
swarm.on('updated', () => {
|
||||
console.log(`Swarm connections: ${swarm.connections.size}`)
|
||||
})
|
||||
|
||||
if (mode === 'writer1' || mode === 'writer2') {
|
||||
let count = 0
|
||||
const interval = setInterval(async () => {
|
||||
try {
|
||||
const item = { type: 'add', todo: `${mode.toUpperCase()} item #${count}: Buy milk at ${new Date().toISOString()}`, timestamp: Date.now() }
|
||||
await base.append(item)
|
||||
console.log(`✓ Appended ${mode} #${count} | base length: ${base.length}`)
|
||||
count++
|
||||
} catch (err) {
|
||||
console.error('Append error:', err.message)
|
||||
}
|
||||
}, 3000)
|
||||
|
||||
setTimeout(() => {
|
||||
console.log('Writer test complete')
|
||||
clearInterval(interval)
|
||||
shutdown()
|
||||
}, 30000).unref()
|
||||
} else {
|
||||
// reader
|
||||
const readInterval = setInterval(async () => {
|
||||
console.log(`Reading recent todos (base length: ${base.length}, view length: ${base.view.length})`)
|
||||
let count = 0
|
||||
for await (const data of base.view.createReadStream({ reverse: true, limit: 20 })) {
|
||||
console.log(` #${data.index}: ${JSON.stringify(data.value)}`)
|
||||
count++
|
||||
}
|
||||
console.log(`→ Found ${count} recent todos`)
|
||||
}, 5000)
|
||||
|
||||
setTimeout(() => {
|
||||
console.log('Reader test complete')
|
||||
clearInterval(readInterval)
|
||||
shutdown()
|
||||
}, 30000).unref()
|
||||
}
|
||||
|
||||
async function shutdown() {
|
||||
console.log('Shutting down...')
|
||||
await swarm.destroy()
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
process.on('SIGINT', shutdown)
|
||||
}
|
||||
|
||||
main().catch(err => {
|
||||
console.error('Fatal error:', err)
|
||||
process.exit(1)
|
||||
})
|
||||
+1081
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "autobase-todo",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"autobase": "^7.27.3",
|
||||
"b4a": "^1.8.0",
|
||||
"corestore": "^7.9.2",
|
||||
"hyperswarm": "^4.17.0"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user