mirror of https://codeberg.org/pzp/pzp-db.git
improve types of dependencies
This commit is contained in:
parent
9e41400cdc
commit
f8a2006eb1
|
@ -3,7 +3,7 @@ node_modules
|
||||||
pnpm-lock.yaml
|
pnpm-lock.yaml
|
||||||
package-lock.json
|
package-lock.json
|
||||||
coverage
|
coverage
|
||||||
**/*.d.ts
|
lib/**/*.d.ts
|
||||||
*~
|
*~
|
||||||
|
|
||||||
# For misc scripts and experiments:
|
# For misc scripts and experiments:
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -1,16 +1,22 @@
|
||||||
const FS = require('fs')
|
const FS = require('fs')
|
||||||
const Path = require('path')
|
const Path = require('path')
|
||||||
// @ts-ignore
|
|
||||||
const atomic = require('atomic-file-rw')
|
const atomic = require('atomic-file-rw')
|
||||||
// @ts-ignore
|
|
||||||
const multicb = require('multicb')
|
const multicb = require('multicb')
|
||||||
// @ts-ignore
|
|
||||||
const mutexify = require('mutexify')
|
const mutexify = require('mutexify')
|
||||||
const ReadyGate = require('./utils/ready-gate')
|
const ReadyGate = require('./utils/ready-gate')
|
||||||
|
|
||||||
// TODO: fs is only supported in node.js. We should support browser by replacing
|
// 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.
|
// 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
|
* @template T
|
||||||
* @typedef {T extends void ?
|
* @typedef {T extends void ?
|
||||||
|
@ -22,14 +28,11 @@ const ReadyGate = require('./utils/ready-gate')
|
||||||
class Ghosts {
|
class Ghosts {
|
||||||
/** @type {string} */
|
/** @type {string} */
|
||||||
#basePath
|
#basePath
|
||||||
|
|
||||||
/** @type {ReadyGate} */
|
/** @type {ReadyGate} */
|
||||||
#loaded
|
#loaded
|
||||||
|
/** @type {Map<MsgID, Map<string, number>>} */
|
||||||
/** @type {Map<string, Map<string, number>>} */
|
|
||||||
#maps
|
#maps
|
||||||
|
/** @type {Mutexify<void>} */
|
||||||
/** @type {(fn: (unlock: (cb: CB<void>, ...args: ([Error] | [null, null])) => void) => void) => void} */
|
|
||||||
#writeLock
|
#writeLock
|
||||||
|
|
||||||
static encodingOpts = { encoding: 'utf-8' }
|
static encodingOpts = { encoding: 'utf-8' }
|
||||||
|
@ -58,7 +61,7 @@ class Ghosts {
|
||||||
cb()
|
cb()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
done((/** @type {any} */ err) => {
|
done((err, _) => {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
if (err) throw new Error('GhostDB failed to load', { cause: err })
|
if (err) throw new Error('GhostDB failed to load', { cause: err })
|
||||||
this.#loaded.setReady()
|
this.#loaded.setReady()
|
||||||
|
@ -96,10 +99,7 @@ class Ghosts {
|
||||||
* @param {CB<Map<string, number>>} cb
|
* @param {CB<Map<string, number>>} cb
|
||||||
*/
|
*/
|
||||||
#read(tangleID, cb) {
|
#read(tangleID, cb) {
|
||||||
atomic.readFile(
|
atomic.readFile(this.#path(tangleID), Ghosts.encodingOpts, (err, str) => {
|
||||||
this.#path(tangleID),
|
|
||||||
Ghosts.encodingOpts,
|
|
||||||
(/** @type {any} */ err, /** @type {any} */ str) => {
|
|
||||||
// Load Map
|
// Load Map
|
||||||
/** @type {Map<string, number>} */
|
/** @type {Map<string, number>} */
|
||||||
let map
|
let map
|
||||||
|
@ -109,8 +109,7 @@ class Ghosts {
|
||||||
else map = this.#deserialize(str)
|
else map = this.#deserialize(str)
|
||||||
|
|
||||||
cb(null, map)
|
cb(null, map)
|
||||||
}
|
})
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -148,11 +147,11 @@ class Ghosts {
|
||||||
this.#path(tangleID),
|
this.#path(tangleID),
|
||||||
this.#serialize(newMap),
|
this.#serialize(newMap),
|
||||||
Ghosts.encodingOpts,
|
Ghosts.encodingOpts,
|
||||||
(/** @type {any} */ err) => {
|
(err, _) => {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
if (err) return unlock(cb, new Error('GhostDB.save() failed to write ghost file', { cause: err }))
|
if (err) return unlock(cb, new Error('GhostDB.save() failed to write ghost file', { cause: err }))
|
||||||
this.#maps.set(tangleID, newMap)
|
this.#maps.set(tangleID, newMap)
|
||||||
unlock(cb, null, null)
|
unlock(cb, null, void 0)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -167,12 +166,12 @@ class Ghosts {
|
||||||
remove(tangleID, msgID, cb) {
|
remove(tangleID, msgID, cb) {
|
||||||
this.#writeLock((unlock) => {
|
this.#writeLock((unlock) => {
|
||||||
this.#loaded.onReady(() => {
|
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>} */ (
|
const map = /** @type {Map<string, number>} */ (
|
||||||
this.#maps.get(tangleID)
|
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)
|
const newMap = new Map(map)
|
||||||
newMap.delete(msgID)
|
newMap.delete(msgID)
|
||||||
|
@ -181,11 +180,11 @@ class Ghosts {
|
||||||
this.#path(tangleID),
|
this.#path(tangleID),
|
||||||
this.#serialize(newMap),
|
this.#serialize(newMap),
|
||||||
Ghosts.encodingOpts,
|
Ghosts.encodingOpts,
|
||||||
(/** @type {any} */ err) => {
|
(err, _) => {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
if (err) return unlock(cb,new Error('GhostDB.save() failed to write ghost file', { cause: err }))
|
if (err) return unlock(cb,new Error('GhostDB.save() failed to write ghost file', { cause: err }))
|
||||||
this.#maps.set(tangleID, newMap)
|
this.#maps.set(tangleID, newMap)
|
||||||
unlock(cb, null, null)
|
unlock(cb, null, void 0)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
21
lib/index.js
21
lib/index.js
|
@ -2,7 +2,6 @@ const Path = require('path')
|
||||||
const promisify = require('promisify-4loc')
|
const promisify = require('promisify-4loc')
|
||||||
const b4a = require('b4a')
|
const b4a = require('b4a')
|
||||||
const base58 = require('bs58')
|
const base58 = require('bs58')
|
||||||
// @ts-ignore
|
|
||||||
const Obz = require('obz')
|
const Obz = require('obz')
|
||||||
const Keypair = require('ppppp-keypair')
|
const Keypair = require('ppppp-keypair')
|
||||||
const Log = require('./log')
|
const Log = require('./log')
|
||||||
|
@ -30,6 +29,12 @@ const { decrypt } = require('./encryption')
|
||||||
* @typedef {Buffer | Uint8Array} B4A
|
* @typedef {Buffer | Uint8Array} B4A
|
||||||
* @typedef {{global: {keypair: Keypair; path: string}}} ExpectedConfig
|
* @typedef {{global: {keypair: Keypair; path: string}}} ExpectedConfig
|
||||||
* @typedef {{global: {keypair: Keypair; path?: string}}} Config
|
* @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
|
* } CB
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @typedef {import('obz').Obz<T>} Obz
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Config} config
|
* @param {Config} config
|
||||||
* @returns {asserts config is ExpectedConfig}
|
* @returns {asserts config is ExpectedConfig}
|
||||||
|
@ -173,7 +183,7 @@ class DBTangle extends MsgV4.Tangle {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {any} peer
|
* @param {Peer} peer
|
||||||
* @param {Config} config
|
* @param {Config} config
|
||||||
*/
|
*/
|
||||||
function initDB(peer, config) {
|
function initDB(peer, config) {
|
||||||
|
@ -181,13 +191,11 @@ function initDB(peer, config) {
|
||||||
|
|
||||||
/** @type {Array<Rec | null>} */
|
/** @type {Array<Rec | null>} */
|
||||||
const recs = []
|
const recs = []
|
||||||
|
|
||||||
/** @type {WeakMap<Rec, Misc>} */
|
/** @type {WeakMap<Rec, Misc>} */
|
||||||
const miscRegistry = new WeakMap()
|
const miscRegistry = new WeakMap()
|
||||||
|
|
||||||
/** @type {Map<string, EncryptionFormat>} */
|
/** @type {Map<string, EncryptionFormat>} */
|
||||||
const encryptionFormats = new Map()
|
const encryptionFormats = new Map()
|
||||||
|
/** @type {Obz<Rec>} */
|
||||||
const onRecordAdded = Obz()
|
const onRecordAdded = Obz()
|
||||||
|
|
||||||
const codec = {
|
const codec = {
|
||||||
|
@ -225,9 +233,8 @@ function initDB(peer, config) {
|
||||||
|
|
||||||
const ghosts = new Ghosts(Path.join(config.global.path, 'db', 'ghosts'))
|
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(() => {
|
log.close(() => {
|
||||||
// @ts-ignore
|
|
||||||
fn.apply(this, args)
|
fn.apply(this, args)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -13,20 +13,14 @@ class ErrorWithCode extends Error {
|
||||||
* @param {number} offset
|
* @param {number} offset
|
||||||
*/
|
*/
|
||||||
function nanOffsetErr(offset) {
|
function nanOffsetErr(offset) {
|
||||||
return new ErrorWithCode(
|
return new ErrorWithCode(`Offset ${offset} is not a number`, 'INVALID_OFFSET')
|
||||||
`Offset ${offset} is not a number`,
|
|
||||||
'ERR_AAOL_INVALID_OFFSET'
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {number} offset
|
* @param {number} offset
|
||||||
*/
|
*/
|
||||||
function negativeOffsetErr(offset) {
|
function negativeOffsetErr(offset) {
|
||||||
return new ErrorWithCode(
|
return new ErrorWithCode(`Offset ${offset} is negative`, 'INVALID_OFFSET')
|
||||||
`Offset ${offset} is negative`,
|
|
||||||
'ERR_AAOL_INVALID_OFFSET'
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -36,12 +30,12 @@ function negativeOffsetErr(offset) {
|
||||||
function outOfBoundsOffsetErr(offset, logSize) {
|
function outOfBoundsOffsetErr(offset, logSize) {
|
||||||
return new ErrorWithCode(
|
return new ErrorWithCode(
|
||||||
`Offset ${offset} is beyond log size ${logSize}`,
|
`Offset ${offset} is beyond log size ${logSize}`,
|
||||||
'ERR_AAOL_OFFSET_OUT_OF_BOUNDS'
|
'OFFSET_OUT_OF_BOUNDS'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function deletedRecordErr() {
|
function deletedRecordErr() {
|
||||||
return new ErrorWithCode('Record has been deleted', 'ERR_AAOL_DELETED_RECORD')
|
return new ErrorWithCode('Record has been deleted', 'DELETED_RECORD')
|
||||||
}
|
}
|
||||||
|
|
||||||
function delDuringCompactErr() {
|
function delDuringCompactErr() {
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const b4a = require('b4a')
|
const b4a = require('b4a')
|
||||||
const p = require('promisify-tuple')
|
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 Cache = require('@alloc/quick-lru') // @ts-ignore
|
||||||
const RAF = require('polyraf') // @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 debounce = require('lodash.debounce') // @ts-ignore
|
||||||
const isBufferZero = require('is-buffer-zero') // @ts-ignore
|
const isBufferZero = require('is-buffer-zero') // @ts-ignore
|
||||||
const debug = require('debug')('ppppp-db:log') // @ts-ignore
|
const debug = require('debug')('ppppp-db:log')
|
||||||
const mutexify = require('mutexify')
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
deletedRecordErr,
|
deletedRecordErr,
|
||||||
|
@ -26,6 +26,16 @@ const Record = require('./record')
|
||||||
* @typedef {number} BlockIndex
|
* @typedef {number} BlockIndex
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @typedef {import('mutexify').Mutexify<T>} Mutexify
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @typedef {import('obz').Obz<T>} Obz
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template T
|
* @template T
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
|
@ -120,18 +130,16 @@ function Log(filename, opts) {
|
||||||
let latestBlockIndex = /** @type {number | null} */ (null)
|
let latestBlockIndex = /** @type {number | null} */ (null)
|
||||||
let nextOffsetInBlock = /** @type {number | null} */ (null)
|
let nextOffsetInBlock = /** @type {number | null} */ (null)
|
||||||
let deletedBytes = 0
|
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
|
let compacting = false
|
||||||
const compactionProgress = Obv()
|
const compactionProgress = Obz()
|
||||||
compactionProgress.set(COMPACTION_PROGRESS_START)
|
compactionProgress.set(COMPACTION_PROGRESS_START)
|
||||||
/** @type {Array<CB<any>>} */
|
/** @type {Array<CB<any>>} */
|
||||||
const waitingCompaction = []
|
const waitingCompaction = []
|
||||||
|
|
||||||
AtomicFile.readFile(
|
AtomicFile.readFile(statsFilename, 'utf8', function onStatsLoaded(err, json) {
|
||||||
statsFilename,
|
|
||||||
'utf8',
|
|
||||||
/** @type {CB<string>} */ function doneLoadingStatsFile(err, json) {
|
|
||||||
if (err) {
|
if (err) {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
if (err.code !== 'ENOENT') debug('Failed loading stats file: %s', err.message)
|
if (err.code !== 'ENOENT') debug('Failed loading stats file: %s', err.message)
|
||||||
|
@ -175,8 +183,7 @@ function Log(filename, opts) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
})
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {number} blockStart
|
* @param {number} blockStart
|
||||||
|
@ -236,7 +243,7 @@ function Log(filename, opts) {
|
||||||
return getBlockStart(offset) / blockSize
|
return getBlockStart(offset) / blockSize
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @type {(fn: (unlock: (cb: CB<any>, ...args: ([Error] | [null, any])) => void) => void) => void} */
|
/** @type {Mutexify<any>} */
|
||||||
const writeLock = mutexify()
|
const writeLock = mutexify()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -697,7 +704,10 @@ function Log(filename, opts) {
|
||||||
*/
|
*/
|
||||||
function saveStats(cb) {
|
function saveStats(cb) {
|
||||||
const stats = JSON.stringify({ deletedBytes })
|
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>} */
|
/** @type {CB<void>} */
|
||||||
|
|
|
@ -41,7 +41,7 @@ test('Log deletes', async (t) => {
|
||||||
await assert.rejects(p(log._get)(offset2), (err) => {
|
await assert.rejects(p(log._get)(offset2), (err) => {
|
||||||
assert.ok(err)
|
assert.ok(err)
|
||||||
assert.equal(err.message, 'Record has been deleted')
|
assert.equal(err.message, 'Record has been deleted')
|
||||||
assert.equal(err.code, 'ERR_AAOL_DELETED_RECORD')
|
assert.equal(err.code, 'DELETED_RECORD')
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -103,7 +103,7 @@ test('Log deletes', async (t) => {
|
||||||
await assert.rejects(p(log2._get)(offset2), (err) => {
|
await assert.rejects(p(log2._get)(offset2), (err) => {
|
||||||
assert.ok(err)
|
assert.ok(err)
|
||||||
assert.equal(err.message, 'Record has been deleted')
|
assert.equal(err.message, 'Record has been deleted')
|
||||||
assert.equal(err.code, 'ERR_AAOL_DELETED_RECORD')
|
assert.equal(err.code, 'DELETED_RECORD')
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"include": ["lib/**/*.js"],
|
"include": ["declarations", "lib/**/*.js"],
|
||||||
"exclude": ["coverage/", "node_modules/", "test/"],
|
"exclude": ["coverage/", "node_modules/", "test/"],
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"checkJs": true,
|
"checkJs": true,
|
||||||
|
@ -11,6 +11,7 @@
|
||||||
"module": "node16",
|
"module": "node16",
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"target": "es2022"
|
"target": "es2022",
|
||||||
|
"typeRoots": ["node_modules/@types", "declarations"]
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue