improve types of dependencies

This commit is contained in:
Andre Staltz 2024-01-19 12:24:38 +02:00
parent 9e41400cdc
commit f8a2006eb1
No known key found for this signature in database
GPG Key ID: 9EDE23EA7E8A4890
11 changed files with 155 additions and 96 deletions

2
.gitignore vendored
View File

@ -3,7 +3,7 @@ node_modules
pnpm-lock.yaml
package-lock.json
coverage
**/*.d.ts
lib/**/*.d.ts
*~
# For misc scripts and experiments:

16
declarations/atomic-file-rw.d.ts vendored Normal file
View File

@ -0,0 +1,16 @@
type CB<T> = (...args: [NodeJS.ErrnoException] | [null, T]) => void
declare module 'atomic-file-rw' {
export function readFile(
path: string,
encodingOrOpts: string | { encoding: string },
cb: CB<string>
): void
export function writeFile(
path: string,
data: string,
encodingOrOpts: string | { encoding: string },
cb: CB<string>
): void
export function deleteFile(path: string, cb: CB<null>): void
}

10
declarations/multicb.d.ts vendored Normal file
View File

@ -0,0 +1,10 @@
declare module 'multicb' {
type Opts = {
pluck?: number
spread?: boolean
}
type CB<T> = (...args: [Error] | [null, T] | []) => void
type Done<T> = ((cb: CB<T>) => void) & (() => CB<T>)
function multicb<T>(opts?: Opts): Done<T>
export = multicb
}

12
declarations/mutexify.d.ts vendored Normal file
View File

@ -0,0 +1,12 @@
declare module 'mutexify' {
type CB<T> = T extends void
? (...args: [NodeJS.ErrnoException] | []) => void
: (...args: [NodeJS.ErrnoException] | [null, T]) => void
export type Mutexify<T> = (
fn: (
unlock: (cb: CB<T>, ...args: [Error] | [null, T]) => void
) => void
) => void
function mutexify<T>(): Mutexify<T>
export = mutexify
}

10
declarations/obz.d.ts vendored Normal file
View File

@ -0,0 +1,10 @@
declare module 'obz' {
type Remove = () => void
export interface Obz<X> {
(listener: (value: X) => void): Remove
set(value: X): this
value: X
}
function createObz(): Obz
export = createObz
}

View File

