diff --git a/fmhub.js b/fmhub.js index dba83e6..e9ead44 100644 --- a/fmhub.js +++ b/fmhub.js @@ -1,42 +1,138 @@ -import interrelated from 'interrelated' +import interrelated from './interrelated.js' +import level from 'level' +import fs from 'fs' +import path from 'path' + +function getStat(path) { + return new Promise((resolve, reject) => { + fs.stat(path, (err, stats) => { + err ? resolve(false) : resolve(stats) + }) + }) +} + +function mkdir(dir) { + return new Promise((resolve, reject) => { + fs.mkdir(dir, err => { + err ? resolve(false) : resolve(true) + }) + }) +} + +async function dirExists(dir) { + let isExists = await getStat(dir) + if (isExists && isExists.isDirectory()) { + return true + } else if (isExists) { + return false + } + let status = await dirExists(path.parse(dir).dir) + return status ? await mkdir(dir) : null +} + +// 检查并创建文件夹 +await dirExists('data/level') + +// 初始化 leveldb +const db = level("./data/level/fmhub") +//db.put('name', 'Level', function (err) { +// if (err) return console.log('Ooops!', err) // some kind of I/O error +// +// // 3) Fetch by key +// db.get('name', function (err, value) { +// if (err) return console.log('Ooops!', err) // likely the key was not found +// +// // Ta da! +// console.log('name=' + value) +// }) +//}) export default class { constructor() { this.用户订阅 = new interrelated() this.用户会话 = new interrelated() } + 订阅频道(fid, uid) { - this.用户订阅.set(fid, uid) + this.用户订阅.关联数据(uid, fid) + // 要向数据库写入订阅记录 + if (uid !== "0") db.get(uid, (err, value) => { + if (err) { + value = JSON.stringify([]) + } else { + let data = JSON.parse(value) // 解码 + data.push(fid) // 添加 + data = Array.from(new Set(data)) // 去重 + value = JSON.stringify(data) // 编码 + } + db.put(uid, value, function (err) { + if (err) return console.log("写入错误", err) + }) + }) } + 取消订阅(fid, uid) { - this.用户订阅.delete(fid, uid) + this.用户订阅.取消关联(uid, fid) + // 从数据库删除订阅记录 + if (uid !== "0") db.get(uid, (err, value) => { + if (err) return console.log("尚无任何订阅") + let data = JSON.parse(value) // 解码 + data = data.filter(item => item !== fid) // 移除 + value = JSON.stringify(data) // 编码 + db.put(uid, function (err) { + if (err) return console.log("写入错误", err) + }) + }) } + 增加会话(uid, ws) { - this.用户会话.set(uid, ws) + let 会话列表 = this.用户会话.A的集合.get(uid) + this.用户会话.关联数据(uid, ws) + if (!会话列表) { + console.log("还没有会话, 则为其添加订阅记录") + this.加载订阅记录(uid) // 但他可能没有订阅记录 + } } + 移除会话(uid, ws) { - this.用户会话.delete(uid, ws) + this.用户会话.取消关联(uid, ws) + if (!this.用户会话.A的集合.get(uid)) { + console.log("所有会话都被移除了, 也移除订阅记录") + this.移除用户(uid) + } } + 发送消息(fm, uid, data) { let msg = JSON.stringify({ fm, uid, data }) - this.用户订阅.atob(fm, (uid) => { - //console.log(`用户 ${uid} 订阅的所有频道`) - this.用户会话.atob(uid, (ws) => { + // 订阅列表中 + // A 是用户, 所以是 A 下 B 的集合 + // B 是频道, 向频道下所有用户的会话发送消息, 所以是 B下A的集合用于查询会话列表 + // 会话列表中: + // A 是用户, 所以是 A 下 B 的 集合 + // B 是 WS, 向用户的每个 WS 发送消息 + this.用户订阅.B中取A(fm, (uid) => { + //console.log(`用户 ${uid} 订阅过此频道`, fm) + this.用户会话.A中取B(uid, (ws) => { //console.log(`用户 ${uid} 的会话`) ws.send(msg) }) }) - //console.log(`用户 ${uid} 订阅的所有频道`) - //this.用户订阅.aall(uid, (fid) => { - // console.log(fid) - //}) - //console.log(`频道 ${fm} 下的所有用户`) - //this.用户订阅.ball(fm, (uid) => { - // console.log(uid) - //}) } + 移除用户(uid) { - this.用户订阅.adelete(uid) - this.用户会话.adelete(uid) + this.用户订阅.B中除A(uid) + this.用户会话.B中除A(uid) + } + + 加载订阅记录(uid) { + let 默认订阅 = ["chat", "system"] + 默认订阅.forEach(item => { + console.log("默认订阅:", item) + this.用户订阅.关联数据(uid, item) + }) + if (uid !== "0") db.get(uid, (err, value) => { + if (err) return console.log("尚无任何订阅") + JSON.parse(value).forEach(item => this.用户订阅.关联数据(uid, item)) + }) } } diff --git a/index.js b/index.js index 077e937..ee4ccee 100644 --- a/index.js +++ b/index.js @@ -49,48 +49,21 @@ const user_load = async (_id) => await new Promise(resolve => db('user').findOne return resolve({ _id, gid, name, avatar }) })) -// 通讯频道 Frequency Modulation +// 通讯频道 Frequency Modulation, 游客使用公共账户 uid = 0 function websocketer(ws, req) { - // 游客使用公共账户 uid = 0 let uid = req.session?.account?.uid || "0" console.log(`用户 ${uid} 连接了服务器`) - // 访客默认订阅的频道列表: 一般是所有公开的频道 - let list = ["chat", "system"] - list.forEach(fid => FM.订阅频道(fid, uid)) - - // 当用户连接时, 读取其订阅列表 - if (req.session.account) { - db('user').findOne({ uid }, function (err, doc) { - if (doc && Array.isArray(doc.fm)) { - doc.fm.forEach(fid => FM.订阅频道(fid, uid)) - } - }) - } - - // 将连接加入到列表 ws + //FM.加载订阅记录(uid) FM.增加会话(uid, ws) - // 收到消息时(只有频道消息) ws.on('message', function (msg) { if (typeof (msg) !== "string") return console.log("消息不是字符串") let { fm, data } = JSON.parse(msg) FM.发送消息(fm, uid, data) - // 基于链表实现消息记录(对所有频道加入查询) - // ws收到的消息不一定是向频道推送, 还可能是消息查询(因为系统推送的消息也不一定是聊天室) - // 因而要为消息加上 id, 并为其附上双向链表字段, 使用 nedb 存储吗.. - // 因此 格式为 { fm, id, direction: before||after, } - }) - - // 关闭连接时 - ws.on('close', function (code) { - FM.移除会话(uid, ws) - }) - - // 发生错误时 - ws.on('error', function (code) { - console.log('link error: ', code) }) + ws.on('close', (code) => FM.移除会话(uid, ws)) + ws.on('error', (code) => console.log('link error: ', code)) } // 会话列表 @@ -320,11 +293,90 @@ function object_patch(req, res, next) { } return db(req.params.name).update({ _id: req.params._id }, { $set: req.body }, function (err, count) { if (!count) return res.status(500).send('修改失败') - return res.send('修改成功') + + // 此处插入 hook + // 使用方法: + // kana.item(name || all).patch. + // kana.list(name || all).post + + // 数据双向绑定 + // 某个用户喜欢的主题 + // 主题被哪些用户喜欢 + // 当某个用户不再喜欢某个主题, 要双向解除绑定 + + // like: uid > [pid] + // like: pid > [uid] + // 修改立即存储为日志 + // 逐期将日志合并 + + // attach + // thread > post + // thread < post + // thread 可以存储 [post] + // post 可以存储 [thread] + // 是直接的相互关联, 但数据众多且不在内存中 + // hub的关联在内存中, 但也会向磁盘更新 + // 时常剔除不被使用的, 每次使用使之存活时间增加 + // 每次读取必然缓存一段时间, 而缓存时间由内存压力界定 + // 维持命中率尽量高, 且在接近标准时, 逐渐剔除适合移除的 + // 若是被跑内存呢..? + // 正常情况下数据的访问率如果都非常高, 则适宜加机器 + // 如果异常情况, 则延迟跑内存ip访问速率 + + res.send('修改成功') + + // 会话完全成功后执行 + //if (typeof (msg) !== "string") return console.log("消息不是字符串") + //let { fm, data } = JSON.parse(msg) + // 此处需要向所有关注此 item 的用户发送消息, 因此还需要向上传递事件(如果这是一个附属对象) + + + // 构建消息内容(由于是作者或管理员修改, 因此不必通知修改者, 消息内容不必特意为修改者书写) + + + // 构建将要接受通知的用户队列, 需要去重, 所以使用 map + // 由于是作者或管理员修改, 因此不必通知修改者, 要将修改者的id特意从最终列表移出 + let userlist = new Map() + let collect = () => { + userlist.set(doc.uid, true) // 先加入本对象作者 + // 再加入上级关注者. (如果是附属) + if (doc.attach && doc.aid) { } + + // 再加入下级关注者.(这似乎需要作双向绑定才行) + if (doc.attachR) { + // 有哪些类型附属? + // 每个类型有哪些对象实体? + // 此类调用涉及了似乎较为庞大的关系网, 当调用具体对象时, 如何不必对下级作全量查询呢? + // 在顶级加入结构表显然并不合适 + // 下级格式: + // then > star { + // userlist: {} // 只有系统维护的字段, 如果写入, 先转换到 map, 或是直接去重 + // } + // 实际就是这个用户是否关注了这个对象, 如果关注了, + // 如何将上下级关系网中所有用户在不加载大量数据的情况下进行关注状态判定? + // 分离式: 与账户系统解耦, 方便随时分离和改变数据的存储形式 + // + // 如果放了挂载点, 查询上下关联时也要分别读取上下的挂载点, 此时是否对上下也全部加载? + } + } + + // 从待通知用户队列移除修改者id + + // 执行发送消息 + //let 发送消息 = () => { + // FM.发送消息(fm, uid, data) // 先通知本级关注者(作者) + // if (doc.attach && doc.aid) { //再通知上级关注者() + // return 发送消息() + // } + //} + }) }) } +// 用户的 like 表 (map) +// 当用户下线自动转换为冷数据, 从内存剔除 + // 删除对象 const object_remove = function (req, res) { return db(req.params.name).findOne({ _id: req.params._id }, async function (err, doc) { diff --git a/interrelated.js b/interrelated.js new file mode 100644 index 0000000..9cf5fd3 --- /dev/null +++ b/interrelated.js @@ -0,0 +1,72 @@ +export default class interrelated { + constructor() { + this.A的集合 = new Map() + this.B的集合 = new Map() + } + + 关联数据(A, B) { + let value = this.A的集合.get(A) || new Map() + if (!value.size) this.A的集合.set(A, value) + value.set(B, true) + + let valux = this.B的集合.get(B) || new Map() + if (!valux.size) this.B的集合.set(B, valux) + valux.set(A, true) + } + + 取消关联(A, B) { + let value = this.A的集合.get(A) + if (value && value.size) value.delete(B) + if (value && value.size === 0) this.A的集合.delete(A) + + let valux = this.B的集合.get(B) + if (valux && valux.size) valux.delete(A) + if (valux && valux.size === 0) this.B的集合.delete(B) + } + + A中取B(A, callback) { + let value = this.A的集合.get(A) + if (value && value.size) { + value.forEach((value, B) => callback(B)) + } + } + + B中取A(B, callback) { + let valux = this.B的集合.get(B) + if (valux && valux.size) { + valux.forEach((value, A) => callback(A)) + } + } + + A中除B(B) { + let value = this.B的集合.get(B) + if (value && value.size) value.forEach((value, A) => { + let valux = this.A的集合.get(A) + if (valux && valux.size) valux.delete(B) + }) + this.B的集合.delete(B) + } + + B中除A(A) { + let value = this.A的集合.get(A) + if (value && value.size) value.forEach((value, B) => { + let valux = this.B的集合.get(B) + if (valux && valux.size) valux.delete(A) + }) + this.A的集合.delete(A) + } + + // 读取所有 a + // aall(uid, callback) { + // this.channels.forEach((value, key) => { + // callback(key) + // }) + // } + // + // 读取所有 b + // ball(fid, callback) { + // this.users.forEach((value, key) => { + // callback(key) + // }) + // } +} diff --git a/kana.js b/kana.js new file mode 100644 index 0000000..d9489e7 --- /dev/null +++ b/kana.js @@ -0,0 +1,29 @@ +import nedb from 'nedb' + +const databases = new Map() +const db = (name) => (databases.get(name) || function () { + let database = new nedb({ filename: `./data/db/${name}.db`, autoload: true, timestampData: true }) + databases.set(name, database) + return database +}()) + + +export default { + list: (name, query, callback) => { + db(name).find(query, callback) + }, + item: (name) => { }, + user: {}, + account: { + create: (data) => { }, + delete: (data) => { }, + }, + session: { + create: (data) => { }, + delete: (data) => { }, + }, + message: { + create: (data) => { }, + delete: (data) => { }, + }, +} diff --git a/main.js b/main.js new file mode 100644 index 0000000..53c4eaa --- /dev/null +++ b/main.js @@ -0,0 +1,93 @@ +import express from 'express' +import expressWs from 'express-ws' +import session from 'express-session' +import sessionDb from 'express-session-nedb' +import kana from './kana.js' + +const app = expressWs(express()).app +app.use(express.json()) +app.use(express.urlencoded({ extended: false })) +app.use(session({ secret: 'kana', name: 'sid', resave: false, saveUninitialized: false, cookie: { maxAge: 180 * 24 * 3600000 }, store: session_store })) +app.use('/data/file/', express.static('data/file')) +app.ws('/', websocketer) + + + +function listFind(req, res) { + // 通过权限判断不可使用的字段 +} + + +app.route('/:list').get(kana.list.find) + +//import kana from 'kana' +// +//// 初始化一个 +////const app = new kana() +// +//// list 是对象集合模型, 它规划了对象集合的生命周期, 因此也在此处配置 +////kana.list.set +// +//// 使用 list 初始化指定类型的对象, 以对其进行特殊意义的操作 +//const list = new kana.list("name") +// +// +//// item 是对象模型, 它规划了对象模型的生命周期 +//const item = new kana.item("name") +// +// +//// 点赞一个对象 +//// 取消点赞 +// +//// fm 是频道通道, +////const fm +// +// +// +// +//// 开始使用 +//// 1. like 事件对模型节点挂载 +//// 1. 需要 +// +//like.config({ +// set: "点赞时", +// del: "取消时", +//}) +// +// +//// 列表执行过程中调用 +//list.config({ +// create: (item) => { +// // 通知关注作者的用户 +// message.create({ +// // 构成消息结构 +// }) +// }, +// remove: "移除时执行", +//}) +// +//// 或向列表执行过程中注入? +//// 函数式要求无状态 +//// HAS +// +//const create = kana.list.create(item => { +// // 创建对象时, 触发的所有事件 +//}) +// +//const remove = kana.list.delete(item => { +// // 移除对象时, 触发的所有事件 +//}) +// +//kana.run({ +// // 如果触发结果 +//}) +// +// +//// 创建对象时发生什么 +//// 1. 记录日志 +//// 2. 通知关注者 +//// +//// 消息或日志挂载到哪里 +//// 1. 创建对象时(通知的信息不同) +//// 2. 删除对象时(记录的信息不同) +//// diff --git a/package.json b/package.json index f092484..fa045d0 100644 --- a/package.json +++ b/package.json @@ -13,8 +13,9 @@ "express-ws": "^5.0.2", "formidable": "^2.0.1", "interrelated": "^2.0.0-0", + "level": "^7.0.1", "md5-node": "^1.0.1", "nedb": "^1.8.0", "string-random": "^0.1.3" } -} \ No newline at end of file +} diff --git a/yarn.lock b/yarn.lock index f8453b2..36c6b7c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,18 @@ # yarn lockfile v1 +abstract-leveldown@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-7.2.0.tgz#08d19d4e26fb5be426f7a57004851b39e1795a2e" + integrity sha512-DnhQwcFEaYsvYDnACLZhMmCWd3rkOeEvglpa4q5i/5Jlm3UIsWaxVzuXvDLFCSCWRO3yy2/+V/G7FusFgejnfQ== + dependencies: + buffer "^6.0.3" + catering "^2.0.0" + is-buffer "^2.0.5" + level-concat-iterator "^3.0.0" + level-supports "^2.0.1" + queue-microtask "^1.2.3" + accepts@~1.3.7: version "1.3.7" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" @@ -25,6 +37,11 @@ async@0.2.10: resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1" integrity sha1-trvgsGdLnXGXCMo43owjfLUmw9E= +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + binary-search-tree@0.2.5: version "0.2.5" resolved "https://registry.yarnpkg.com/binary-search-tree/-/binary-search-tree-0.2.5.tgz#7dbb3b210fdca082450dad2334c304af39bdc784" @@ -48,11 +65,24 @@ body-parser@1.19.0: raw-body "2.4.0" type-is "~1.6.17" +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + bytes@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== +catering@^2.0.0, catering@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/catering/-/catering-2.1.1.tgz#66acba06ed5ee28d5286133982a927de9a04b510" + integrity sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w== + content-disposition@0.5.3: version "0.5.3" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" @@ -87,6 +117,14 @@ debug@2.6.9: dependencies: ms "2.0.0" +deferred-leveldown@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/deferred-leveldown/-/deferred-leveldown-7.0.0.tgz#39802715fda6ec06d0159a8b28bd1c7e2b1cf0bf" + integrity sha512-QKN8NtuS3BC6m0B8vAnBls44tX1WXAFATUsJlruyAYbZpysWV3siH6o/i3g9DCHauzodksO60bdj5NazNbjCmg== + dependencies: + abstract-leveldown "^7.2.0" + inherits "^2.0.3" + depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" @@ -120,6 +158,16 @@ encodeurl@~1.0.2: resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= +encoding-down@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/encoding-down/-/encoding-down-7.1.0.tgz#8d55b5a20d50eb6f0edaf7233f6aee0ff562386a" + integrity sha512-ky47X5jP84ryk5EQmvedQzELwVJPjCgXDQZGeb9F6r4PdChByCGHTBrVcF3h8ynKVJ1wVbkxTsDC8zBROPypgQ== + dependencies: + abstract-leveldown "^7.2.0" + inherits "^2.0.3" + level-codec "^10.0.0" + level-errors "^3.0.0" + escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" @@ -259,6 +307,11 @@ iconv-lite@0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" +ieee754@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + immediate@~3.0.5: version "3.0.6" resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" @@ -269,7 +322,7 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= -inherits@2.0.4: +inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -284,6 +337,92 @@ ipaddr.js@1.9.1: resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== +is-buffer@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" + integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== + +level-codec@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/level-codec/-/level-codec-10.0.0.tgz#f9e892770532c6cdcc83529546730791b0c62c12" + integrity sha512-QW3VteVNAp6c/LuV6nDjg7XDXx9XHK4abmQarxZmlRSDyXYk20UdaJTSX6yzVvQ4i0JyWSB7jert0DsyD/kk6g== + dependencies: + buffer "^6.0.3" + +level-concat-iterator@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/level-concat-iterator/-/level-concat-iterator-3.1.0.tgz#5235b1f744bc34847ed65a50548aa88d22e881cf" + integrity sha512-BWRCMHBxbIqPxJ8vHOvKUsaO0v1sLYZtjN3K2iZJsRBYtp+ONsY6Jfi6hy9K3+zolgQRryhIn2NRZjZnWJ9NmQ== + dependencies: + catering "^2.1.0" + +level-errors@^3.0.0, level-errors@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/level-errors/-/level-errors-3.0.1.tgz#4bed48a33108cd83b0e39fdf9bbd84e96fbbef9f" + integrity sha512-tqTL2DxzPDzpwl0iV5+rBCv65HWbHp6eutluHNcVIftKZlQN//b6GEnZDM2CvGZvzGYMwyPtYppYnydBQd2SMQ== + +level-iterator-stream@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/level-iterator-stream/-/level-iterator-stream-5.0.0.tgz#85b3438e1b4c54ce5aa8c0eb973cfb628117df9e" + integrity sha512-wnb1+o+CVFUDdiSMR/ZymE2prPs3cjVLlXuDeSq9Zb8o032XrabGEXcTCsBxprAtseO3qvFeGzh6406z9sOTRA== + dependencies: + inherits "^2.0.4" + readable-stream "^3.4.0" + +level-js@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/level-js/-/level-js-6.1.0.tgz#982ee9e583fca801aa75689c041995d0e7aab4ef" + integrity sha512-i7mPtkZm68aewfv0FnIUWvFUFfoyzIvVKnUmuQGrelEkP72vSPTaA1SGneWWoCV5KZJG4wlzbJLp1WxVNGuc6A== + dependencies: + abstract-leveldown "^7.2.0" + buffer "^6.0.3" + inherits "^2.0.3" + ltgt "^2.1.2" + run-parallel-limit "^1.1.0" + +level-packager@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/level-packager/-/level-packager-6.0.1.tgz#46b521e63df7f9728543f6792c0a8fe967e679a0" + integrity sha512-8Ezr0XM6hmAwqX9uu8IGzGNkWz/9doyPA8Oo9/D7qcMI6meJC+XhIbNYHukJhIn8OGdlzQs/JPcL9B8lA2F6EQ== + dependencies: + encoding-down "^7.1.0" + levelup "^5.1.1" + +level-supports@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/level-supports/-/level-supports-2.1.0.tgz#9af908d853597ecd592293b2fad124375be79c5f" + integrity sha512-E486g1NCjW5cF78KGPrMDRBYzPuueMZ6VBXHT6gC7A8UYWGiM14fGgp+s/L1oFfDWSPV/+SFkYCmZ0SiESkRKA== + +level@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/level/-/level-7.0.1.tgz#05121748d95a4ff7355860d56eb5d0aa36faef2a" + integrity sha512-w3E64+ALx2eZf8RV5JL4kIcE0BFAvQscRYd1yU4YVqZN9RGTQxXSvH202xvK15yZwFFxRXe60f13LJjcJ//I4Q== + dependencies: + level-js "^6.1.0" + level-packager "^6.0.1" + leveldown "^6.1.0" + +leveldown@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/leveldown/-/leveldown-6.1.0.tgz#7ab1297706f70c657d1a72b31b40323aa612b9ee" + integrity sha512-8C7oJDT44JXxh04aSSsfcMI8YiaGRhOFI9/pMEL7nWJLVsWajDPTRxsSHTM2WcTVY5nXM+SuRHzPPi0GbnDX+w== + dependencies: + abstract-leveldown "^7.2.0" + napi-macros "~2.0.0" + node-gyp-build "^4.3.0" + +levelup@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/levelup/-/levelup-5.1.1.tgz#9f99699f414ac084a3f8a28fc262a1f49cd7a52c" + integrity sha512-0mFCcHcEebOwsQuk00WJwjLI6oCjbBuEYdh/RaRqhjnyVlzqf41T1NnDtCedumZ56qyIh8euLFDqV1KfzTAVhg== + dependencies: + catering "^2.0.0" + deferred-leveldown "^7.0.0" + level-errors "^3.0.1" + level-iterator-stream "^5.0.0" + level-supports "^2.0.1" + queue-microtask "^1.2.3" + lie@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e" @@ -298,6 +437,11 @@ localforage@^1.3.0: dependencies: lie "3.1.1" +ltgt@^2.1.2: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.2.1.tgz#f35ca91c493f7b73da0e07495304f17b31f87ee5" + integrity sha1-81ypHEk/e3PaDgdJUwTxezH4fuU= + md5-node@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/md5-node/-/md5-node-1.0.1.tgz#0e22d009d46bdc95b1d3c5e8c8feddc1a5c3aa88" @@ -357,6 +501,11 @@ ms@2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== +napi-macros@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/napi-macros/-/napi-macros-2.0.0.tgz#2b6bae421e7b96eb687aa6c77a7858640670001b" + integrity sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg== + nedb@^1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/nedb/-/nedb-1.8.0.tgz#0e3502cd82c004d5355a43c9e55577bd7bd91d88" @@ -373,6 +522,11 @@ negotiator@0.6.2: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== +node-gyp-build@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3" + integrity sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q== + on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" @@ -420,6 +574,11 @@ qs@6.9.3: resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.3.tgz#bfadcd296c2d549f1dffa560619132c977f5008e" integrity sha512-EbZYNarm6138UKKq46tdx08Yo/q9ZhFoAXAI1meAFd2GtbRDhbZY2WQSICskT0c5q99aFzLG1D4nvTk9tqfXIw== +queue-microtask@^1.2.2, queue-microtask@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + random-bytes@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/random-bytes/-/random-bytes-1.0.0.tgz#4f68a1dc0ae58bd3fb95848c30324db75d64360b" @@ -440,12 +599,28 @@ raw-body@2.4.0: iconv-lite "0.4.24" unpipe "1.0.0" +readable-stream@^3.4.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +run-parallel-limit@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/run-parallel-limit/-/run-parallel-limit-1.1.0.tgz#be80e936f5768623a38a963262d6bef8ff11e7ba" + integrity sha512-jJA7irRNM91jaKc3Hcl1npHsFLOXOoTkPCUL1JEa1R82O2miplXXRaGdjW/KM/98YQWDhJLiSs793CnXfblJUw== + dependencies: + queue-microtask "^1.2.2" + safe-buffer@5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@5.2.1: +safe-buffer@5.2.1, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -499,6 +674,13 @@ string-random@^0.1.3: resolved "https://registry.yarnpkg.com/string-random/-/string-random-0.1.3.tgz#083f39835c2430fe0be76b9e72f0736e2e0f7b74" integrity sha512-g+UsIwzKhNi+9/+Q3Q7hP8R4HkQxiIkQlttnxw6GRdk9pnnkGIv53C6H8dvh8wxAVDhkqpnWeauaPXS1b2sBJg== +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + toidentifier@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" @@ -529,6 +711,11 @@ unpipe@1.0.0, unpipe@~1.0.0: resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= +util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + utils-merge@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"