mirror of https://codeberg.org/pzp/pzp-dict.git
Use async db fns and make ready for publish
This commit is contained in:
parent
26954a12f4
commit
ef03f97950
|
@ -1,25 +0,0 @@
|
||||||
name: CI
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [master]
|
|
||||||
pull_request:
|
|
||||||
branches: [master]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
timeout-minutes: 10
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
node-version: [16.x, 18.x]
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
|
||||||
uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: ${{ matrix.node-version }}
|
|
||||||
- run: npm install
|
|
||||||
- run: npm test
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
matrix:
|
||||||
|
NODE_VERSION:
|
||||||
|
- 18
|
||||||
|
- 20
|
||||||
|
|
||||||
|
steps:
|
||||||
|
test:
|
||||||
|
when:
|
||||||
|
event: [push]
|
||||||
|
image: node:${NODE_VERSION}
|
||||||
|
commands:
|
||||||
|
- npm install
|
||||||
|
- npm test
|
|
@ -1,9 +1,7 @@
|
||||||
**Work in progress**
|
# pzp-dict
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
We're not on npm yet. In your package.json, include this as
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
"ppppp-dict": "github:staltz/ppppp-dict"
|
npm install pzp-dict
|
||||||
```
|
```
|
||||||
|
|
253
lib/index.js
253
lib/index.js
|
@ -1,10 +1,11 @@
|
||||||
const MsgV4 = require('ppppp-db/msg-v4')
|
const MsgV4 = require('pzp-db/msg-v4')
|
||||||
|
const pull = require('pull-stream')
|
||||||
|
|
||||||
const PREFIX = 'dict_v1__'
|
const PREFIX = 'dict_v1__'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {ReturnType<import('ppppp-db').init>} PPPPPDB
|
* @typedef {ReturnType<import('pzp-db').init>} PZPDB
|
||||||
* @typedef {import('ppppp-db').RecPresent} RecPresent
|
* @typedef {import('pzp-db').RecPresent} RecPresent
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
* hook: (
|
* hook: (
|
||||||
* cb: (
|
* cb: (
|
||||||
|
@ -32,12 +33,12 @@ const PREFIX = 'dict_v1__'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template [T = any]
|
* @template [T = any]
|
||||||
* @typedef {import('ppppp-db/msg-v4').Msg<T>} Msg<T>
|
* @typedef {import('pzp-db/msg-v4').Msg<T>} Msg<T>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template T
|
* @template T
|
||||||
* @typedef {T extends void ?
|
* @typedef {[T] extends [void] ?
|
||||||
* (...args: [Error] | []) => void :
|
* (...args: [Error] | []) => void :
|
||||||
* (...args: [Error] | [null, T]) => void
|
* (...args: [Error] | [null, T]) => void
|
||||||
* } CB
|
* } CB
|
||||||
|
@ -60,7 +61,7 @@ function fromSubdomain(subdomain) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {{ db: PPPPPDB, close: ClosableHook }} peer
|
* @param {{ db: PZPDB, close: ClosableHook }} peer
|
||||||
* @param {Config} config
|
* @param {Config} config
|
||||||
*/
|
*/
|
||||||
function initDict(peer, config) {
|
function initDict(peer, config) {
|
||||||
|
@ -245,26 +246,29 @@ function initDict(peer, config) {
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @param {string} subdomain
|
* @param {string} subdomain
|
||||||
* @returns {number}
|
* @param {CB<number>} cb
|
||||||
*/
|
*/
|
||||||
function _squeezePotential(subdomain) {
|
function _squeezePotential(subdomain, cb) {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
if (!loadedAccountID) throw new Error('Cannot squeeze potential before loading')
|
if (!loadedAccountID) return cb(Error('Cannot squeeze potential before loading'))
|
||||||
// TODO: improve this so that the squeezePotential is the size of the
|
// TODO: improve this so that the squeezePotential is the size of the
|
||||||
// tangle suffix built as a slice from the fieldRoots
|
// tangle suffix built as a slice from the fieldRoots
|
||||||
const mootID = MsgV4.getMootID(loadedAccountID, fromSubdomain(subdomain))
|
const mootID = MsgV4.getMootID(loadedAccountID, fromSubdomain(subdomain))
|
||||||
const tangle = peer.db.getTangle(mootID)
|
peer.db.getTangle(mootID, (err, tangle) => {
|
||||||
if (!tangle) return 0
|
if (err) return cb(err)
|
||||||
const maxDepth = tangle.maxDepth
|
|
||||||
const fieldRoots = _getFieldRoots(subdomain)
|
if (!tangle) return cb(null, 0)
|
||||||
let minDepth = Infinity
|
const maxDepth = tangle.maxDepth
|
||||||
for (const field in fieldRoots) {
|
const fieldRoots = _getFieldRoots(subdomain)
|
||||||
for (const msgID of fieldRoots[field]) {
|
let minDepth = Infinity
|
||||||
const depth = tangle.getDepth(msgID)
|
for (const field in fieldRoots) {
|
||||||
if (depth < minDepth) minDepth = depth
|
for (const msgID of fieldRoots[field]) {
|
||||||
|
const depth = tangle.getDepth(msgID)
|
||||||
|
if (depth < minDepth) minDepth = depth
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
return cb(null, maxDepth - minDepth)
|
||||||
return maxDepth - minDepth
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -288,7 +292,6 @@ function initDict(peer, config) {
|
||||||
(err, rec) => {
|
(err, rec) => {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
if (err) return cb(new Error('Failed to create msg when force-updating Dict', { cause: err }))
|
if (err) return cb(new Error('Failed to create msg when force-updating Dict', { cause: err }))
|
||||||
// @ts-ignore
|
|
||||||
cb(null, true)
|
cb(null, true)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -315,8 +318,8 @@ function initDict(peer, config) {
|
||||||
// microtask is needed to ensure that loadPromise is assigned BEFORE this
|
// microtask is needed to ensure that loadPromise is assigned BEFORE this
|
||||||
// body is executed (which in turn does inversion of control when `cb` or
|
// body is executed (which in turn does inversion of control when `cb` or
|
||||||
// `resolve` is called)
|
// `resolve` is called)
|
||||||
queueMicrotask(() => {
|
queueMicrotask(async () => {
|
||||||
for (const rec of peer.db.records()) {
|
for await (const rec of peer.db.records()) {
|
||||||
if (!rec.msg) continue
|
if (!rec.msg) continue
|
||||||
maybeLearnAboutDict(rec.id, rec.msg)
|
maybeLearnAboutDict(rec.id, rec.msg)
|
||||||
}
|
}
|
||||||
|
@ -347,74 +350,103 @@ function initDict(peer, config) {
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
* @param {string} tangleID
|
* @param {string} tangleID
|
||||||
* @returns {number}
|
* @param {CB<number>} cb
|
||||||
*/
|
*/
|
||||||
function minRequiredDepth(tangleID) {
|
function minRequiredDepth(tangleID, cb) {
|
||||||
const tangle = peer.db.getTangle(tangleID)
|
peer.db.getTangle(tangleID, (err, tangle) => {
|
||||||
|
if (err) return cb(err)
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
if (!tangle) return 0
|
if (!tangle) return cb(null, 0)
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
if (!MsgV4.isMoot(tangle.root)) throw new Error(`Tangle "${tangleID}" is not a moot`)
|
if (!MsgV4.isMoot(tangle.root)) return cb(Error(`Tangle "${tangleID}" is not a moot`))
|
||||||
const domain = tangle.root.metadata.domain
|
const domain = tangle.root.metadata.domain
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
if (!domain.startsWith(PREFIX)) throw new Error(`Tangle "${tangleID}" is not a Dict moot`)
|
if (!domain.startsWith(PREFIX)) return cb(Error(`Tangle "${tangleID}" is not a Dict moot`))
|
||||||
|
|
||||||
// Discover field roots
|
// Discover field roots
|
||||||
const fieldRoots = new Set()
|
const fieldRoots = new Set()
|
||||||
const msgIDs = tangle.topoSort()
|
|
||||||
for (const msgID of msgIDs) {
|
pull(
|
||||||
const msg = peer.db.get(msgID)
|
pull.values(tangle.topoSort()),
|
||||||
if (!msg?.data) continue
|
pull.asyncMap((msgID, cb) => {
|
||||||
for (const supersededMsgID of msg.data.supersedes) {
|
peer.db.get(msgID, (err, msg) => {
|
||||||
fieldRoots.delete(supersededMsgID)
|
if (err) return cb(err)
|
||||||
}
|
|
||||||
fieldRoots.add(msgID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get minimum depth of all field roots
|
if (!msg?.data) return cb(null, null)
|
||||||
let minDepth = Infinity
|
for (const supersededMsgID of msg.data.supersedes) {
|
||||||
for (const msgID of fieldRoots) {
|
fieldRoots.delete(supersededMsgID)
|
||||||
const depth = tangle.getDepth(msgID)
|
}
|
||||||
if (depth < minDepth) minDepth = depth
|
fieldRoots.add(msgID)
|
||||||
}
|
|
||||||
|
|
||||||
return minDepth
|
return cb(null, null)
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
pull.drain(() => {}, (err) => {
|
||||||
|
// prettier-ignore
|
||||||
|
if (err) return cb(Error("minRequiredDepth() stream in dict failed", { cause: err }))
|
||||||
|
|
||||||
|
// Get minimum depth of all field roots
|
||||||
|
let minDepth = Infinity
|
||||||
|
for (const msgID of fieldRoots) {
|
||||||
|
const depth = tangle.getDepth(msgID)
|
||||||
|
if (depth < minDepth) minDepth = depth
|
||||||
|
}
|
||||||
|
|
||||||
|
return cb(null, minDepth)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
* @param {string} tangleID
|
* @param {string} tangleID
|
||||||
* @returns {number}
|
* @param {CB<number>} cb
|
||||||
*/
|
*/
|
||||||
function minGhostDepth(tangleID) {
|
function minGhostDepth(tangleID, cb) {
|
||||||
return Math.max(0, minRequiredDepth(tangleID) - ghostSpan)
|
minRequiredDepth(tangleID, (err, minDepth) => {
|
||||||
|
if (err) return cb(err)
|
||||||
|
|
||||||
|
return cb(null, Math.max(0, minDepth - ghostSpan))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
* @param {string} id
|
* @param {string} id
|
||||||
* @param {string} subdomain
|
* @param {string} subdomain
|
||||||
* @returns {{[field in string]: any} | null}
|
* @param {CB<{[field in string]: any} | null>} cb
|
||||||
*/
|
*/
|
||||||
function read(id, subdomain) {
|
function read(id, subdomain, cb) {
|
||||||
const domain = fromSubdomain(subdomain)
|
const domain = fromSubdomain(subdomain)
|
||||||
const mootID = MsgV4.getMootID(id, domain)
|
const mootID = MsgV4.getMootID(id, domain)
|
||||||
const tangle = peer.db.getTangle(mootID)
|
peer.db.getTangle(mootID, (err, tangle) => {
|
||||||
if (!tangle) {
|
if (err) return cb(err)
|
||||||
if (id === loadedAccountID) return {}
|
|
||||||
else return null
|
if (!tangle) {
|
||||||
}
|
if (id === loadedAccountID) return cb(null, {})
|
||||||
const msgIDs = tangle.topoSort()
|
else return cb(null, null)
|
||||||
const dict = /** @type {{[field in string]: any}} */ ({})
|
|
||||||
for (const msgID of msgIDs) {
|
|
||||||
const msg = peer.db.get(msgID)
|
|
||||||
if (isValidDictMsg(msg)) {
|
|
||||||
const { update } = msg.data
|
|
||||||
Object.assign(dict, update)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return dict
|
const dict = /** @type {{[field in string]: any}} */ ({})
|
||||||
|
|
||||||
|
pull(
|
||||||
|
pull.values(tangle.topoSort()),
|
||||||
|
pull.asyncMap((msgID, cb) => {
|
||||||
|
peer.db.get(msgID, cb)
|
||||||
|
}),
|
||||||
|
pull.drain((msg) => {
|
||||||
|
if (isValidDictMsg(msg)) {
|
||||||
|
const { update } = msg.data
|
||||||
|
Object.assign(dict, update)
|
||||||
|
}
|
||||||
|
}, (err) => {
|
||||||
|
if (err) return cb(Error("dict read failed", { cause: err }))
|
||||||
|
cb(null, dict)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -432,20 +464,27 @@ function initDict(peer, config) {
|
||||||
* @public
|
* @public
|
||||||
* @param {MsgID} ghostableMsgID
|
* @param {MsgID} ghostableMsgID
|
||||||
* @param {MsgID} tangleID
|
* @param {MsgID} tangleID
|
||||||
|
* @param {CB<boolean>} cb
|
||||||
*/
|
*/
|
||||||
function isGhostable(ghostableMsgID, tangleID) {
|
function isGhostable(ghostableMsgID, tangleID, cb) {
|
||||||
if (ghostableMsgID === tangleID) return false
|
if (ghostableMsgID === tangleID) return cb(null, false)
|
||||||
|
let i = 0
|
||||||
|
|
||||||
const msg = peer.db.get(ghostableMsgID)
|
peer.db.get(ghostableMsgID, (err, msg) => {
|
||||||
|
if (err) return cb(err)
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
if (!msg) throw new Error(`isGhostable() msgID "${ghostableMsgID}" does not exist in the database`)
|
if (!msg) return cb(Error(`isGhostable() msgID "${ghostableMsgID}" does not exist in the database`))
|
||||||
|
|
||||||
const minFieldRootDepth = minRequiredDepth(tangleID)
|
minRequiredDepth(tangleID, (err, minFieldRootDepth) => {
|
||||||
const minGhostDepth = minFieldRootDepth - ghostSpan
|
if (err) return cb(err)
|
||||||
const msgDepth = msg.metadata.tangles[tangleID].depth
|
|
||||||
if (minGhostDepth <= msgDepth && msgDepth < minFieldRootDepth) return true
|
const minGhostDepth = minFieldRootDepth - ghostSpan
|
||||||
return false
|
const msgDepth = msg.metadata.tangles[tangleID].depth
|
||||||
|
if (minGhostDepth <= msgDepth && msgDepth < minFieldRootDepth) return cb(null, true)
|
||||||
|
return cb(null, false)
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -476,19 +515,22 @@ function initDict(peer, config) {
|
||||||
loaded(() => {
|
loaded(() => {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
if (!loadedAccountID) return cb(new Error('Expected account to be loaded'))
|
if (!loadedAccountID) return cb(new Error('Expected account to be loaded'))
|
||||||
const dict = read(loadedAccountID, subdomain)
|
read(loadedAccountID, subdomain, (err, dict) => {
|
||||||
// prettier-ignore
|
if (err) return cb(err)
|
||||||
if (!dict) return cb(new Error(`Cannot update non-existent dict "${subdomain}`))
|
|
||||||
|
|
||||||
let hasChanges = false
|
// prettier-ignore
|
||||||
for (const [field, value] of Object.entries(update)) {
|
if (!dict) return cb(Error(`Cannot update non-existent dict "${subdomain}`))
|
||||||
if (value !== dict[field]) {
|
|
||||||
hasChanges = true
|
let hasChanges = false
|
||||||
break
|
for (const [field, value] of Object.entries(update)) {
|
||||||
|
if (value !== dict[field]) {
|
||||||
|
hasChanges = true
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
if (!hasChanges) return cb(null, false)
|
||||||
if (!hasChanges) return cb(null, false)
|
forceUpdate(subdomain, update, cb)
|
||||||
forceUpdate(subdomain, update, cb)
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -498,20 +540,25 @@ function initDict(peer, config) {
|
||||||
*/
|
*/
|
||||||
function squeeze(subdomain, cb) {
|
function squeeze(subdomain, cb) {
|
||||||
if (!loadedAccountID) return cb(new Error('Cannot squeeze before loading'))
|
if (!loadedAccountID) return cb(new Error('Cannot squeeze before loading'))
|
||||||
const potential = _squeezePotential(subdomain)
|
_squeezePotential(subdomain, (err, potential) => {
|
||||||
if (potential < 1) return cb(null, false)
|
if (err) return cb(err)
|
||||||
|
|
||||||
loaded(() => {
|
if (potential < 1) return cb(null, false)
|
||||||
// prettier-ignore
|
|
||||||
if (!loadedAccountID) return cb(new Error('Expected account to be loaded'))
|
loaded(() => {
|
||||||
const dict = read(loadedAccountID, subdomain)
|
|
||||||
// prettier-ignore
|
|
||||||
if (!dict) return cb(new Error(`Cannot squeeze non-existent Dict "${subdomain}"`))
|
|
||||||
forceUpdate(subdomain, dict, (err, _forceUpdated) => {
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
if (err) return cb(new Error(`Failed to force update when squeezing Dict "${subdomain}"`, { cause: err }))
|
if (!loadedAccountID) return cb(new Error('Expected account to be loaded'))
|
||||||
// @ts-ignore
|
read(loadedAccountID, subdomain, (err, dict) => {
|
||||||
cb(null, true)
|
if (err) return cb(err)
|
||||||
|
|
||||||
|
// prettier-ignore
|
||||||
|
if (!dict) return cb(new Error(`Cannot squeeze non-existent Dict "${subdomain}"`))
|
||||||
|
forceUpdate(subdomain, dict, (err, _forceUpdated) => {
|
||||||
|
// prettier-ignore
|
||||||
|
if (err) return cb(new Error(`Failed to force update when squeezing Dict "${subdomain}"`, { cause: err }))
|
||||||
|
cb(null, true)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
18
package.json
18
package.json
|
@ -1,13 +1,13 @@
|
||||||
{
|
{
|
||||||
"name": "ppppp-dict",
|
"name": "pzp-dict",
|
||||||
"version": "1.0.0",
|
"version": "0.0.1",
|
||||||
"description": "Dictionary data structure over append-only logs with pruning",
|
"description": "Dictionary data structure over append-only logs with pruning",
|
||||||
"author": "Andre Staltz <contact@staltz.com>",
|
"author": "Andre Staltz <contact@staltz.com>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"homepage": "https://github.com/staltz/ppppp-dict",
|
"homepage": "https://codeberg.org/pzp/pzp-dict",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git@github.com:staltz/ppppp-dict.git"
|
"url": "git@codeberg.org:pzp/pzp-dict.git"
|
||||||
},
|
},
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"files": [
|
"files": [
|
||||||
|
@ -25,16 +25,18 @@
|
||||||
"node": ">=16"
|
"node": ">=16"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"pull-stream": "^3.7.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/pull-stream": "^3.6.7",
|
||||||
"bs58": "^5.0.0",
|
"bs58": "^5.0.0",
|
||||||
"c8": "7",
|
"c8": "7",
|
||||||
"ppppp-db": "github:staltz/ppppp-db#667b33779d98aff12a9b0cd2d7c80469a95cd04e",
|
"pzp-caps": "^1.0.0",
|
||||||
"ppppp-caps": "github:staltz/ppppp-caps#93fa810b9a40b78aef4872d4c2a8412cccb52929",
|
"pzp-db": "^1.0.1",
|
||||||
"ppppp-keypair": "github:staltz/ppppp-keypair#61ef4420578f450dc2cc7b1efc1c5a691a871c74",
|
"pzp-keypair": "^1.0.0",
|
||||||
"rimraf": "^4.4.0",
|
"rimraf": "^4.4.0",
|
||||||
"secret-stack": "~8.1.0",
|
|
||||||
"secret-handshake-ext": "0.0.10",
|
"secret-handshake-ext": "0.0.10",
|
||||||
|
"secret-stack": "~8.1.0",
|
||||||
"ssb-box": "^1.0.1",
|
"ssb-box": "^1.0.1",
|
||||||
"typescript": "^5.1.3"
|
"typescript": "^5.1.3"
|
||||||
},
|
},
|
||||||
|
|
|
@ -3,19 +3,19 @@ const assert = require('node:assert')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const os = require('os')
|
const os = require('os')
|
||||||
const rimraf = require('rimraf')
|
const rimraf = require('rimraf')
|
||||||
const MsgV4 = require('ppppp-db/msg-v4')
|
const MsgV4 = require('pzp-db/msg-v4')
|
||||||
const Keypair = require('ppppp-keypair')
|
const Keypair = require('pzp-keypair')
|
||||||
const p = require('util').promisify
|
const p = require('util').promisify
|
||||||
const { createPeer } = require('./util')
|
const { createPeer } = require('./util')
|
||||||
|
|
||||||
const DIR = path.join(os.tmpdir(), 'ppppp-dict')
|
const DIR = path.join(os.tmpdir(), 'pzp-dict')
|
||||||
rimraf.sync(DIR)
|
rimraf.sync(DIR)
|
||||||
|
|
||||||
const aliceKeypair = Keypair.generate('ed25519', 'alice')
|
const aliceKeypair = Keypair.generate('ed25519', 'alice')
|
||||||
|
|
||||||
function getMsgID(peer, index, domain) {
|
async function getMsgID(peer, index, domain) {
|
||||||
let i = 0
|
let i = 0
|
||||||
for (const rec of peer.db.records()) {
|
for await (const rec of peer.db.records()) {
|
||||||
if (rec.msg.metadata.domain === domain && !!rec.msg.data) {
|
if (rec.msg.metadata.domain === domain && !!rec.msg.data) {
|
||||||
if (i === index) return rec.id
|
if (i === index) return rec.id
|
||||||
i++
|
i++
|
||||||
|
@ -53,16 +53,16 @@ test('Dict update() and get()', async (t) => {
|
||||||
await p(peer.dict.update)('profile', { name: 'alice' }),
|
await p(peer.dict.update)('profile', { name: 'alice' }),
|
||||||
'update .name'
|
'update .name'
|
||||||
)
|
)
|
||||||
const UPDATE0_ID = getMsgID(peer, 0, 'dict_v1__profile')
|
const UPDATE0_ID = await getMsgID(peer, 0, 'dict_v1__profile')
|
||||||
assert.deepEqual(peer.dict.read(aliceID, 'profile'), { name: 'alice' }, 'get')
|
assert.deepEqual(await p(peer.dict.read)(aliceID, 'profile'), { name: 'alice' }, 'get')
|
||||||
|
|
||||||
const fieldRoots1 = peer.dict._getFieldRoots('profile')
|
const fieldRoots1 = peer.dict._getFieldRoots('profile')
|
||||||
assert.deepEqual(fieldRoots1, { name: [UPDATE0_ID] }, 'fieldRoots')
|
assert.deepEqual(fieldRoots1, { name: [UPDATE0_ID] }, 'fieldRoots')
|
||||||
|
|
||||||
assert(await p(peer.dict.update)('profile', { age: 20 }), 'update .age')
|
assert(await p(peer.dict.update)('profile', { age: 20 }), 'update .age')
|
||||||
const UPDATE1_ID = getMsgID(peer, 1, 'dict_v1__profile')
|
const UPDATE1_ID = await getMsgID(peer, 1, 'dict_v1__profile')
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
peer.dict.read(aliceID, 'profile'),
|
await p(peer.dict.read)(aliceID, 'profile'),
|
||||||
{ name: 'alice', age: 20 },
|
{ name: 'alice', age: 20 },
|
||||||
'get'
|
'get'
|
||||||
)
|
)
|
||||||
|
@ -80,7 +80,7 @@ test('Dict update() and get()', async (t) => {
|
||||||
'redundant update .name'
|
'redundant update .name'
|
||||||
)
|
)
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
peer.dict.read(aliceID, 'profile'),
|
await p(peer.dict.read)(aliceID, 'profile'),
|
||||||
{ name: 'alice', age: 20 },
|
{ name: 'alice', age: 20 },
|
||||||
'get'
|
'get'
|
||||||
)
|
)
|
||||||
|
@ -90,9 +90,9 @@ test('Dict update() and get()', async (t) => {
|
||||||
true,
|
true,
|
||||||
'update .name'
|
'update .name'
|
||||||
)
|
)
|
||||||
const UPDATE2_ID = getMsgID(peer, 2, 'dict_v1__profile')
|
const UPDATE2_ID = await getMsgID(peer, 2, 'dict_v1__profile')
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
peer.dict.read(aliceID, 'profile'),
|
await p(peer.dict.read)(aliceID, 'profile'),
|
||||||
{ name: 'Alice', age: 20 },
|
{ name: 'Alice', age: 20 },
|
||||||
'get'
|
'get'
|
||||||
)
|
)
|
||||||
|
@ -109,8 +109,8 @@ test('Dict squeeze', async (t) => {
|
||||||
assert(await p(peer.dict.update)('profile', { age: 21 }), 'update .age')
|
assert(await p(peer.dict.update)('profile', { age: 21 }), 'update .age')
|
||||||
assert(await p(peer.dict.update)('profile', { age: 22 }), 'update .age')
|
assert(await p(peer.dict.update)('profile', { age: 22 }), 'update .age')
|
||||||
assert(await p(peer.dict.update)('profile', { age: 23 }), 'update .age')
|
assert(await p(peer.dict.update)('profile', { age: 23 }), 'update .age')
|
||||||
const UPDATE2_ID = getMsgID(peer, 2, 'dict_v1__profile')
|
const UPDATE2_ID = await getMsgID(peer, 2, 'dict_v1__profile')
|
||||||
const UPDATE5_ID = getMsgID(peer, 5, 'dict_v1__profile')
|
const UPDATE5_ID = await getMsgID(peer, 5, 'dict_v1__profile')
|
||||||
|
|
||||||
const fieldRoots4 = peer.dict._getFieldRoots('profile')
|
const fieldRoots4 = peer.dict._getFieldRoots('profile')
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
|
@ -119,9 +119,9 @@ test('Dict squeeze', async (t) => {
|
||||||
'fieldRoots'
|
'fieldRoots'
|
||||||
)
|
)
|
||||||
|
|
||||||
assert.equal(peer.dict._squeezePotential('profile'), 3, 'squeezePotential=3')
|
assert.equal(await p(peer.dict._squeezePotential)('profile'), 3, 'squeezePotential=3')
|
||||||
assert.equal(await p(peer.dict.squeeze)('profile'), true, 'squeezed')
|
assert.equal(await p(peer.dict.squeeze)('profile'), true, 'squeezed')
|
||||||
const UPDATE6_ID = getMsgID(peer, 6, 'dict_v1__profile')
|
const UPDATE6_ID = await getMsgID(peer, 6, 'dict_v1__profile')
|
||||||
|
|
||||||
const fieldRoots5 = peer.dict._getFieldRoots('profile')
|
const fieldRoots5 = peer.dict._getFieldRoots('profile')
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
|
@ -130,7 +130,7 @@ test('Dict squeeze', async (t) => {
|
||||||
'fieldRoots'
|
'fieldRoots'
|
||||||
)
|
)
|
||||||
|
|
||||||
assert.equal(peer.dict._squeezePotential('profile'), 0, 'squeezePotential=0')
|
assert.equal(await p(peer.dict._squeezePotential)('profile'), 0, 'squeezePotential=0')
|
||||||
assert.equal(
|
assert.equal(
|
||||||
await p(peer.dict.squeeze)('profile'),
|
await p(peer.dict.squeeze)('profile'),
|
||||||
false,
|
false,
|
||||||
|
@ -141,13 +141,13 @@ test('Dict squeeze', async (t) => {
|
||||||
assert.deepEqual(fieldRoots6, fieldRoots5, 'fieldRoots')
|
assert.deepEqual(fieldRoots6, fieldRoots5, 'fieldRoots')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Dict isGhostable', (t) => {
|
test('Dict isGhostable', async (t) => {
|
||||||
const moot = MsgV4.createMoot(aliceID, 'dict_v1__profile', aliceKeypair)
|
const moot = MsgV4.createMoot(aliceID, 'dict_v1__profile', aliceKeypair)
|
||||||
const mootID = MsgV4.getMsgID(moot)
|
const mootID = MsgV4.getMsgID(moot)
|
||||||
|
|
||||||
assert.equal(mootID, peer.dict.getFeedID('profile'), 'getFeedID')
|
assert.equal(mootID, peer.dict.getFeedID('profile'), 'getFeedID')
|
||||||
|
|
||||||
const tangle = peer.db.getTangle(mootID)
|
const tangle = await p(peer.db.getTangle)(mootID)
|
||||||
const msgIDs = tangle.topoSort()
|
const msgIDs = tangle.topoSort()
|
||||||
|
|
||||||
const fieldRoots = peer.dict._getFieldRoots('profile')
|
const fieldRoots = peer.dict._getFieldRoots('profile')
|
||||||
|
@ -155,23 +155,23 @@ test('Dict isGhostable', (t) => {
|
||||||
|
|
||||||
// Remember from the setup, that ghostSpan=4
|
// Remember from the setup, that ghostSpan=4
|
||||||
assert.equal(msgIDs.length, 8)
|
assert.equal(msgIDs.length, 8)
|
||||||
assert.equal(peer.dict.isGhostable(msgIDs[0], mootID), false) // moot
|
assert.equal(await p(peer.dict.isGhostable)(msgIDs[0], mootID), false) // moot
|
||||||
assert.equal(peer.dict.isGhostable(msgIDs[1], mootID), false)
|
assert.equal(await p(peer.dict.isGhostable)(msgIDs[1], mootID), false)
|
||||||
assert.equal(peer.dict.isGhostable(msgIDs[2], mootID), false)
|
assert.equal(await p(peer.dict.isGhostable)(msgIDs[2], mootID), false)
|
||||||
assert.equal(peer.dict.isGhostable(msgIDs[3], mootID), true) // in ghostSpan
|
assert.equal(await p(peer.dict.isGhostable)(msgIDs[3], mootID), true) // in ghostSpan
|
||||||
assert.equal(peer.dict.isGhostable(msgIDs[4], mootID), true) // in ghostSpan
|
assert.equal(await p(peer.dict.isGhostable)(msgIDs[4], mootID), true) // in ghostSpan
|
||||||
assert.equal(peer.dict.isGhostable(msgIDs[5], mootID), true) // in ghostSpan
|
assert.equal(await p(peer.dict.isGhostable)(msgIDs[5], mootID), true) // in ghostSpan
|
||||||
assert.equal(peer.dict.isGhostable(msgIDs[6], mootID), true) // in ghostSpan
|
assert.equal(await p(peer.dict.isGhostable)(msgIDs[6], mootID), true) // in ghostSpan
|
||||||
assert.equal(peer.dict.isGhostable(msgIDs[7], mootID), false) // field root
|
assert.equal(await p(peer.dict.isGhostable)(msgIDs[7], mootID), false) // field root
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Dict receives old branched update', async (t) => {
|
test('Dict receives old branched update', async (t) => {
|
||||||
const UPDATE6_ID = getMsgID(peer, 6, 'dict_v1__profile')
|
const UPDATE6_ID = await getMsgID(peer, 6, 'dict_v1__profile')
|
||||||
|
|
||||||
const moot = MsgV4.createMoot(aliceID, 'dict_v1__profile', aliceKeypair)
|
const moot = MsgV4.createMoot(aliceID, 'dict_v1__profile', aliceKeypair)
|
||||||
const mootID = MsgV4.getMsgID(moot)
|
const mootID = MsgV4.getMsgID(moot)
|
||||||
|
|
||||||
assert.equal(peer.dict.minRequiredDepth(mootID), 7, 'minRequiredDepth')
|
assert.equal(await p(peer.dict.minRequiredDepth)(mootID), 7, 'minRequiredDepth')
|
||||||
|
|
||||||
const tangle = new MsgV4.Tangle(mootID)
|
const tangle = new MsgV4.Tangle(mootID)
|
||||||
tangle.add(mootID, moot)
|
tangle.add(mootID, moot)
|
||||||
|
@ -199,9 +199,9 @@ test('Dict receives old branched update', async (t) => {
|
||||||
'fieldRoots'
|
'fieldRoots'
|
||||||
)
|
)
|
||||||
|
|
||||||
assert.equal(peer.dict.minRequiredDepth(mootID), 1, 'minRequiredDepth')
|
assert.equal(await p(peer.dict.minRequiredDepth)(mootID), 1, 'minRequiredDepth')
|
||||||
|
|
||||||
assert.equal(peer.dict._squeezePotential('profile'), 6, 'squeezePotential=6')
|
assert.equal(await p(peer.dict._squeezePotential)('profile'), 6, 'squeezePotential=6')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('teardown', async (t) => {
|
test('teardown', async (t) => {
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
const OS = require('node:os')
|
const OS = require('node:os')
|
||||||
const Path = require('node:path')
|
const Path = require('node:path')
|
||||||
const rimraf = require('rimraf')
|
const rimraf = require('rimraf')
|
||||||
const caps = require('ppppp-caps')
|
const caps = require('pzp-caps')
|
||||||
const Keypair = require('ppppp-keypair')
|
const Keypair = require('pzp-keypair')
|
||||||
|
|
||||||
function createPeer(config) {
|
function createPeer(config) {
|
||||||
if (config.name) {
|
if (config.name) {
|
||||||
const name = config.name
|
const name = config.name
|
||||||
const tmp = OS.tmpdir()
|
const tmp = OS.tmpdir()
|
||||||
config.global ??= {}
|
config.global ??= {}
|
||||||
config.global.path ??= Path.join(tmp, `ppppp-dict-${name}-${Date.now()}`)
|
config.global.path ??= Path.join(tmp, `pzp-dict-${name}-${Date.now()}`)
|
||||||
config.global.keypair ??= Keypair.generate('ed25519', name)
|
config.global.keypair ??= Keypair.generate('ed25519', name)
|
||||||
delete config.name
|
delete config.name
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ function createPeer(config) {
|
||||||
return require('secret-stack/bare')()
|
return require('secret-stack/bare')()
|
||||||
.use(require('secret-stack/plugins/net'))
|
.use(require('secret-stack/plugins/net'))
|
||||||
.use(require('secret-handshake-ext/secret-stack'))
|
.use(require('secret-handshake-ext/secret-stack'))
|
||||||
.use(require('ppppp-db'))
|
.use(require('pzp-db'))
|
||||||
.use(require('ssb-box'))
|
.use(require('ssb-box'))
|
||||||
.use(require('../lib'))
|
.use(require('../lib'))
|
||||||
.call(null, {
|
.call(null, {
|
||||||
|
|
|
@ -1,13 +1,22 @@
|
||||||
{
|
{
|
||||||
"include": ["lib/**/*.js"],
|
"include": [
|
||||||
"exclude": ["coverage/", "node_modules/", "test/"],
|
"lib/**/*.js"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"coverage/",
|
||||||
|
"node_modules/",
|
||||||
|
"test/"
|
||||||
|
],
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"checkJs": true,
|
"checkJs": true,
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"emitDeclarationOnly": true,
|
"emitDeclarationOnly": true,
|
||||||
"exactOptionalPropertyTypes": true,
|
"exactOptionalPropertyTypes": true,
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
"lib": ["es2022", "dom"],
|
"lib": [
|
||||||
|
"es2022",
|
||||||
|
"dom"
|
||||||
|
],
|
||||||
"module": "node16",
|
"module": "node16",
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
|
|
Loading…
Reference in New Issue