restructure index.js for better exported types

This commit is contained in:
Andre Staltz 2023-09-20 15:33:32 +03:00
parent 037aec208c
commit d8a0f841fe
No known key found for this signature in database
GPG Key ID: 9EDE23EA7E8A4890
2 changed files with 123 additions and 129 deletions

1
.gitignore vendored
View File

@ -4,3 +4,4 @@ pnpm-lock.yaml
package-lock.json package-lock.json
coverage coverage
*~ *~
lib/*.d.ts

View File

@ -26,9 +26,7 @@ const Obz = require('obz')
*/ */
/** /**
* @param {{ * @param {{ db: PPPPPDB | null }} peer
* db: PPPPPDB | null,
* }} peer
* @returns {asserts peer is { db: PPPPPDB }} * @returns {asserts peer is { db: PPPPPDB }}
*/ */
function assertDBExists(peer) { function assertDBExists(peer) {
@ -110,141 +108,136 @@ class GoalImpl {
} }
} }
module.exports = { /**
name: 'goals', * @param {{ db: PPPPPDB | null }} peer
manifest: {}, * @param {unknown} config
permissions: { */
anonymous: {}, function initGoals(peer, config) {
}, assertDBExists(peer)
// Constants:
const EMPTY_RANGE = /** @type {Range} */ ([1, 0])
// State:
const goals = /** @type {Map<string, Goal>} */ (new Map())
const listen = Obz()
/** /**
* @param {{ db: PPPPPDB | null, close: ClosableHook }} peer * @private
* @param {{ path: string; keypair: Keypair; }} config * @param {Goal} goal
* @param {Tangle} tangle
* @returns {Range}
*/ */
init(peer, config) { function crossGoalWithTangle(goal, tangle) {
const maxDepth = tangle.maxDepth
switch (goal.type) {
case 'none':
return EMPTY_RANGE
case 'all':
case 'set':
case 'record':
return [0, maxDepth]
case 'newest':
const start = Math.max(0, maxDepth - goal.count + 1)
return [start, maxDepth]
case 'oldest':
const end = Math.min(maxDepth, goal.count - 1)
return [0, end]
}
}
/**
* @public
* @param {string} tangleID
* @param {GoalDSL} goalDSL
* @returns {void}
*/
function set(tangleID, goalDSL) {
const goal = new GoalImpl(tangleID, goalDSL)
goals.set(tangleID, goal)
listen.set(goal)
}
/**
* @public
* @param {string} tangleID
* @returns {Goal | null}
*/
function get(tangleID) {
return goals.get(tangleID) ?? null
}
/**
* @public
* @param {RecPresent} rec
* @returns {Purpose}
*/
function getRecordPurpose(rec) {
assertDBExists(peer) assertDBExists(peer)
// Constants: let servesAsTrail = false
const EMPTY_RANGE = /** @type {Range} */ ([1, 0])
// State: // Check whether this record is a goalful root of some tangle:
const goals = /** @type {Map<string, Goal>} */ (new Map()) asRoot: if (goals.has(rec.id)) {
const listen = Obz() const goal = /** @type {GoalImpl} */ (goals.get(rec.id))
if (goal.type === 'none') break asRoot
/** const tangle = peer.db.getTangle(rec.id)
* @private if (!tangle) break asRoot
* @param {Goal} goal const [min, max] = crossGoalWithTangle(goal, tangle)
* @param {Tangle} tangle if (min > max) break asRoot
* @returns {Range} if (min === 0) return 'goal'
*/ if (min > 0) servesAsTrail = true
function crossGoalWithTangle(goal, tangle) {
const maxDepth = tangle.maxDepth
switch (goal.type) {
case 'none':
return EMPTY_RANGE
case 'all':
case 'set':
case 'record':
return [0, maxDepth]
case 'newest':
const start = Math.max(0, maxDepth - goal.count + 1)
return [start, maxDepth]
case 'oldest':
const end = Math.min(maxDepth, goal.count - 1)
return [0, end]
}
} }
/** // Check whether this record is a goalful affix of some tangle:
* @public const validTangles =
* @param {string} tangleID /** @type {Array<[DBTangle, number, number, number]>} */ ([])
* @param {GoalDSL} goalDSL asAffix: for (const tangleID in rec.msg.metadata.tangles) {
* @returns {void} if (!goals.has(tangleID)) continue asAffix
*/ const goal = /** @type {GoalImpl} */ (goals.get(tangleID))
function set(tangleID, goalDSL) { if (goal.type === 'none') continue asAffix
const goal = new GoalImpl(tangleID, goalDSL) const tangle = peer.db.getTangle(tangleID)
goals.set(tangleID, goal) if (!tangle) continue asAffix
listen.set(goal) const [min, max] = crossGoalWithTangle(goal, tangle)
if (min > max) continue asAffix
const recDepth = tangle.getDepth(rec.id)
if (recDepth < 0) continue asAffix
validTangles.push([tangle, min, max, recDepth])
}
// (Loop over once without heavy computations and maybe return early:)
for (const [, min, max, recDepth] of validTangles) {
if (min <= recDepth && recDepth <= max) return 'goal'
}
// At this point we know that the record *cannot* serve as 'goal',
// so if it serves as trail, that'll do:
if (servesAsTrail) return 'trail'
// Check whether this record is a trail affix of some tangle:
// (Loop again with heavy computations now that it's inevitable:)
for (const [tangle, min] of validTangles) {
const minMsgIDs = tangle
.topoSort()
.filter((msgID) => tangle.getDepth(msgID) === min)
const { erasables } = tangle.getDeletablesAndErasables(...minMsgIDs)
if (erasables.has(rec.id)) return 'trail'
} }
/** return 'none'
* @public }
* @param {string} tangleID
* @returns {Goal | null}
*/
function get(tangleID) {
return goals.get(tangleID) ?? null
}
/** /**
* @public * @public
* @param {RecPresent} rec * @returns {IterableIterator<Goal>}
* @returns {Purpose} */
*/ function list() {
function getRecordPurpose(rec) { return goals.values()
assertDBExists(peer) }
let servesAsTrail = false
// Check whether this record is a goalful root of some tangle: return {
asRoot: if (goals.has(rec.id)) { set,
const goal = /** @type {GoalImpl} */ (goals.get(rec.id)) get,
if (goal.type === 'none') break asRoot getRecordPurpose,
const tangle = peer.db.getTangle(rec.id) list,
if (!tangle) break asRoot listen,
const [min, max] = crossGoalWithTangle(goal, tangle) }
if (min > max) break asRoot
if (min === 0) return 'goal'
if (min > 0) servesAsTrail = true
}
// Check whether this record is a goalful affix of some tangle:
const validTangles =
/** @type {Array<[DBTangle, number, number, number]>} */ ([])
asAffix: for (const tangleID in rec.msg.metadata.tangles) {
if (!goals.has(tangleID)) continue asAffix
const goal = /** @type {GoalImpl} */ (goals.get(tangleID))
if (goal.type === 'none') continue asAffix
const tangle = peer.db.getTangle(tangleID)
if (!tangle) continue asAffix
const [min, max] = crossGoalWithTangle(goal, tangle)
if (min > max) continue asAffix
const recDepth = tangle.getDepth(rec.id)
if (recDepth < 0) continue asAffix
validTangles.push([tangle, min, max, recDepth])
}
// (Loop over once without heavy computations and maybe return early:)
for (const [, min, max, recDepth] of validTangles) {
if (min <= recDepth && recDepth <= max) return 'goal'
}
// At this point we know that the record *cannot* serve as 'goal',
// so if it serves as trail, that'll do:
if (servesAsTrail) return 'trail'
// Check whether this record is a trail affix of some tangle:
// (Loop again with heavy computations now that it's inevitable:)
for (const [tangle, min] of validTangles) {
const minMsgIDs = tangle
.topoSort()
.filter((msgID) => tangle.getDepth(msgID) === min)
const { erasables } = tangle.getDeletablesAndErasables(...minMsgIDs)
if (erasables.has(rec.id)) return 'trail'
}
return 'none'
}
/**
* @public
* @returns {IterableIterator<Goal>}
*/
function list() {
return goals.values()
}
return {
set,
get,
getRecordPurpose,
list,
listen,
}
},
} }
exports.name = 'goals'
exports.init = initGoals