forked from public/fvtt-cthulhu-eternal
Initial import with skill sheet working
This commit is contained in:
490
node_modules/abstract-level/abstract-iterator.js
generated
vendored
Normal file
490
node_modules/abstract-level/abstract-iterator.js
generated
vendored
Normal file
@@ -0,0 +1,490 @@
|
||||
'use strict'
|
||||
|
||||
const { fromCallback } = require('catering')
|
||||
const ModuleError = require('module-error')
|
||||
const { getOptions, getCallback } = require('./lib/common')
|
||||
|
||||
const kPromise = Symbol('promise')
|
||||
const kCallback = Symbol('callback')
|
||||
const kWorking = Symbol('working')
|
||||
const kHandleOne = Symbol('handleOne')
|
||||
const kHandleMany = Symbol('handleMany')
|
||||
const kAutoClose = Symbol('autoClose')
|
||||
const kFinishWork = Symbol('finishWork')
|
||||
const kReturnMany = Symbol('returnMany')
|
||||
const kClosing = Symbol('closing')
|
||||
const kHandleClose = Symbol('handleClose')
|
||||
const kClosed = Symbol('closed')
|
||||
const kCloseCallbacks = Symbol('closeCallbacks')
|
||||
const kKeyEncoding = Symbol('keyEncoding')
|
||||
const kValueEncoding = Symbol('valueEncoding')
|
||||
const kAbortOnClose = Symbol('abortOnClose')
|
||||
const kLegacy = Symbol('legacy')
|
||||
const kKeys = Symbol('keys')
|
||||
const kValues = Symbol('values')
|
||||
const kLimit = Symbol('limit')
|
||||
const kCount = Symbol('count')
|
||||
|
||||
const emptyOptions = Object.freeze({})
|
||||
const noop = () => {}
|
||||
let warnedEnd = false
|
||||
|
||||
// This class is an internal utility for common functionality between AbstractIterator,
|
||||
// AbstractKeyIterator and AbstractValueIterator. It's not exported.
|
||||
class CommonIterator {
|
||||
constructor (db, options, legacy) {
|
||||
if (typeof db !== 'object' || db === null) {
|
||||
const hint = db === null ? 'null' : typeof db
|
||||
throw new TypeError(`The first argument must be an abstract-level database, received ${hint}`)
|
||||
}
|
||||
|
||||
if (typeof options !== 'object' || options === null) {
|
||||
throw new TypeError('The second argument must be an options object')
|
||||
}
|
||||
|
||||
this[kClosed] = false
|
||||
this[kCloseCallbacks] = []
|
||||
this[kWorking] = false
|
||||
this[kClosing] = false
|
||||
this[kAutoClose] = false
|
||||
this[kCallback] = null
|
||||
this[kHandleOne] = this[kHandleOne].bind(this)
|
||||
this[kHandleMany] = this[kHandleMany].bind(this)
|
||||
this[kHandleClose] = this[kHandleClose].bind(this)
|
||||
this[kKeyEncoding] = options[kKeyEncoding]
|
||||
this[kValueEncoding] = options[kValueEncoding]
|
||||
this[kLegacy] = legacy
|
||||
this[kLimit] = Number.isInteger(options.limit) && options.limit >= 0 ? options.limit : Infinity
|
||||
this[kCount] = 0
|
||||
|
||||
// Undocumented option to abort pending work on close(). Used by the
|
||||
// many-level module as a temporary solution to a blocked close().
|
||||
// TODO (next major): consider making this the default behavior. Native
|
||||
// implementations should have their own logic to safely close iterators.
|
||||
this[kAbortOnClose] = !!options.abortOnClose
|
||||
|
||||
this.db = db
|
||||
this.db.attachResource(this)
|
||||
this.nextTick = db.nextTick
|
||||
}
|
||||
|
||||
get count () {
|
||||
return this[kCount]
|
||||
}
|
||||
|
||||
get limit () {
|
||||
return this[kLimit]
|
||||
}
|
||||
|
||||
next (callback) {
|
||||
let promise
|
||||
|
||||
if (callback === undefined) {
|
||||
promise = new Promise((resolve, reject) => {
|
||||
callback = (err, key, value) => {
|
||||
if (err) reject(err)
|
||||
else if (!this[kLegacy]) resolve(key)
|
||||
else if (key === undefined && value === undefined) resolve()
|
||||
else resolve([key, value])
|
||||
}
|
||||
})
|
||||
} else if (typeof callback !== 'function') {
|
||||
throw new TypeError('Callback must be a function')
|
||||
}
|
||||
|
||||
if (this[kClosing]) {
|
||||
this.nextTick(callback, new ModuleError('Iterator is not open: cannot call next() after close()', {
|
||||
code: 'LEVEL_ITERATOR_NOT_OPEN'
|
||||
}))
|
||||
} else if (this[kWorking]) {
|
||||
this.nextTick(callback, new ModuleError('Iterator is busy: cannot call next() until previous call has completed', {
|
||||
code: 'LEVEL_ITERATOR_BUSY'
|
||||
}))
|
||||
} else {
|
||||
this[kWorking] = true
|
||||
this[kCallback] = callback
|
||||
|
||||
if (this[kCount] >= this[kLimit]) this.nextTick(this[kHandleOne], null)
|
||||
else this._next(this[kHandleOne])
|
||||
}
|
||||
|
||||
return promise
|
||||
}
|
||||
|
||||
_next (callback) {
|
||||
this.nextTick(callback)
|
||||
}
|
||||
|
||||
nextv (size, options, callback) {
|
||||
callback = getCallback(options, callback)
|
||||
callback = fromCallback(callback, kPromise)
|
||||
options = getOptions(options, emptyOptions)
|
||||
|
||||
if (!Number.isInteger(size)) {
|
||||
this.nextTick(callback, new TypeError("The first argument 'size' must be an integer"))
|
||||
return callback[kPromise]
|
||||
}
|
||||
|
||||
if (this[kClosing]) {
|
||||
this.nextTick(callback, new ModuleError('Iterator is not open: cannot call nextv() after close()', {
|
||||
code: 'LEVEL_ITERATOR_NOT_OPEN'
|
||||
}))
|
||||
} else if (this[kWorking]) {
|
||||
this.nextTick(callback, new ModuleError('Iterator is busy: cannot call nextv() until previous call has completed', {
|
||||
code: 'LEVEL_ITERATOR_BUSY'
|
||||
}))
|
||||
} else {
|
||||
if (size < 1) size = 1
|
||||
if (this[kLimit] < Infinity) size = Math.min(size, this[kLimit] - this[kCount])
|
||||
|
||||
this[kWorking] = true
|
||||
this[kCallback] = callback
|
||||
|
||||
if (size <= 0) this.nextTick(this[kHandleMany], null, [])
|
||||
else this._nextv(size, options, this[kHandleMany])
|
||||
}
|
||||
|
||||
return callback[kPromise]
|
||||
}
|
||||
|
||||
_nextv (size, options, callback) {
|
||||
const acc = []
|
||||
const onnext = (err, key, value) => {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
} else if (this[kLegacy] ? key === undefined && value === undefined : key === undefined) {
|
||||
return callback(null, acc)
|
||||
}
|
||||
|
||||
acc.push(this[kLegacy] ? [key, value] : key)
|
||||
|
||||
if (acc.length === size) {
|
||||
callback(null, acc)
|
||||
} else {
|
||||
this._next(onnext)
|
||||
}
|
||||
}
|
||||
|
||||
this._next(onnext)
|
||||
}
|
||||
|
||||
all (options, callback) {
|
||||
callback = getCallback(options, callback)
|
||||
callback = fromCallback(callback, kPromise)
|
||||
options = getOptions(options, emptyOptions)
|
||||
|
||||
if (this[kClosing]) {
|
||||
this.nextTick(callback, new ModuleError('Iterator is not open: cannot call all() after close()', {
|
||||
code: 'LEVEL_ITERATOR_NOT_OPEN'
|
||||
}))
|
||||
} else if (this[kWorking]) {
|
||||
this.nextTick(callback, new ModuleError('Iterator is busy: cannot call all() until previous call has completed', {
|
||||
code: 'LEVEL_ITERATOR_BUSY'
|
||||
}))
|
||||
} else {
|
||||
this[kWorking] = true
|
||||
this[kCallback] = callback
|
||||
this[kAutoClose] = true
|
||||
|
||||
if (this[kCount] >= this[kLimit]) this.nextTick(this[kHandleMany], null, [])
|
||||
else this._all(options, this[kHandleMany])
|
||||
}
|
||||
|
||||
return callback[kPromise]
|
||||
}
|
||||
|
||||
_all (options, callback) {
|
||||
// Must count here because we're directly calling _nextv()
|
||||
let count = this[kCount]
|
||||
const acc = []
|
||||
|
||||
const nextv = () => {
|
||||
// Not configurable, because implementations should optimize _all().
|
||||
const size = this[kLimit] < Infinity ? Math.min(1e3, this[kLimit] - count) : 1e3
|
||||
|
||||
if (size <= 0) {
|
||||
this.nextTick(callback, null, acc)
|
||||
} else {
|
||||
this._nextv(size, emptyOptions, onnextv)
|
||||
}
|
||||
}
|
||||
|
||||
const onnextv = (err, items) => {
|
||||
if (err) {
|
||||
callback(err)
|
||||
} else if (items.length === 0) {
|
||||
callback(null, acc)
|
||||
} else {
|
||||
acc.push.apply(acc, items)
|
||||
count += items.length
|
||||
nextv()
|
||||
}
|
||||
}
|
||||
|
||||
nextv()
|
||||
}
|
||||
|
||||
[kFinishWork] () {
|
||||
const cb = this[kCallback]
|
||||
|
||||
// Callback will be null if work was aborted on close
|
||||
if (this[kAbortOnClose] && cb === null) return noop
|
||||
|
||||
this[kWorking] = false
|
||||
this[kCallback] = null
|
||||
|
||||
if (this[kClosing]) this._close(this[kHandleClose])
|
||||
|
||||
return cb
|
||||
}
|
||||
|
||||
[kReturnMany] (cb, err, items) {
|
||||
if (this[kAutoClose]) {
|
||||
this.close(cb.bind(null, err, items))
|
||||
} else {
|
||||
cb(err, items)
|
||||
}
|
||||
}
|
||||
|
||||
seek (target, options) {
|
||||
options = getOptions(options, emptyOptions)
|
||||
|
||||
if (this[kClosing]) {
|
||||
// Don't throw here, to be kind to implementations that wrap
|
||||
// another db and don't necessarily control when the db is closed
|
||||
} else if (this[kWorking]) {
|
||||
throw new ModuleError('Iterator is busy: cannot call seek() until next() has completed', {
|
||||
code: 'LEVEL_ITERATOR_BUSY'
|
||||
})
|
||||
} else {
|
||||
const keyEncoding = this.db.keyEncoding(options.keyEncoding || this[kKeyEncoding])
|
||||
const keyFormat = keyEncoding.format
|
||||
|
||||
if (options.keyEncoding !== keyFormat) {
|
||||
options = { ...options, keyEncoding: keyFormat }
|
||||
}
|
||||
|
||||
const mapped = this.db.prefixKey(keyEncoding.encode(target), keyFormat)
|
||||
this._seek(mapped, options)
|
||||
}
|
||||
}
|
||||
|
||||
_seek (target, options) {
|
||||
throw new ModuleError('Iterator does not support seek()', {
|
||||
code: 'LEVEL_NOT_SUPPORTED'
|
||||
})
|
||||
}
|
||||
|
||||
close (callback) {
|
||||
callback = fromCallback(callback, kPromise)
|
||||
|
||||
if (this[kClosed]) {
|
||||
this.nextTick(callback)
|
||||
} else if (this[kClosing]) {
|
||||
this[kCloseCallbacks].push(callback)
|
||||
} else {
|
||||
this[kClosing] = true
|
||||
this[kCloseCallbacks].push(callback)
|
||||
|
||||
if (!this[kWorking]) {
|
||||
this._close(this[kHandleClose])
|
||||
} else if (this[kAbortOnClose]) {
|
||||
// Don't wait for work to finish. Subsequently ignore the result.
|
||||
const cb = this[kFinishWork]()
|
||||
|
||||
cb(new ModuleError('Aborted on iterator close()', {
|
||||
code: 'LEVEL_ITERATOR_NOT_OPEN'
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
return callback[kPromise]
|
||||
}
|
||||
|
||||
_close (callback) {
|
||||
this.nextTick(callback)
|
||||
}
|
||||
|
||||
[kHandleClose] () {
|
||||
this[kClosed] = true
|
||||
this.db.detachResource(this)
|
||||
|
||||
const callbacks = this[kCloseCallbacks]
|
||||
this[kCloseCallbacks] = []
|
||||
|
||||
for (const cb of callbacks) {
|
||||
cb()
|
||||
}
|
||||
}
|
||||
|
||||
async * [Symbol.asyncIterator] () {
|
||||
try {
|
||||
let item
|
||||
|
||||
while ((item = (await this.next())) !== undefined) {
|
||||
yield item
|
||||
}
|
||||
} finally {
|
||||
if (!this[kClosed]) await this.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For backwards compatibility this class is not (yet) called AbstractEntryIterator.
|
||||
class AbstractIterator extends CommonIterator {
|
||||
constructor (db, options) {
|
||||
super(db, options, true)
|
||||
this[kKeys] = options.keys !== false
|
||||
this[kValues] = options.values !== false
|
||||
}
|
||||
|
||||
[kHandleOne] (err, key, value) {
|
||||
const cb = this[kFinishWork]()
|
||||
if (err) return cb(err)
|
||||
|
||||
try {
|
||||
key = this[kKeys] && key !== undefined ? this[kKeyEncoding].decode(key) : undefined
|
||||
value = this[kValues] && value !== undefined ? this[kValueEncoding].decode(value) : undefined
|
||||
} catch (err) {
|
||||
return cb(new IteratorDecodeError('entry', err))
|
||||
}
|
||||
|
||||
if (!(key === undefined && value === undefined)) {
|
||||
this[kCount]++
|
||||
}
|
||||
|
||||
cb(null, key, value)
|
||||
}
|
||||
|
||||
[kHandleMany] (err, entries) {
|
||||
const cb = this[kFinishWork]()
|
||||
if (err) return this[kReturnMany](cb, err)
|
||||
|
||||
try {
|
||||
for (const entry of entries) {
|
||||
const key = entry[0]
|
||||
const value = entry[1]
|
||||
|
||||
entry[0] = this[kKeys] && key !== undefined ? this[kKeyEncoding].decode(key) : undefined
|
||||
entry[1] = this[kValues] && value !== undefined ? this[kValueEncoding].decode(value) : undefined
|
||||
}
|
||||
} catch (err) {
|
||||
return this[kReturnMany](cb, new IteratorDecodeError('entries', err))
|
||||
}
|
||||
|
||||
this[kCount] += entries.length
|
||||
this[kReturnMany](cb, null, entries)
|
||||
}
|
||||
|
||||
end (callback) {
|
||||
if (!warnedEnd && typeof console !== 'undefined') {
|
||||
warnedEnd = true
|
||||
console.warn(new ModuleError(
|
||||
'The iterator.end() method was renamed to close() and end() is an alias that will be removed in a future version',
|
||||
{ code: 'LEVEL_LEGACY' }
|
||||
))
|
||||
}
|
||||
|
||||
return this.close(callback)
|
||||
}
|
||||
}
|
||||
|
||||
class AbstractKeyIterator extends CommonIterator {
|
||||
constructor (db, options) {
|
||||
super(db, options, false)
|
||||
}
|
||||
|
||||
[kHandleOne] (err, key) {
|
||||
const cb = this[kFinishWork]()
|
||||
if (err) return cb(err)
|
||||
|
||||
try {
|
||||
key = key !== undefined ? this[kKeyEncoding].decode(key) : undefined
|
||||
} catch (err) {
|
||||
return cb(new IteratorDecodeError('key', err))
|
||||
}
|
||||
|
||||
if (key !== undefined) this[kCount]++
|
||||
cb(null, key)
|
||||
}
|
||||
|
||||
[kHandleMany] (err, keys) {
|
||||
const cb = this[kFinishWork]()
|
||||
if (err) return this[kReturnMany](cb, err)
|
||||
|
||||
try {
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i]
|
||||
keys[i] = key !== undefined ? this[kKeyEncoding].decode(key) : undefined
|
||||
}
|
||||
} catch (err) {
|
||||
return this[kReturnMany](cb, new IteratorDecodeError('keys', err))
|
||||
}
|
||||
|
||||
this[kCount] += keys.length
|
||||
this[kReturnMany](cb, null, keys)
|
||||
}
|
||||
}
|
||||
|
||||
class AbstractValueIterator extends CommonIterator {
|
||||
constructor (db, options) {
|
||||
super(db, options, false)
|
||||
}
|
||||
|
||||
[kHandleOne] (err, value) {
|
||||
const cb = this[kFinishWork]()
|
||||
if (err) return cb(err)
|
||||
|
||||
try {
|
||||
value = value !== undefined ? this[kValueEncoding].decode(value) : undefined
|
||||
} catch (err) {
|
||||
return cb(new IteratorDecodeError('value', err))
|
||||
}
|
||||
|
||||
if (value !== undefined) this[kCount]++
|
||||
cb(null, value)
|
||||
}
|
||||
|
||||
[kHandleMany] (err, values) {
|
||||
const cb = this[kFinishWork]()
|
||||
if (err) return this[kReturnMany](cb, err)
|
||||
|
||||
try {
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
const value = values[i]
|
||||
values[i] = value !== undefined ? this[kValueEncoding].decode(value) : undefined
|
||||
}
|
||||
} catch (err) {
|
||||
return this[kReturnMany](cb, new IteratorDecodeError('values', err))
|
||||
}
|
||||
|
||||
this[kCount] += values.length
|
||||
this[kReturnMany](cb, null, values)
|
||||
}
|
||||
}
|
||||
|
||||
// Internal utility, not typed or exported
|
||||
class IteratorDecodeError extends ModuleError {
|
||||
constructor (subject, cause) {
|
||||
super(`Iterator could not decode ${subject}`, {
|
||||
code: 'LEVEL_DECODE_ERROR',
|
||||
cause
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// To help migrating to abstract-level
|
||||
for (const k of ['_ended property', '_nexting property', '_end method']) {
|
||||
Object.defineProperty(AbstractIterator.prototype, k.split(' ')[0], {
|
||||
get () { throw new ModuleError(`The ${k} has been removed`, { code: 'LEVEL_LEGACY' }) },
|
||||
set () { throw new ModuleError(`The ${k} has been removed`, { code: 'LEVEL_LEGACY' }) }
|
||||
})
|
||||
}
|
||||
|
||||
// Exposed so that AbstractLevel can set these options
|
||||
AbstractIterator.keyEncoding = kKeyEncoding
|
||||
AbstractIterator.valueEncoding = kValueEncoding
|
||||
|
||||
exports.AbstractIterator = AbstractIterator
|
||||
exports.AbstractKeyIterator = AbstractKeyIterator
|
||||
exports.AbstractValueIterator = AbstractValueIterator
|
||||
Reference in New Issue
Block a user