From d8a0f841fe94ff25000082db104c34836fcc4735 Mon Sep 17 00:00:00 2001 From: Andre Staltz Date: Wed, 20 Sep 2023 15:33:32 +0300 Subject: [PATCH] restructure index.js for better exported types --- .gitignore | 1 + lib/index.js | 251 +++++++++++++++++++++++++-------------------------- 2 files changed, 123 insertions(+), 129 deletions(-) diff --git a/.gitignore b/.gitignore index 3ef31a7..b093961 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ pnpm-lock.yaml package-lock.json coverage *~ +lib/*.d.ts \ No newline at end of file diff --git a/lib/index.js b/lib/index.js index ede9514..21a0b6b 100644 --- a/lib/index.js +++ b/lib/index.js @@ -26,9 +26,7 @@ const Obz = require('obz') */ /** - * @param {{ - * db: PPPPPDB | null, - * }} peer + * @param {{ db: PPPPPDB | null }} peer * @returns {asserts peer is { db: PPPPPDB }} */ function assertDBExists(peer) { @@ -110,141 +108,136 @@ class GoalImpl { } } -module.exports = { - name: 'goals', - manifest: {}, - permissions: { - anonymous: {}, - }, +/** + * @param {{ db: PPPPPDB | null }} peer + * @param {unknown} config + */ +function initGoals(peer, config) { + assertDBExists(peer) + // Constants: + const EMPTY_RANGE = /** @type {Range} */ ([1, 0]) + + // State: + const goals = /** @type {Map} */ (new Map()) + const listen = Obz() /** - * @param {{ db: PPPPPDB | null, close: ClosableHook }} peer - * @param {{ path: string; keypair: Keypair; }} config + * @private + * @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) - // Constants: - const EMPTY_RANGE = /** @type {Range} */ ([1, 0]) + let servesAsTrail = false - // State: - const goals = /** @type {Map} */ (new Map()) - const listen = Obz() - - /** - * @private - * @param {Goal} goal - * @param {Tangle} tangle - * @returns {Range} - */ - 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 root of some tangle: + asRoot: if (goals.has(rec.id)) { + const goal = /** @type {GoalImpl} */ (goals.get(rec.id)) + if (goal.type === 'none') break asRoot + const tangle = peer.db.getTangle(rec.id) + if (!tangle) break asRoot + const [min, max] = crossGoalWithTangle(goal, tangle) + if (min > max) break asRoot + if (min === 0) return 'goal' + if (min > 0) servesAsTrail = true } - /** - * @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) + // 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' } - /** - * @public - * @param {string} tangleID - * @returns {Goal | null} - */ - function get(tangleID) { - return goals.get(tangleID) ?? null - } + return 'none' + } - /** - * @public - * @param {RecPresent} rec - * @returns {Purpose} - */ - function getRecordPurpose(rec) { - assertDBExists(peer) - let servesAsTrail = false + /** + * @public + * @returns {IterableIterator} + */ + function list() { + return goals.values() + } - // Check whether this record is a goalful root of some tangle: - asRoot: if (goals.has(rec.id)) { - const goal = /** @type {GoalImpl} */ (goals.get(rec.id)) - if (goal.type === 'none') break asRoot - const tangle = peer.db.getTangle(rec.id) - if (!tangle) break asRoot - 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} - */ - function list() { - return goals.values() - } - - return { - set, - get, - getRecordPurpose, - list, - listen, - } - }, + return { + set, + get, + getRecordPurpose, + list, + listen, + } } + +exports.name = 'goals' +exports.init = initGoals