@ -1,16 +1,22 @@
const FS = require('fs')
const Path = require('path')
// @ts-ignore
const atomic = require('atomic-file-rw')
// @ts-ignore
const multicb = require('multicb')
// @ts-ignore
const mutexify = require('mutexify')
const ReadyGate = require('./utils/ready-gate')
// TODO: fs is only supported in node.js. We should support browser by replacing
// fs.readdir with a browser "file" that just lists all ghost files.
/**
* @typedef {import('./index').MsgID} MsgID
*/
/**
* @template T
* @typedef {import('mutexify').Mutexify<T>} Mutexify
*/
/**
* @template T
* @typedef {T extends void ?
@ -22,14 +28,11 @@ const ReadyGate = require('./utils/ready-gate')
class Ghosts {
/** @type {string} */
#basePath
/** @type {ReadyGate} */
#loaded
/** @type {Map<string, Map<string, number>>} */
/** @type {Map<MsgID, Map<string, number>>} */
#maps
/** @type {(fn: (unlock: (cb: CB<void>, ...args: ([Error] | [null, null])) => void) => void) => void} */
/** @type {Mutexify<void>} */
#writeLock
static encodingOpts = { encoding: 'utf-8' }
@ -58,7 +61,7 @@ class Ghosts {
cb()
})
})
done((/** @type {any} */ err) => {
done((err, _) => {
// prettier-ignore
if (err) throw new Error('GhostDB failed to load', { cause: err })
this.#loaded.setReady()
@ -96,10 +99,7 @@ class Ghosts {
* @param {CB<Map<string, number>>} cb
*/
#read(tangleID, cb) {
atomic.readFile(
this.#path(tangleID),
Ghosts.encodingOpts,
(/** @type {any} */ err, /** @type {any} */ str) => {
atomic.readFile(this.#path(tangleID), Ghosts.encodingOpts, (err, str) => {
// Load Map
/** @type {Map<string, number>} */
let map
@ -109,8 +109,7 @@ class Ghosts {
else map = this.#deserialize(str)
cb(null, map)
}
)
})
}
/**
@ -148,11 +147,11 @@ class Ghosts {
this.#path(tangleID),
this.#serialize(newMap),
Ghosts.encodingOpts,
(/** @type {any} */ err) => {
(err, _) => {
// prettier-ignore
if (err) return unlock(cb, new Error('GhostDB.save() failed to write ghost file', { cause: err }))
this.#maps.set(tangleID, newMap)
unlock(cb, null, null)
unlock(cb, null, void 0)
}
)
})
@ -167,12 +166,12 @@ class Ghosts {
remove(tangleID, msgID, cb) {
this.#writeLock((unlock) => {
this.#loaded.onReady(() => {
if (!this.#maps.has(tangleID)) return unlock(cb, null, null)
if (!this.#maps.has(tangleID)) return unlock(cb, null, void 0)
const map = /** @type {Map<string, number>} */ (
this.#maps.get(tangleID)
)
if (!map.has(msgID)) return unlock(cb, null, null)
if (!map.has(msgID)) return unlock(cb, null, void 0)
const newMap = new Map(map)
newMap.delete(msgID)
@ -181,11 +180,11 @@ class Ghosts {
this.#path(tangleID),
this.#serialize(newMap),
Ghosts.encodingOpts,
(/** @type {any} */ err) => {
(err, _) => {
// prettier-ignore
if (err) return unlock(cb,new Error('GhostDB.save() failed to write ghost file', { cause: err }))
this.#maps.set(tangleID, newMap)
unlock(cb, null, null)
unlock(cb, null, void 0)
}
)
})

View File

@ -2,7 +2,6 @@ const Path = require('path')
const promisify = require('promisify-4loc')
const b4a = require('b4a')
const base58 = require('bs58')
// @ts-ignore
const Obz = require('obz')
const Keypair = require('ppppp-keypair')
const Log = require('./log')
@ -30,6 +29,12 @@ const { decrypt } = require('./encryption')
* @typedef {Buffer | Uint8Array} B4A
* @typedef {{global: {keypair: Keypair; path: string}}} ExpectedConfig
* @typedef {{global: {keypair: Keypair; path?: string}}} Config
* @typedef {{
* close: {
* (errOrEnd: boolean, cb?: CB<void>): void,
* hook(hookIt: (this: unknown, fn: any, args: any) => any): void
* };
* }} Peer
*/
/**
@ -71,6 +76,11 @@ const { decrypt } = require('./encryption')
* } CB
*/
/**
* @template T
* @typedef {import('obz').Obz<T>} Obz
*/
/**
* @param {Config} config
* @returns {asserts config is ExpectedConfig}
@ -173,7 +183,7 @@ class DBTangle extends MsgV4.Tangle {
}
/**
* @param {any} peer
* @param {Peer} peer
* @param {Config} config
*/
function initDB(peer, config) {
@ -181,13 +191,11 @@ function initDB(peer, config) {
/** @type {Array<Rec | null>} */
const recs = []
/** @type {WeakMap<Rec, Misc>} */
const miscRegistry = new WeakMap()
/** @type {Map<string, EncryptionFormat>} */
const encryptionFormats = new Map()
/** @type {Obz<Rec>} */
const onRecordAdded = Obz()
const codec = {
@ -225,9 +233,8 @@ function initDB(peer, config) {
const ghosts = new Ghosts(Path.join(config.global.path, 'db', 'ghosts'))
peer.close.hook(function (/** @type {any} */ fn, /** @type {any} */ args) {
peer.close.hook(function hookToCloseDB(fn, args) {
log.close(() => {
// @ts-ignore
fn.apply(this, args)
})
})

View File

@ -13,20 +13,14 @@ class ErrorWithCode extends Error {
* @param {number} offset
*/
function nanOffsetErr(offset) {
return new ErrorWithCode(
`Offset ${offset} is not a number`,
'ERR_AAOL_INVALID_OFFSET'
)
return new ErrorWithCode(`Offset ${offset} is not a number`, 'INVALID_OFFSET')
}
/**
* @param {number} offset
*/
function negativeOffsetErr(offset) {
return new ErrorWithCode(
`Offset ${offset} is negative`,
'ERR_AAOL_INVALID_OFFSET'
)
return new ErrorWithCode(`Offset ${offset} is negative`, 'INVALID_OFFSET')
}
/**
@ -36,12 +30,12 @@ function negativeOffsetErr(offset) {
function outOfBoundsOffsetErr(offset, logSize) {
return new ErrorWithCode(
`Offset ${offset} is beyond log size ${logSize}`,
'ERR_AAOL_OFFSET_OUT_OF_BOUNDS'
'OFFSET_OUT_OF_BOUNDS'
)
}
function deletedRecordErr() {
return new ErrorWithCode('Record has been deleted', 'ERR_AAOL_DELETED_RECORD')
return new ErrorWithCode('Record has been deleted', 'DELETED_RECORD')
}
function delDuringCompactErr() {

View File

@ -1,14 +1,14 @@
const fs = require('fs')
const b4a = require('b4a')
const p = require('promisify-tuple')
const AtomicFile = require('atomic-file-rw')
const mutexify = require('mutexify')
const Obz = require('obz') // @ts-ignore
const Cache = require('@alloc/quick-lru') // @ts-ignore
const RAF = require('polyraf') // @ts-ignore
const Obv = require('obz') // @ts-ignore
const AtomicFile = require('atomic-file-rw') // @ts-ignore
const debounce = require('lodash.debounce') // @ts-ignore
const isBufferZero = require('is-buffer-zero') // @ts-ignore
const debug = require('debug')('ppppp-db:log') // @ts-ignore
const mutexify = require('mutexify')
const debug = require('debug')('ppppp-db:log')
const {
deletedRecordErr,
@ -26,6 +26,16 @@ const Record = require('./record')
* @typedef {number} BlockIndex
*/
/**
* @template T
* @typedef {import('mutexify').Mutexify<T>} Mutexify
*/
/**
* @template T
* @typedef {import('obz').Obz<T>} Obz
*/
/**
* @template T
* @typedef {{
@ -120,18 +130,16 @@ function Log(filename, opts) {
let latestBlockIndex = /** @type {number | null} */ (null)
let nextOffsetInBlock = /** @type {number | null} */ (null)
let deletedBytes = 0
const lastRecOffset = Obv() // offset of last written record
/** Offset of last written record @type {Obz<number>} */
const lastRecOffset = Obz()
let compacting = false
const compactionProgress = Obv()
const compactionProgress = Obz()
compactionProgress.set(COMPACTION_PROGRESS_START)
/** @type {Array<CB<any>>} */
const waitingCompaction = []
AtomicFile.readFile(
statsFilename,
'utf8',
/** @type {CB<string>} */ function doneLoadingStatsFile(err, json) {
AtomicFile.readFile(statsFilename, 'utf8', function onStatsLoaded(err, json) {
if (err) {
// prettier-ignore
if (err.code !== 'ENOENT') debug('Failed loading stats file: %s', err.message)
@ -175,8 +183,7 @@ function Log(filename, opts) {
}
}
)
}
)
})
/**
* @param {number} blockStart
@ -236,7 +243,7 @@ function Log(filename, opts) {
return getBlockStart(offset) / blockSize
}
/** @type {(fn: (unlock: (cb: CB<any>, ...args: ([Error] | [null, any])) => void) => void) => void} */
/** @type {Mutexify<any>} */
const writeLock = mutexify()
/**
@ -697,7 +704,10 @@ function Log(filename, opts) {
*/
function saveStats(cb) {
const stats = JSON.stringify({ deletedBytes })
AtomicFile.writeFile(statsFilename, stats, 'utf8', cb)
AtomicFile.writeFile(statsFilename, stats, 'utf8', (err, _) => {
if (err) return cb(new Error('Failed to save stats file', { cause: err }))
cb()
})
}
/** @type {CB<void>} */

View File

@ -41,7 +41,7 @@ test('Log deletes', async (t) => {
await assert.rejects(p(log._get)(offset2), (err) => {
assert.ok(err)
assert.equal(err.message, 'Record has been deleted')
assert.equal(err.code, 'ERR_AAOL_DELETED_RECORD')
assert.equal(err.code, 'DELETED_RECORD')
return true
})
@ -103,7 +103,7 @@ test('Log deletes', async (t) => {
await assert.rejects(p(log2._get)(offset2), (err) => {
assert.ok(err)
assert.equal(err.message, 'Record has been deleted')
assert.equal(err.code, 'ERR_AAOL_DELETED_RECORD')
assert.equal(err.code, 'DELETED_RECORD')
return true
})

View File

@ -1,5 +1,5 @@
{
"include": ["lib/**/*.js"],
"include": ["declarations", "lib/**/*.js"],
"exclude": ["coverage/", "node_modules/", "test/"],
"compilerOptions": {
"checkJs": true,
@ -11,6 +11,7 @@
"module": "node16",
"skipLibCheck": true,
"strict": true,
"target": "es2022"
"target": "es2022",
"typeRoots": ["node_modules/@types", "declarations"]
}
}