mirror of https://codeberg.org/pzp/pzp-db.git
new API tangle.slice()
This commit is contained in:
parent
f523df3a0e
commit
99c1520415
58
lib/index.js
58
lib/index.js
|
@ -86,12 +86,17 @@ function assertValidConfig(config) {
|
|||
}
|
||||
|
||||
class DBTangle extends MsgV3.Tangle {
|
||||
/** @type {(msgID: MsgID) => Msg | undefined} */
|
||||
#getMsg
|
||||
|
||||
/**
|
||||
* @param {MsgID} rootID
|
||||
* @param {Iterable<Rec>} recordsIter
|
||||
* @param {(msgID: MsgID) => Msg | undefined} getMsg
|
||||
*/
|
||||
constructor(rootID, recordsIter) {
|
||||
constructor(rootID, recordsIter, getMsg) {
|
||||
super(rootID)
|
||||
this.#getMsg = getMsg
|
||||
for (const rec of recordsIter) {
|
||||
if (!rec.msg) continue
|
||||
this.add(rec.id, rec.msg)
|
||||
|
@ -133,6 +138,42 @@ class DBTangle extends MsgV3.Tangle {
|
|||
|
||||
return { deletables, erasables }
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array<string>} minSet
|
||||
* @param {Array<string>} maxSet
|
||||
* @returns {Array<Msg>}
|
||||
*/
|
||||
slice(minSet, maxSet = []) {
|
||||
const minSetGood = minSet.filter((msgID) => this.has(msgID))
|
||||
const maxSetGood = maxSet.filter((msgID) => this.has(msgID))
|
||||
const minSetTight = this.getMinimumAmong(minSetGood)
|
||||
|
||||
const trail = new Set()
|
||||
for (const msgID of minSetTight) {
|
||||
const path = this.shortestPathToRoot(msgID)
|
||||
for (const msgID of path) {
|
||||
trail.add(msgID)
|
||||
}
|
||||
}
|
||||
|
||||
const msgs = /**@type {Array<Msg>}*/ ([])
|
||||
for (const msgID of this.topoSort()) {
|
||||
if (trail.has(msgID)) {
|
||||
const msg = this.#getMsg(msgID)
|
||||
if (msg) msgs.push({ ...msg, data: null })
|
||||
}
|
||||
const isMin = minSetGood.includes(msgID)
|
||||
const isMax = maxSetGood.includes(msgID)
|
||||
const isBeforeMin = minSetGood.some((min) => this.precedes(msgID, min))
|
||||
const isAfterMax = maxSetGood.some((max) => this.precedes(max, msgID))
|
||||
if (!isMin && isBeforeMin) continue
|
||||
if (!isMax && isAfterMax) continue
|
||||
const msg = this.#getMsg(msgID)
|
||||
if (msg) msgs.push(msg)
|
||||
}
|
||||
return msgs
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -288,7 +329,7 @@ function initDB(peer, config) {
|
|||
/** @type {Record<MsgID, DBTangle>} */
|
||||
const tangles = {}
|
||||
for (const tangleID of tangleIDs) {
|
||||
tangles[tangleID] ??= new DBTangle(tangleID, records())
|
||||
tangles[tangleID] ??= new DBTangle(tangleID, records(), get)
|
||||
}
|
||||
return tangles
|
||||
}
|
||||
|
@ -301,7 +342,7 @@ function initDB(peer, config) {
|
|||
const accountID = getAccountID(rec)
|
||||
let accountTangle = /** @type {Tangle | null} */ (null)
|
||||
if (accountID) {
|
||||
accountTangle = new DBTangle(accountID, records())
|
||||
accountTangle = new DBTangle(accountID, records(), get)
|
||||
if (rec.id === accountID) {
|
||||
accountTangle.add(rec.id, rec.msg)
|
||||
}
|
||||
|
@ -359,7 +400,7 @@ function initDB(peer, config) {
|
|||
function verifyRec(rec, tangleID) {
|
||||
// TODO: optimize this. This may be slow if you're adding many msgs in a
|
||||
// row, because it creates a new Map() each time. Perhaps with QuickLRU
|
||||
const tangle = new DBTangle(tangleID, records())
|
||||
const tangle = new DBTangle(tangleID, records(), get)
|
||||
if (rec.id === tangleID) {
|
||||
tangle.add(rec.id, rec.msg)
|
||||
}
|
||||
|
@ -560,7 +601,7 @@ function initDB(peer, config) {
|
|||
function accountHas(opts) {
|
||||
const keypair = opts?.keypair ?? config.keypair
|
||||
|
||||
const accountTangle = new DBTangle(opts.account, records())
|
||||
const accountTangle = new DBTangle(opts.account, records(), get)
|
||||
for (const msgID of accountTangle.topoSort()) {
|
||||
const msg = get(msgID)
|
||||
if (!msg?.data) continue
|
||||
|
@ -731,7 +772,7 @@ function initDB(peer, config) {
|
|||
}
|
||||
|
||||
// Verify powers of the signingKeypair:
|
||||
const accountTangle = new DBTangle(opts.account, records())
|
||||
const accountTangle = new DBTangle(opts.account, records(), get)
|
||||
if (obeying) {
|
||||
const signingPowers = getAccountPowers(accountTangle, signingKeypair)
|
||||
if (!signingPowers.has('add')) {
|
||||
|
@ -856,7 +897,7 @@ function initDB(peer, config) {
|
|||
const tangleTemplates = opts.tangles ?? []
|
||||
tangleTemplates.push(mootID)
|
||||
const tangles = populateTangles(tangleTemplates)
|
||||
const accountTangle = new DBTangle(opts.account, records())
|
||||
const accountTangle = new DBTangle(opts.account, records(), get)
|
||||
const accountTips = [...accountTangle.tips]
|
||||
const fullOpts = { ...opts, tangles, accountTips, keypair }
|
||||
|
||||
|
@ -946,6 +987,7 @@ function initDB(peer, config) {
|
|||
|
||||
/**
|
||||
* @param {MsgID} msgID
|
||||
* @returns {Msg | undefined}
|
||||
*/
|
||||
function get(msgID) {
|
||||
return getRecord(msgID)?.msg
|
||||
|
@ -1066,7 +1108,7 @@ function initDB(peer, config) {
|
|||
* @returns {DBTangle}
|
||||
*/
|
||||
function getTangle(tangleID) {
|
||||
return new DBTangle(tangleID, records())
|
||||
return new DBTangle(tangleID, records(), get)
|
||||
}
|
||||
|
||||
function* msgs() {
|
||||
|
|
|
@ -11,9 +11,15 @@ const Keypair = require('ppppp-keypair')
|
|||
const DIR = path.join(os.tmpdir(), 'ppppp-db-tangle')
|
||||
rimraf.sync(DIR)
|
||||
|
||||
/**
|
||||
* /–-reply1Hi <-\ /--reply3Hi
|
||||
* root <-< >-reply2 <-<
|
||||
* \--reply1Lo <-/ \--reply3Lo
|
||||
*/
|
||||
test('getTangle()', async (t) => {
|
||||
let peer
|
||||
let rootPost, reply1Lo, reply1Hi, reply2, reply3Lo, reply3Hi
|
||||
let reply1LoText, reply1HiText, reply3LoText, reply3HiText
|
||||
let tangle
|
||||
|
||||
// Setup
|
||||
|
@ -29,7 +35,10 @@ test('getTangle()', async (t) => {
|
|||
|
||||
await peer.db.loaded()
|
||||
|
||||
const id = await p(peer.db.account.create)({ subdomain: 'person' })
|
||||
const id = await p(peer.db.account.create)({
|
||||
subdomain: 'person',
|
||||
_nonce: 'alice',
|
||||
})
|
||||
|
||||
// Slow down append so that we can trigger msg creation in parallel
|
||||
const originalAppend = peer.db._getLog().append
|
||||
|
@ -64,6 +73,8 @@ test('getTangle()', async (t) => {
|
|||
])
|
||||
reply1Lo = reply1B.localeCompare(reply1C) < 0 ? reply1B : reply1C
|
||||
reply1Hi = reply1B.localeCompare(reply1C) < 0 ? reply1C : reply1B
|
||||
reply1LoText = reply1B.localeCompare(reply1C) < 0 ? 'reply 1B' : 'reply 1C'
|
||||
reply1HiText = reply1B.localeCompare(reply1C) < 0 ? 'reply 1C' : 'reply 1B'
|
||||
|
||||
reply2 = (
|
||||
await p(peer.db.feed.publish)({
|
||||
|
@ -93,6 +104,8 @@ test('getTangle()', async (t) => {
|
|||
])
|
||||
reply3Lo = reply3B.localeCompare(reply3C) < 0 ? reply3B : reply3C
|
||||
reply3Hi = reply3B.localeCompare(reply3C) < 0 ? reply3C : reply3B
|
||||
reply3LoText = reply3B.localeCompare(reply3C) < 0 ? 'reply 3B' : 'reply 3C'
|
||||
reply3HiText = reply3B.localeCompare(reply3C) < 0 ? 'reply 3C' : 'reply 3B'
|
||||
|
||||
tangle = peer.db.getTangle(rootPost)
|
||||
}
|
||||
|
@ -264,6 +277,47 @@ test('getTangle()', async (t) => {
|
|||
assert.deepEqual(actual4, expected4)
|
||||
})
|
||||
|
||||
await t.test('Tangle.slice', (t) => {
|
||||
{
|
||||
const msgs = tangle.slice([], [reply2])
|
||||
const texts = msgs.map((msg) => msg.data?.text)
|
||||
assert.deepEqual(texts, ['root', reply1LoText, reply1HiText, 'reply 2'])
|
||||
}
|
||||
|
||||
{
|
||||
const msgs = tangle.slice([reply2], [])
|
||||
const texts = msgs.map((msg) => msg.data?.text)
|
||||
assert.deepEqual(texts, [
|
||||
undefined, // root
|
||||
undefined, // reply1Lo (no need to have a trail from reply1Hi)
|
||||
'reply 2',
|
||||
reply3LoText,
|
||||
reply3HiText,
|
||||
])
|
||||
}
|
||||
|
||||
{
|
||||
const msgs = tangle.slice([reply2], [reply2])
|
||||
const texts = msgs.map((msg) => msg.data?.text)
|
||||
assert.deepEqual(texts, [
|
||||
undefined, // root
|
||||
undefined, // reply1Lo (no need to have a trail from reply1Hi)
|
||||
'reply 2',
|
||||
])
|
||||
}
|
||||
|
||||
{
|
||||
const msgs = tangle.slice([reply2], [reply2, reply3Lo])
|
||||
const texts = msgs.map((msg) => msg.data?.text)
|
||||
assert.deepEqual(texts, [
|
||||
undefined, // root
|
||||
undefined, // reply1Lo (no need to have a trail from reply1Hi)
|
||||
'reply 2',
|
||||
reply3LoText,
|
||||
])
|
||||
}
|
||||
})
|
||||
|
||||
await t.test('Tangle.topoSort after some deletes and erases', async (t) => {
|
||||
const { deletables, erasables } = tangle.getDeletablesAndErasables(reply3Lo)
|
||||
for (const msgID of deletables) {
|
||||
|
|
Loading…
Reference in New Issue