Merge pull request 'Support async db functions and make ready for publish' (#1) from async-db into master

Reviewed-on: https://codeberg.org/pzp/pzp-dict/pulls/1
This commit is contained in:
Powersource 2024-04-30 11:04:58 +00:00
commit 50605c04f3
8 changed files with 222 additions and 178 deletions

View File

@ -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

13
.woodpecker.yaml Normal file
View File

@ -0,0 +1,13 @@
matrix:
NODE_VERSION:
- 18
- 20
steps:
test:
when:
event: [push]
image: node:${NODE_VERSION}
commands:
- npm install
- npm test

View File

@ -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
``` ```

View File

@ -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) {
const msg = peer.db.get(msgID)
if (!msg?.data) continue
for (const supersededMsgID of msg.data.supersedes) {
fieldRoots.delete(supersededMsgID)
}
fieldRoots.add(msgID)
}
// Get minimum depth of all field roots pull(
let minDepth = Infinity pull.values(tangle.topoSort()),
for (const msgID of fieldRoots) { pull.asyncMap((msgID, cb) => {
const depth = tangle.getDepth(msgID) peer.db.get(msgID, (err, msg) => {
if (depth < minDepth) minDepth = depth if (err) return cb(err)
}
return minDepth if (!msg?.data) return cb(null, null)
for (const supersededMsgID of msg.data.supersedes) {
fieldRoots.delete(supersededMsgID)
}
fieldRoots.add(msgID)
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)
})
})
}) })
}) })
} }

View File

@ -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"
}, },

View File

@ -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) => {

View File

@ -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, {

View File

@ -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,