ddat interop with dat js
cable interop with cabal js
ddat's frontend = dat-thang
cable fe = cable-thang
dillo support based on cel's dillo-dat
bootstrap node
xplatform support

Signed-off-by: divo <418@cryspace.space>
This commit is contained in:
divorce committed 2026-10-19 00:17:02 -04:00
1 parent f0f91540be
commit b735900f2e
323 files changed
+134347 -343

No files matched your search

@@ -0,0 +1,87 @@
'use strict'
// Opens the native Cable user log with the bundled legacy Hypercore 7.7.1,
// appends a JS record, and leaves the C side to read it back in the runner.
const assert = require('assert')
const fs = require('fs')
const path = require('path')
const root = path.resolve(__dirname, '../../..')
const home = path.resolve(process.argv[2])
const roomKey = process.argv[3]
assert.match(roomKey, /^[0-9a-f]{64}$/)
const roomDir = path.join(home, '.cable', 'rooms', roomKey)
const feedDir = path.join(roomDir, '0')
const feedKey = fs.readFileSync(path.join(feedDir, 'key'))
const secretKey = fs.readFileSync(path.join(roomDir, 'user-feed.key'))
assert.strictEqual(feedKey.length, 32)
assert.strictEqual(secretKey.length, 64)
const Hypercore = require(path.join(root, 'extra/dillo-dat/node_modules/hypercore'))
const randomAccessFile = require(path.join(root, 'extra/dillo-dat/node_modules/random-access-file'))
function memoryStorage () {
let bytes = Buffer.alloc(0)
return {
read (offset, length, callback) {
const value = Buffer.alloc(length)
bytes.copy(value, 0, offset, offset + length)
callback(null, value)
},
write (offset, value, callback) {
if (bytes.length < offset + value.length) {
const grown = Buffer.alloc(offset + value.length)
bytes.copy(grown)
bytes = grown
}
value.copy(bytes, offset)
callback(null)
},
stat (callback) { callback(null, { size: bytes.length }) },
close (callback) { callback(null) }
}
}
const storage = name => name === 'secret_key'
? memoryStorage()
: randomAccessFile(name, { directory: feedDir })
function ready (feed) {
return new Promise((resolve, reject) => feed.ready(err => err ? reject(err) : resolve()))
}
function get (feed, index) {
return new Promise((resolve, reject) => feed.get(index, (err, value) => err ? reject(err) : resolve(value)))
}
function append (feed, value) {
return new Promise((resolve, reject) => feed.append(Buffer.from(JSON.stringify(value)), err => err ? reject(err) : resolve()))
}
function close (feed) {
return new Promise((resolve, reject) => feed.close(err => err ? reject(err) : resolve()))
}
async function main () {
const feed = Hypercore(storage, feedKey, { secretKey })
feed.on('error', err => { throw err })
try {
await ready(feed)
assert.strictEqual(feed.length, 2, 'C should have written two Cabal records')
const nick = JSON.parse((await get(feed, 0)).toString('utf8'))
const message = JSON.parse((await get(feed, 1)).toString('utf8'))
assert.strictEqual(nick.type, 'about')
assert.strictEqual(nick.content.name, 'cable-c-user')
assert.strictEqual(message.type, 'chat/text')
assert.strictEqual(message.content.channel, 'interop')
assert.strictEqual(message.content.text, 'hello from C Hypercore')
await append(feed, {
type: 'chat/text',
content: { channel: 'interop', text: 'hello from Cabal JavaScript Hypercore' },
timestamp: Date.now()
})
process.stdout.write(`Cabal Hypercore v7.7.1 read C user feed and appended reply: PASS key=${feedKey.toString('hex')}\n`)
} finally {
await close(feed)
}
}
main().catch(err => { console.error(err); process.exitCode = 1 })
@@ -0,0 +1,44 @@
param([switch]$KeepFixture)
$ErrorActionPreference = 'Stop'
$root = (Resolve-Path (Join-Path $PSScriptRoot '..\..\..')).Path
$buildDir = [System.IO.Path]::GetFullPath((Join-Path $root 'build\linux'))
$binary = Join-Path $buildDir 'cable'
$nodeScript = Join-Path $PSScriptRoot 'cable_hypercore_compat.js'
$runRoot = [System.IO.Path]::GetFullPath((Join-Path $root 'local-data\test-runs'))
$fixtureHome = Join-Path $runRoot ('cable-hypercore-interop-' + [guid]::NewGuid().ToString('N'))
$linuxRoot = '/mnt/' + $root.Substring(0, 1).ToLower() + '/' + $root.Substring(3).Replace('\', '/')
$linuxHome = $linuxRoot + '/local-data/test-runs/' + (Split-Path $fixtureHome -Leaf)
$room = '42' * 32
function Invoke-WslCable([string[]]$Arguments) {
& wsl.exe --exec env "HOME=$linuxHome" "$linuxRoot/build/linux/cable" @Arguments
if ($LASTEXITCODE -ne 0) { throw "C cable command failed with exit code $LASTEXITCODE" }
}
New-Item -ItemType Directory -Path $fixtureHome -Force | Out-Null
try {
& wsl.exe --exec make -C $linuxRoot build/linux/cable
if ($LASTEXITCODE -ne 0) { throw "WSL Cable build failed with exit code $LASTEXITCODE" }
Invoke-WslCable @('nick', "cabal://$room", 'cable-c-user') | Out-Null
Invoke-WslCable @('publish', "cabal://$room", 'interop', 'hello from C Hypercore') | Out-Null
& node $nodeScript $fixtureHome $room
if ($LASTEXITCODE -ne 0) { throw "JavaScript Hypercore check failed with exit code $LASTEXITCODE" }
$records = Invoke-WslCable @('feed', "cabal://$room")
if (($records -join "`n") -notmatch 'hello from Cabal JavaScript Hypercore') {
throw 'C did not read the JavaScript-appended block'
}
Write-Output 'Cabal Core room/0 storage + Hypercore v7.7.1 read/write: PASS'
Write-Output 'C read JavaScript-appended Hypercore block: PASS'
} finally {
if (-not $KeepFixture -and (Test-Path -LiteralPath $fixtureHome)) {
$resolvedFixture = [System.IO.Path]::GetFullPath((Resolve-Path -LiteralPath $fixtureHome).Path)
$runPrefix = $runRoot.TrimEnd('\') + '\'
if (-not $resolvedFixture.StartsWith($runPrefix, [System.StringComparison]::OrdinalIgnoreCase)) {
throw "Refusing to remove fixture outside local-data/test-runs/: $resolvedFixture"
}
Remove-Item -LiteralPath $resolvedFixture -Recurse -Force
}
}
+69
View File
@@ -0,0 +1,69 @@
const fs = require('fs')
const os = require('os')
const path = require('path')
const hyperdrive = require('../../extra/dillo-dat/node_modules/hyperdrive')
const hyperdiscovery = require('../../extra/dillo-dat/node_modules/hyperdiscovery')
function ready (archive) {
return new Promise((resolve, reject) => archive.ready(err => err ? reject(err) : resolve()))
}
function writeFile (archive, name, data) {
return new Promise((resolve, reject) => archive.writeFile(name, data, err => err ? reject(err) : resolve()))
}
function readFile (archive, name) {
return new Promise((resolve, reject) => archive.readFile(name, (err, data) => err ? reject(err) : resolve(data)))
}
function listening (swarm) {
if (swarm.address()) return Promise.resolve(swarm.address())
return new Promise(resolve => swarm.once('listening', () => resolve(swarm.address())))
}
function sleep (ms) { return new Promise(resolve => setTimeout(resolve, ms)) }
async function main () {
const root = fs.mkdtempSync(path.join(process.env.DDAT_TEST_DIR || os.tmpdir(), 'ddat-js-p2p-'))
let source, replica, sourceSwarm, replicaSwarm
try {
source = hyperdrive(path.join(root, 'source'))
await ready(source)
const key = source.key.toString('hex')
const address = 'dat://' + key
await writeFile(source, 'hello.txt', 'hello from old dillo-dat v1')
await writeFile(source, 'hello.txt', 'hello from old dillo-dat v2')
if (source.key.toString('hex') !== key) throw new Error('JS archive address changed after update')
replica = hyperdrive(path.join(root, 'replica'), source.key, {sparse: true})
await ready(replica)
sourceSwarm = hyperdiscovery(source, {port: 0, discovery: false, dht: false, dns: false, utp: false})
replicaSwarm = hyperdiscovery(replica, {port: 0, discovery: false, dht: false, dns: false, utp: false})
const sourceAddress = await listening(sourceSwarm)
const replicaAddress = await listening(replicaSwarm)
const topic = source.discoveryKey
sourceSwarm.addPeer(topic, {host: '127.0.0.1', port: replicaAddress.port})
replicaSwarm.addPeer(topic, {host: '127.0.0.1', port: sourceAddress.port})
const deadline = Date.now() + 15000
let result
while (Date.now() < deadline) {
try { result = await readFile(replica, 'hello.txt'); break } catch (_) { await sleep(100) }
}
if (!result || result.toString() !== 'hello from old dillo-dat v2') throw new Error('old JS replication did not deliver the latest file')
process.stdout.write('old Dillo Dat JS create/update/address/xfer: PASS ' + address +
' metadata=' + source.metadata.length + ' content=' + source.content.length + '\n')
} finally {
if (sourceSwarm) sourceSwarm.destroy()
if (replicaSwarm) replicaSwarm.destroy()
if (source) source.close()
if (replica) replica.close()
if (process.env.DDAT_TEST_KEEP === '1') {
process.stdout.write('kept old JS source and replica at ' + root + '\n')
} else {
fs.rmSync(root, {recursive: true, force: true})
}
}
}
main().catch(err => { console.error(err); process.exitCode = 1 })