@wxn0brp/database.js
Advanced tools
Comparing version 0.0.1 to 0.0.2
138
action.js
@@ -1,6 +0,5 @@ | ||
const fs = require("fs"); | ||
const gen = require("./gen"); | ||
const format = require("./format"); | ||
const fileM = require("./file"); | ||
const CacheManager = require("./cacheManager"); | ||
import { existsSync, mkdirSync, readdirSync, appendFileSync, rmSync, writeFileSync, statSync } from "fs"; | ||
import gen from "./gen.js"; | ||
import { stringify } from "./format.js"; | ||
import { find as _find, findOne as _findOne, update as _update, remove as _remove } from "./file/index.js"; | ||
@@ -18,10 +17,9 @@ const maxFileSize = 2 * 1024 * 1024; //2 MB | ||
* @param {string} folder - The folder where database files are stored. | ||
* @param {number} cacheThreshold - The cache threshold for query results. | ||
* @param {number} ttl - The time-to-live (TTL) for cached data. | ||
* @param {object} options - The options object. | ||
*/ | ||
constructor(folder, cacheThreshold, ttl){ | ||
constructor(folder, options){ | ||
this.folder = folder; | ||
this.cacheManager = new CacheManager(cacheThreshold, ttl); | ||
// this.cacheManager = new CacheManager(options.cacheThreshold, options.cacheTTL); | ||
if(!fs.existsSync(folder)) fs.mkdirSync(folder, { recursive: true }); | ||
if(!existsSync(folder)) mkdirSync(folder, { recursive: true }); | ||
} | ||
@@ -33,4 +31,11 @@ | ||
*/ | ||
getDBs(){ | ||
return fs.readdirSync(this.folder); | ||
getCollections(){ | ||
const collections = readdirSync(this.folder, { recursive: true, withFileTypes: true }) | ||
.filter(dirent => dirent.isDirectory()) | ||
.map(dirent => { | ||
if(dirent.parentPath === this.folder) return dirent.name; | ||
return dirent.parentPath.replace(this.folder + "/", "") + "/" + dirent.name | ||
}); | ||
return collections; | ||
} | ||
@@ -45,6 +50,17 @@ | ||
const path = this.folder + "/" + collection; | ||
if(!fs.existsSync(path)) fs.mkdirSync(path, { recursive: true }); | ||
if(!existsSync(path)) mkdirSync(path, { recursive: true }); | ||
} | ||
/** | ||
* Check if a collection exists. | ||
* @function | ||
* @param {string} collection - The name of the collection. | ||
* @returns {boolean} True if the collection exists, false otherwise. | ||
*/ | ||
issetCollection(collection){ | ||
const path = this.folder + "/" + collection; | ||
return existsSync(path); | ||
} | ||
/** | ||
* Add a new entry to the specified database. | ||
@@ -58,8 +74,8 @@ * @async | ||
async add(collection, arg, id_gen=true){ | ||
this.checkCollection(collection); | ||
await this.checkCollection(collection); | ||
const file = this.folder + "/" + collection + "/" + getLastFile(this.folder + "/" + collection); | ||
if(id_gen) arg._id = arg._id || gen(); | ||
const data = format.stringify(arg); | ||
fs.appendFileSync(file, data+"\n"); | ||
const data = stringify(arg); | ||
appendFileSync(file, data+"\n"); | ||
return arg; | ||
@@ -73,13 +89,15 @@ } | ||
* @param {function|Object} arg - The search criteria. It can be a function or an object. | ||
* @param {Object} context - The context object (for functions). | ||
* @param {Object} options - The options for the search. | ||
* @param {number} options.max - The maximum number of entries to return. Default is -1, meaning no limit. | ||
* @param {boolean} options.reverse - Whether to reverse the order of returned entries. Default is false. | ||
* @param {Object} findOpts - Update result object with findOpts options. | ||
* @returns {Promise<Object[]>} A Promise that resolves to an array of matching entries. | ||
*/ | ||
async find(collection, arg, options={}){ | ||
async find(collection, arg, context={}, options={}, findOpts={}){ | ||
options.reverse = options.reverse || false; | ||
options.max = options.max || -1; | ||
this.checkCollection(collection); | ||
let files = fs.readdirSync(this.folder + "/" + collection).filter(file => !/\.tmp$/.test(file)); | ||
await this.checkCollection(collection); | ||
const files = getSortedFiles(this.folder + "/" + collection).map(f => f.f); | ||
if(options.reverse) files.reverse(); | ||
@@ -91,3 +109,3 @@ let datas = []; | ||
for(let f of files){ | ||
let data = await fileM.find(this.folder + "/" + collection + "/" + f, arg, options); | ||
let data = await _find(this.folder + "/" + collection + "/" + f, arg, context, findOpts); | ||
if(options.reverse) data.reverse(); | ||
@@ -117,11 +135,13 @@ | ||
* @param {function|Object} arg - The search criteria. It can be a function or an object. | ||
* @param {Object} context - The context object (for functions). | ||
* @param {Object} findOpts - Update result object with findOpts options. | ||
* @returns {Promise<Object|null>} A Promise that resolves to the first matching entry or null if not found. | ||
*/ | ||
async findOne(collection, arg){ | ||
this.checkCollection(collection); | ||
let files = fs.readdirSync(this.folder + "/" + collection).filter(file => !/\.tmp$/.test(file)); | ||
async findOne(collection, arg, context={}, findOpts={}){ | ||
await this.checkCollection(collection); | ||
const files = getSortedFiles(this.folder + "/" + collection).map(f => f.f); | ||
files.reverse(); | ||
for(let f of files){ | ||
let data = await fileM.findOne(this.folder + "/" + collection + "/" + f, arg); | ||
let data = await _findOne(this.folder + "/" + collection + "/" + f, arg, context, findOpts); | ||
if(data){ | ||
@@ -140,7 +160,8 @@ return data; | ||
* @param {function|Object} obj - The updater function or object. | ||
* @param {Object} context - The context object (for functions). | ||
* @returns {Promise<boolean>} A Promise that resolves to `true` if entries were updated, or `false` otherwise. | ||
*/ | ||
async update(collection, arg, obj){ | ||
this.checkCollection(collection); | ||
return await fileM.update(this.folder, collection, arg, obj); | ||
async update(collection, arg, obj, context={}){ | ||
await this.checkCollection(collection); | ||
return await _update(this.folder, collection, arg, obj, context); | ||
} | ||
@@ -154,7 +175,8 @@ | ||
* @param {function|Object} obj - The updater function or object. | ||
* @param {Object} context - The context object (for functions). | ||
* @returns {Promise<boolean>} A Promise that resolves to `true` if one entry was updated, or `false` otherwise. | ||
*/ | ||
async updateOne(collection, arg, obj){ | ||
this.checkCollection(collection); | ||
return await fileM.update(this.folder, collection, arg, obj, true); | ||
async updateOne(collection, arg, obj, context={}){ | ||
await this.checkCollection(collection); | ||
return await _update(this.folder, collection, arg, obj, context, true); | ||
} | ||
@@ -167,7 +189,8 @@ | ||
* @param {function|Object} arg - The search criteria. It can be a function or an object. | ||
* @param {Object} context - The context object (for functions). | ||
* @returns {Promise<boolean>} A Promise that resolves to `true` if entries were removed, or `false` otherwise. | ||
*/ | ||
async remove(collection, arg){ | ||
this.checkCollection(collection); | ||
return await fileM.remove(this.folder, collection, arg); | ||
async remove(collection, arg, context={}){ | ||
await this.checkCollection(collection); | ||
return await _remove(this.folder, collection, arg, context); | ||
} | ||
@@ -180,7 +203,8 @@ | ||
* @param {function|Object} arg - The search criteria. It can be a function or an object. | ||
* @param {Object} context - The context object (for functions). | ||
* @returns {Promise<boolean>} A Promise that resolves to `true` if one entry was removed, or `false` otherwise. | ||
*/ | ||
async removeOne(collection, arg){ | ||
this.checkCollection(collection); | ||
return await fileM.remove(this.folder, collection, arg, true); | ||
async removeOne(collection, arg, context={}){ | ||
await this.checkCollection(collection); | ||
return await _remove(this.folder, collection, arg, context, true); | ||
} | ||
@@ -195,3 +219,3 @@ | ||
removeDb(collection){ | ||
fs.rmSync(this.folder + "/" + collection, { recursive: true, force: true }); | ||
rmSync(this.folder + "/" + collection, { recursive: true, force: true }); | ||
} | ||
@@ -206,22 +230,34 @@ } | ||
function getLastFile(path){ | ||
if(!fs.existsSync(path)) fs.mkdirSync(path, { recursive: true }); | ||
let files = fs.readdirSync(path).filter(file => !/\.tmp$/.test(file)); | ||
if(!existsSync(path)) mkdirSync(path, { recursive: true }); | ||
const files = getSortedFiles(path); | ||
if(files.length == 0){ | ||
fs.writeFileSync(path+"/1.db", ""); | ||
writeFileSync(path+"/1.db", ""); | ||
return "1.db"; | ||
} | ||
files = files.sort(); | ||
const last = files[files.length-1]; | ||
const info = path + "/" + last; | ||
if(fs.statSync(info).size > maxFileSize){ | ||
const temName = last.replace(".db", ""); | ||
const int = parseInt(temName) + 1; | ||
fs.writeFileSync(path + "/" + int + ".db", ""); | ||
return int+".db"; | ||
}else{ | ||
return last; | ||
} | ||
const info = path + "/" + last.f; | ||
if(statSync(info).size < maxFileSize) return last.f; | ||
const num = last.i + 1; | ||
writeFileSync(path + "/" + num + ".db", ""); | ||
return num+".db"; | ||
} | ||
module.exports = dbActionC; | ||
/** | ||
* Get all files in a directory sorted by name. | ||
* @param {string} path - The path to the directory. | ||
* @return {string[]} An array of file names sorted by name. | ||
*/ | ||
function getSortedFiles(path){ | ||
let files = readdirSync(path).filter(file => file.endsWith(".db")); | ||
if(files.length == 0) return []; | ||
files = files.map(file => parseInt(file.replace(".db", ""))) | ||
files = files.sort(); | ||
files = files.map(file => { return { i: file, f: file+".db" } }); | ||
return files; | ||
} | ||
export default dbActionC; |
@@ -83,2 +83,2 @@ /** | ||
module.exports = CacheManager; | ||
export default CacheManager; |
@@ -1,3 +0,4 @@ | ||
const dbActionC = require("./action"); | ||
const executorC = require("./executor"); | ||
import dbActionC from "./action.js"; | ||
import executorC from "./executor.js"; | ||
import CollectionManager from "./CollectionManager.js"; | ||
@@ -13,7 +14,13 @@ /** | ||
* @param {string} folder - The folder path where the database files are stored. | ||
* @param {number} [cacheThreshold=3] - The cache threshold for database entries (default: 3). | ||
* @param {number} [ttl=300000] - The time-to-live (TTL) for cached entries in milliseconds (default: 300,000 milliseconds or 5 minutes). | ||
* @param {object} [options] - The options object. | ||
* @param {number} [options.cacheThreshold=3] - The cache threshold for database entries (default: 3). | ||
* @param {number} [options.cacheTTL=300000] - The time-to-live (TTL) for cached entries in milliseconds (default: 300,000 milliseconds or 5 minutes). | ||
*/ | ||
constructor(folder, cacheThreshold=3, ttl=300_000){ | ||
this.dbAction = new dbActionC(folder, cacheThreshold, ttl); | ||
constructor(folder, options={}){ | ||
options = { | ||
cacheThreshold: 3, | ||
cacheTTL: 300_000, | ||
...options | ||
} | ||
this.dbAction = new dbActionC(folder, options); | ||
this.executor = new executorC(); | ||
@@ -23,2 +30,12 @@ } | ||
/** | ||
* Create a new instance of a CollectionManager class. | ||
* @function | ||
* @param {string} collection - The name of the collection. | ||
* @returns {CollectionManager} A new instance of CollectionManager. | ||
*/ | ||
c(collection){ | ||
return new CollectionManager(this, collection); | ||
} | ||
/** | ||
* Get the names of all available databases. | ||
@@ -29,4 +46,4 @@ * | ||
*/ | ||
getDBs(){ | ||
return this.dbAction.getDBs(); | ||
async getCollections(){ | ||
return await this.dbAction.getCollections(); | ||
} | ||
@@ -40,7 +57,18 @@ | ||
*/ | ||
checkCollection(collection){ | ||
this.dbAction.checkCollection(collection); | ||
async checkCollection(collection){ | ||
await this.dbAction.checkCollection(collection); | ||
} | ||
/** | ||
* Check if a collection exists. | ||
* | ||
* @function | ||
* @param {string} collection - The name of the collection. | ||
* @returns {boolean} True if the collection exists, false otherwise. | ||
*/ | ||
async issetCollection(collection){ | ||
return await this.dbAction.issetCollection(collection); | ||
} | ||
/** | ||
* Add data to a database. | ||
@@ -66,9 +94,11 @@ * | ||
* @param {function|Object} search - The query. It can be an object or a function. | ||
* @param {Object} context - The context object (for functions). | ||
* @param {Object} options - The options for the search. | ||
* @param {number} options.max - The maximum number of entries to return. Default is -1, meaning no limit. | ||
* @param {boolean} options.reverse - Whether to reverse the order of returned entries. Default is false. | ||
* @param {Object} findOpts - Update result object with findOpts options. | ||
* @returns {Promise<Array<Object>>} A Promise that resolves with the matching data. | ||
*/ | ||
async find(collection, search, options={}){ | ||
return await this.executor.addOp(this.dbAction.find.bind(this.dbAction), collection, search, options); | ||
async find(collection, search, context={}, options={}, findOpts={}){ | ||
return await this.executor.addOp(this.dbAction.find.bind(this.dbAction), collection, search, context, options, findOpts); | ||
} | ||
@@ -83,6 +113,8 @@ | ||
* @param {function|Object} search - The query. It can be an object or a function. | ||
* @param {Object} context - The context object (for functions). | ||
* @param {Object} findOpts - Update result object with findOpts options. | ||
* @returns {Promise<Object|null>} A Promise that resolves with the first matching data entry. | ||
*/ | ||
async findOne(collection, search){ | ||
return await this.executor.addOp(this.dbAction.findOne.bind(this.dbAction), collection, search); | ||
async findOne(collection, search, context={}, findOpts={}){ | ||
return await this.executor.addOp(this.dbAction.findOne.bind(this.dbAction), collection, search, context, findOpts); | ||
} | ||
@@ -98,6 +130,7 @@ | ||
* @param {function|Object} arg - Update arguments. | ||
* @param {Object} context - The context object (for functions). | ||
* @returns {Promise<boolean>} A Promise that resolves when the data is updated. | ||
*/ | ||
async update(collection, search, arg){ | ||
return await this.executor.addOp(this.dbAction.update.bind(this.dbAction), collection, search, arg); | ||
async update(collection, search, arg, context={}){ | ||
return await this.executor.addOp(this.dbAction.update.bind(this.dbAction), collection, search, arg, context); | ||
} | ||
@@ -113,6 +146,7 @@ | ||
* @param {function|Object} arg - The query. | ||
* @param {Object} context - The context object (for functions). | ||
* @returns {Promise<boolean>} A Promise that resolves when the data entry is updated. | ||
*/ | ||
async updateOne(collection, search, arg){ | ||
return await this.executor.addOp(this.dbAction.updateOne.bind(this.dbAction), collection, search, arg); | ||
async updateOne(collection, search, arg, context={}){ | ||
return await this.executor.addOp(this.dbAction.updateOne.bind(this.dbAction), collection, search, arg, context); | ||
} | ||
@@ -127,6 +161,7 @@ | ||
* @param {function|Object} search - The query. It can be an object or a function. | ||
* @param {Object} context - The context object (for functions). | ||
* @returns {Promise<boolean>} A Promise that resolves when the data is removed. | ||
*/ | ||
async remove(collection, search){ | ||
return await this.executor.addOp(this.dbAction.remove.bind(this.dbAction), collection, search); | ||
async remove(collection, search, context={}){ | ||
return await this.executor.addOp(this.dbAction.remove.bind(this.dbAction), collection, search, context); | ||
} | ||
@@ -141,6 +176,7 @@ | ||
* @param {function|Object} search - The query. It can be an object or a function. | ||
* @param {Object} context - The context object (for functions). | ||
* @returns {Promise<boolean>} A Promise that resolves when the data entry is removed. | ||
*/ | ||
async removeOne(collection, search){ | ||
return await this.executor.addOp(this.dbAction.removeOne.bind(this.dbAction), collection, search); | ||
async removeOne(collection, search, context={}){ | ||
return await this.executor.addOp(this.dbAction.removeOne.bind(this.dbAction), collection, search, context); | ||
} | ||
@@ -155,7 +191,15 @@ | ||
* @param {function|Object} add_arg - The arguments to be added to the new entry. | ||
* @param {Object} context - The context object (for functions). | ||
* @param {boolean} id_gen - Whether to generate an ID for the entry. Default is true. | ||
* @return {Promise<boolean>} A Promise that resolves to `true` if the entry was updated, or `false` if it was added. | ||
*/ | ||
async updateOneOrAdd(collection, search, arg, add_arg={}){ | ||
const res = await this.updateOne(collection, search, arg); | ||
if(!res) await this.add(collection, Object.assign(search, arg, add_arg)); | ||
async updateOneOrAdd(collection, search, arg, add_arg={}, context={}, id_gen=true){ | ||
const res = await this.updateOne(collection, search, arg, context); | ||
if(!res){ | ||
const assignData = []; | ||
if(typeof search === "object" && !Array.isArray(search)) assignData.push(search); | ||
if(typeof arg === "object" && !Array.isArray(arg)) assignData.push(arg); | ||
if(typeof add_arg === "object" && !Array.isArray(add_arg)) assignData.push(add_arg); | ||
await this.add(collection, Object.assign({}, ...assignData), id_gen); | ||
} | ||
return res; | ||
@@ -175,2 +219,2 @@ } | ||
module.exports = DataBase; | ||
export default DataBase; |
@@ -54,2 +54,2 @@ /** | ||
module.exports = executorC; | ||
export default executorC; |
@@ -1,5 +0,5 @@ | ||
const fs = require("fs"); | ||
const { pathRepair, createRL } = require("./utils"); | ||
const format = require("../format"); | ||
const more = require("../more"); | ||
import { existsSync, promises } from "fs"; | ||
import { pathRepair, createRL } from "./utils.js"; | ||
import { parse } from "../format.js"; | ||
import { hasFieldsAdvanced, updateFindObject } from "../more.js"; | ||
@@ -11,15 +11,18 @@ /** | ||
* @param {string} line - The line of text from the file. | ||
* @param {Object} context - The context object (for functions). | ||
* @param {Object} findOpts - Update result object with findOpts options. | ||
* @returns {Promise<Object|null>} A Promise that resolves to the matching object or null. | ||
*/ | ||
async function findProcesLine(arg, line){ | ||
const ob = format.parse(line); | ||
async function findProcesLine(arg, line, context={}, findOpts={}){ | ||
const ob = parse(line); | ||
let res = false; | ||
if(typeof arg === "function"){ | ||
if(arg(ob)) res = true; | ||
if(arg(ob, context)) res = true; | ||
}else if(typeof arg === "object" && !Array.isArray(arg)){ | ||
if(more.hasFieldsAdvanced(ob, arg)) res = true; | ||
if(hasFieldsAdvanced(ob, arg)) res = true; | ||
} | ||
return res ? ob : null; | ||
if(res) return updateFindObject(ob, findOpts); | ||
return null; | ||
} | ||
@@ -32,9 +35,11 @@ | ||
* @param {function|Object} arg - The search criteria. It can be a function or an object. | ||
* @param {Object} context - The context object (for functions). | ||
* @param {Object} findOpts - Update result object with findOpts options. | ||
* @returns {Promise<Object[]>} A Promise that resolves to an array of matching objects. | ||
*/ | ||
async function find(file, arg){ | ||
export async function find(file, arg, context={}, findOpts={}){ | ||
file = pathRepair(file); | ||
return await new Promise(async (resolve) => { | ||
if(!fs.existsSync(file)){ | ||
await fs.promises.writeFile(file, ""); | ||
if(!existsSync(file)){ | ||
await promises.writeFile(file, ""); | ||
resolve(false); | ||
@@ -48,6 +53,7 @@ return; | ||
const res = await findProcesLine(arg, line); | ||
const res = await findProcesLine(arg, line, context, findOpts); | ||
if(res) resF.push(res); | ||
}; | ||
resolve(resF); | ||
rl.close(); | ||
}) | ||
@@ -61,9 +67,11 @@ } | ||
* @param {function|Object} arg - The search criteria. It can be a function or an object. | ||
* @param {Object} context - The context object (for functions). | ||
* @param {Object} findOpts - Update result object with findOpts options. | ||
* @returns {Promise<Object>} A Promise that resolves to the first matching object found or an empty array. | ||
*/ | ||
async function findOne(file, arg){ | ||
export async function findOne(file, arg, context={}, findOpts={}){ | ||
file = pathRepair(file); | ||
return await new Promise(async (resolve) => { | ||
if(!fs.existsSync(file)){ | ||
await fs.promises.writeFile(file, ""); | ||
if(!existsSync(file)){ | ||
await promises.writeFile(file, ""); | ||
resolve(false); | ||
@@ -76,3 +84,3 @@ return; | ||
const res = await findProcesLine(arg, line); | ||
const res = await findProcesLine(arg, line, context, findOpts); | ||
if(res){ | ||
@@ -85,7 +93,2 @@ resolve(res); | ||
}); | ||
} | ||
module.exports = { | ||
find, | ||
findOne | ||
}; | ||
} |
@@ -1,10 +0,3 @@ | ||
const update = require("./update"); | ||
const remove = require("./remove"); | ||
const { find, findOne } = require("./find"); | ||
module.exports = { | ||
update, | ||
remove, | ||
find, | ||
findOne | ||
} | ||
export { default as update } from "./update.js"; | ||
export { default as remove } from "./remove.js"; | ||
export * from "./find.js"; |
@@ -1,5 +0,5 @@ | ||
const fs = require("fs"); | ||
const { pathRepair, createRL } = require("./utils"); | ||
const format = require("../format"); | ||
const more = require("../more"); | ||
import { existsSync, promises, appendFileSync, readdirSync } from "fs"; | ||
import { pathRepair, createRL } from "./utils.js"; | ||
import { parse } from "../format.js"; | ||
import { hasFieldsAdvanced } from "../more.js"; | ||
@@ -11,13 +11,14 @@ /** | ||
* @param {function|Object} search - The search criteria. It can be a function or an object. | ||
* @param {Object} context - The context object (for functions). | ||
* @param {boolean} [one=false] - Indicates whether to remove only one matching entry (default: false). | ||
* @returns {Promise<boolean>} A Promise that resolves to `true` if entries were removed, or `false` otherwise. | ||
*/ | ||
async function removeWorker(file, search, one=false){ | ||
async function removeWorker(file, search, context={}, one=false){ | ||
file = pathRepair(file); | ||
if(!fs.existsSync(file)){ | ||
await fs.promises.writeFile(file, ""); | ||
if(!existsSync(file)){ | ||
await promises.writeFile(file, ""); | ||
return false; | ||
} | ||
await fs.promises.copyFile(file, file+".tmp"); | ||
await fs.promises.writeFile(file, ""); | ||
await promises.copyFile(file, file+".tmp"); | ||
await promises.writeFile(file, ""); | ||
@@ -29,10 +30,10 @@ const rl = createRL(file+".tmp"); | ||
if(one && removed){ | ||
fs.appendFileSync(file, line+"\n"); | ||
appendFileSync(file, line+"\n"); | ||
continue; | ||
} | ||
const data = format.parse(line); | ||
const data = parse(line); | ||
if(typeof search === "function"){ | ||
if(search(data)){ | ||
if(search(data, context)){ | ||
removed = true; | ||
@@ -42,3 +43,3 @@ continue; | ||
}else if(typeof search === "object" && !Array.isArray(search)){ | ||
if(more.hasFieldsAdvanced(data, search)){ | ||
if(hasFieldsAdvanced(data, search)){ | ||
removed = true; | ||
@@ -49,5 +50,5 @@ continue; | ||
fs.appendFileSync(file, line+"\n"); | ||
appendFileSync(file, line+"\n"); | ||
} | ||
await fs.promises.writeFile(file+".tmp", ""); | ||
await promises.writeFile(file+".tmp", ""); | ||
return removed; | ||
@@ -62,11 +63,12 @@ } | ||
* @param {function|Object} arg - The search criteria. It can be a function or an object. | ||
* @param {Object} context - The context object (for functions). | ||
* @param {boolean} one - Indicates whether to remove only one matching entry (default: false). | ||
* @returns {Promise<boolean>} A Promise that resolves to `true` if entries were removed, or `false` otherwise. | ||
*/ | ||
async function remove(folder, name, arg, one){ | ||
let files = fs.readdirSync(folder + "/" + name).filter(file => !/\.tmp$/.test(file)); | ||
async function remove(folder, name, arg, context={}, one){ | ||
let files = readdirSync(folder + "/" + name).filter(file => !/\.tmp$/.test(file)); | ||
files.reverse(); | ||
let remove = false; | ||
for(const file of files){ | ||
const removed = await removeWorker(folder + "/" + name + "/" + file, arg, one); | ||
const removed = await removeWorker(folder + "/" + name + "/" + file, arg, context, one); | ||
if(one && removed) break; | ||
@@ -78,2 +80,2 @@ remove = remove || removed; | ||
module.exports = remove; | ||
export default remove; |
@@ -1,5 +0,5 @@ | ||
const fs = require("fs"); | ||
const { pathRepair, createRL } = require("./utils"); | ||
const format = require("../format"); | ||
const more = require("../more"); | ||
import { existsSync, promises, appendFileSync, readdirSync } from "fs"; | ||
import { pathRepair, createRL } from "./utils.js"; | ||
import { parse, stringify } from "../format.js"; | ||
import { hasFieldsAdvanced, updateObject } from "../more.js"; | ||
@@ -12,13 +12,14 @@ /** | ||
* @param {function|Object} updater - The updater function or object. | ||
* @param {Object} context - The context object (for functions). | ||
* @param {boolean} [one=false] - Indicates whether to update only one matching entry (default: false). | ||
* @returns {Promise<boolean>} A Promise that resolves to `true` if the file was updated, or `false` otherwise. | ||
*/ | ||
async function updateWorker(file, search, updater, one=false){ | ||
async function updateWorker(file, search, updater, context={}, one=false){ | ||
file = pathRepair(file); | ||
if(!fs.existsSync(file)){ | ||
await fs.promises.writeFile(file, ""); | ||
if(!existsSync(file)){ | ||
await promises.writeFile(file, ""); | ||
return false; | ||
} | ||
await fs.promises.copyFile(file, file+".tmp"); | ||
await fs.promises.writeFile(file, ""); | ||
await promises.copyFile(file, file+".tmp"); | ||
await promises.writeFile(file, ""); | ||
@@ -30,13 +31,13 @@ const rl = createRL(file+".tmp"); | ||
if(one && updated){ | ||
fs.appendFileSync(file, line+"\n"); | ||
appendFileSync(file, line+"\n"); | ||
continue; | ||
} | ||
const data = format.parse(line); | ||
const data = parse(line); | ||
let ob = false; | ||
if(typeof search === "function"){ | ||
ob = search(data) || false; | ||
ob = search(data, context) || false; | ||
}else if(typeof search === "object" && !Array.isArray(search)){ | ||
ob = more.hasFieldsAdvanced(data, search); | ||
ob = hasFieldsAdvanced(data, search); | ||
} | ||
@@ -47,13 +48,13 @@ | ||
if(typeof updater === "function"){ | ||
updateObj = updater(data); | ||
updateObj = updater(data, context); | ||
}else if(typeof updater === "object" && !Array.isArray(updater)){ | ||
updateObj = more.updateObject(data, updater); | ||
updateObj = updateObject(data, updater); | ||
} | ||
line = await format.stringify(updateObj); | ||
line = await stringify(updateObj); | ||
updated = true; | ||
} | ||
fs.appendFileSync(file, line+"\n"); | ||
appendFileSync(file, line+"\n"); | ||
} | ||
await fs.promises.writeFile(file+".tmp", ""); | ||
await promises.writeFile(file+".tmp", ""); | ||
return updated; | ||
@@ -69,11 +70,12 @@ } | ||
* @param {function|Object} obj - The updater function or object. | ||
* @param {Object} context - The context object (for functions). | ||
* @param {boolean} one - Indicates whether to update only one matching entry (default: false). | ||
* @returns {Promise<boolean>} A Promise that resolves to `true` if entries were updated, or `false` otherwise. | ||
*/ | ||
async function update(folder, name, arg, obj, one){ | ||
let files = fs.readdirSync(folder + "/" + name).filter(file => !/\.tmp$/.test(file)); | ||
async function update(folder, name, arg, obj, context={}, one){ | ||
let files = readdirSync(folder + "/" + name).filter(file => !/\.tmp$/.test(file)); | ||
files.reverse(); | ||
let update = false; | ||
for(const file of files){ | ||
const updated = await updateWorker(folder + "/" + name + "/" + file, arg, obj, one); | ||
const updated = await updateWorker(folder + "/" + name + "/" + file, arg, obj, context, one); | ||
if(one && updated) return true; | ||
@@ -85,2 +87,2 @@ update = update || updated; | ||
module.exports = update; | ||
export default update; |
@@ -1,3 +0,3 @@ | ||
const fs = require("fs"); | ||
const readline = require("readline"); | ||
import { createReadStream } from "fs"; | ||
import { createInterface } from "readline"; | ||
@@ -10,3 +10,3 @@ /** | ||
*/ | ||
function pathRepair(path){ | ||
export function pathRepair(path){ | ||
return path.replaceAll("//", "/"); | ||
@@ -21,5 +21,5 @@ } | ||
*/ | ||
function createRL(file){ | ||
const read_stream = fs.createReadStream(file, { highWaterMark: 10 * 1024 * 1024 }); //10MB | ||
const rl = readline.createInterface({ | ||
export function createRL(file){ | ||
const read_stream = createReadStream(file, { highWaterMark: 10 * 1024 * 1024 }); //10MB | ||
const rl = createInterface({ | ||
input: read_stream, | ||
@@ -29,4 +29,2 @@ crlfDelay: Infinity | ||
return rl; | ||
} | ||
module.exports = { pathRepair, createRL }; | ||
} |
@@ -1,27 +0,29 @@ | ||
const json5 = require("json5"); | ||
import json5 from "json5"; | ||
module.exports = { | ||
/** | ||
* Parse JSON5 data. | ||
* @function | ||
* @param {string} data - The JSON5 data to parse. | ||
* @returns {Object} The parsed JavaScript object. | ||
*/ | ||
parse: (data) => { | ||
if(!data.startsWith("{")) data = "{"+data+"}"; | ||
return json5.parse(data); | ||
}, | ||
/** | ||
* Stringify data into JSON5 format. | ||
* @function | ||
* @param {Object} data - The JavaScript object to stringify. | ||
* @returns {string} The JSON5 formatted string. | ||
*/ | ||
stringify: (data) => { | ||
data = json5.stringify(data); | ||
if(data.startsWith("{")){ | ||
data = data.slice(1, -1); | ||
} | ||
return data; | ||
}, | ||
}; | ||
/** | ||
* Parses given string into a JSON object. If the string does not start with | ||
* a {, it is wrapped in one. This allows for a shorthand when | ||
* storing/reading data from a file. | ||
* | ||
* @param {string} data | ||
* @returns {Object} | ||
*/ | ||
export function parse(data){ | ||
if(!data.startsWith("{")) data = "{" + data + "}"; | ||
return json5.parse(data); | ||
} | ||
/** | ||
* Converts given object to a string. If the string is a valid json5, it is | ||
* returned as is. If it is a valid json5 wrapped in {}, the curly brackets | ||
* are removed. Otherwise the string is wrapped in {}. | ||
* | ||
* @param {Object} data | ||
* @return {String} | ||
*/ | ||
export function stringify(data){ | ||
data = json5.stringify(data); | ||
if(data.startsWith("{")){ | ||
data = data.slice(1, -1); | ||
} | ||
return data; | ||
} |
@@ -10,3 +10,3 @@ const usedIdsMap = new Map(); | ||
*/ | ||
function genId(parts, fill=1){ | ||
export default function genId(parts, fill=1){ | ||
parts = changeInputToPartsArray(parts, fill); | ||
@@ -98,4 +98,2 @@ const time = getTime(); | ||
return [1, 1]; | ||
} | ||
module.exports = genId; | ||
} |
138
graph.js
@@ -1,36 +0,130 @@ | ||
const DataBase = require("./database"); | ||
import DataBase from "./database.js"; | ||
/** | ||
* A class representing a graph database. | ||
* @class | ||
*/ | ||
class Graph{ | ||
constructor(folder){ | ||
this.db = new DataBase(folder); | ||
/** | ||
* Initializes the graph database. | ||
* @constructor | ||
* @param {string} databaseFolder - The folder where the database is stored. | ||
*/ | ||
constructor(databaseFolder){ | ||
this.db = new DataBase(databaseFolder); | ||
} | ||
async add(collection, a, b){ | ||
[a, b] = [a, b].sort(); | ||
return await this.db.add(collection, { a, b }, false); | ||
/** | ||
* Adds an edge between two nodes. | ||
* @async | ||
* @function | ||
* @param {string} collection - The name of the collection. | ||
* @param {string} nodeA - The first node. | ||
* @param {string} nodeB - The second node. | ||
* @returns {Promise<Object>} A promise that resolves with the added edge. | ||
*/ | ||
async add(collection, nodeA, nodeB){ | ||
const sortedNodes = [nodeA, nodeB].sort(); | ||
return await this.db.add(collection, { | ||
a: sortedNodes[0], | ||
b: sortedNodes[1] | ||
}, false); | ||
} | ||
async remove(collection, a, b){ | ||
[a, b] = [a, b].sort(); | ||
return await this.db.removeOne(collection, { a, b }); | ||
/** | ||
* Removes an edge between two nodes. | ||
* @async | ||
* @function | ||
* @param {string} collection - The name of the collection. | ||
* @param {string} nodeA - The first node. | ||
* @param {string} nodeB - The second node. | ||
* @returns {Promise<boolean>} A promise that resolves when the edge is removed. | ||
*/ | ||
async remove(collection, nodeA, nodeB){ | ||
const sortedNodes = [nodeA, nodeB].sort(); | ||
const query = { a: sortedNodes[0], b: sortedNodes[1] }; | ||
return await this.db.removeOne(collection, query); | ||
} | ||
async find(collection, d){ | ||
const buffor = []; | ||
const a = await this.db.find(collection, { a: d }); | ||
const b = await this.db.find(collection, { b: d }); | ||
if(a) buffor.push(...a); | ||
if(b) buffor.push(...b); | ||
return buffor; | ||
/** | ||
* Finds all edges with either node equal to `node`. | ||
* @async | ||
* @function | ||
* @param {string} collection - The name of the collection. | ||
* @param {string} node - The node to search for. | ||
* @returns {Promise<Object[]>} A promise that resolves with the found edges. | ||
*/ | ||
async find(collection, node){ | ||
const edges = []; | ||
const edgesByANode = await this.db.find(collection, { a: node }); | ||
const edgesByBNode = await this.db.find(collection, { b: node }); | ||
if(edgesByANode) edges.push(...edgesByANode); | ||
if(edgesByBNode) edges.push(...edgesByBNode); | ||
return edges; | ||
} | ||
async findOne(collection, d, e){ | ||
const a = await this.db.findOne(collection, { a: d, b: e }); | ||
if(a) return a; | ||
const b = await this.db.findOne(collection, { a: e, b: d }); | ||
if(b) return b; | ||
/** | ||
* Finds one edge with either node equal to `nodeA` and the other equal to `nodeB`. | ||
* @async | ||
* @function | ||
* @param {string} collection - The name of the collection. | ||
* @param {string} nodeA - The first node. | ||
* @param {string} nodeB - The second node. | ||
* @returns {Promise<Object|null>} A promise that resolves with the found edge or null if not found. | ||
*/ | ||
async findOne(collection, nodeA, nodeB){ | ||
const edgeAB = await this.db.findOne(collection, { a: nodeA, b: nodeB }); | ||
if(edgeAB) return edgeAB; | ||
const edgeBA = await this.db.findOne(collection, { a: nodeB, b: nodeA }); | ||
if(edgeBA) return edgeBA; | ||
return null; | ||
} | ||
/** | ||
* Gets all edges in the database. | ||
* @async | ||
* @function | ||
* @param {string} collection - The name of the collection. | ||
* @returns {Promise<Object[]>} A promise that resolves with all edges in the database. | ||
*/ | ||
async getAll(collection){ | ||
return await this.db.find(collection, {}); | ||
} | ||
/** | ||
* Get the names of all available databases. | ||
* | ||
* @function | ||
* @returns {string[]} An array of database names. | ||
*/ | ||
async getCollections(){ | ||
return await this.db.getCollections(); | ||
} | ||
/** | ||
* Check and create the specified collection if it doesn't exist. | ||
* | ||
* @function | ||
* @param {string} collection - The collection to check. | ||
*/ | ||
async checkCollection(collection){ | ||
await this.dbAction.checkCollection(collection); | ||
} | ||
/** | ||
* Check if a collection exists. | ||
* | ||
* @function | ||
* @param {string} collection - The name of the collection. | ||
* @returns {boolean} True if the collection exists, false otherwise. | ||
*/ | ||
async issetCollection(collection){ | ||
return await this.dbAction.issetCollection(collection); | ||
} | ||
} | ||
module.exports = Graph; | ||
export default Graph; |
63
more.js
@@ -9,22 +9,22 @@ /** | ||
*/ | ||
function hasFieldsAdvanced(obj, fields){ | ||
if(typeof fields !== 'object' || fields === null){ | ||
export function hasFieldsAdvanced(obj, fields){ | ||
if(typeof fields !== "object" || fields === null){ | ||
throw new Error("Fields must be an object"); | ||
} | ||
if('$and' in fields){ | ||
return fields['$and'].every(subFields => hasFieldsAdvanced(obj, subFields)); | ||
if("$and" in fields){ | ||
return fields["$and"].every(subFields => hasFieldsAdvanced(obj, subFields)); | ||
} | ||
if('$or' in fields){ | ||
return fields['$or'].some(subFields => hasFieldsAdvanced(obj, subFields)); | ||
if("$or" in fields){ | ||
return fields["$or"].some(subFields => hasFieldsAdvanced(obj, subFields)); | ||
} | ||
if('$set' in fields){ | ||
const setFields = fields['$set']; | ||
if("$set" in fields){ | ||
const setFields = fields["$set"]; | ||
return hasFields(obj, setFields); | ||
} | ||
if('$not' in fields){ | ||
return !hasFieldsAdvanced(obj, fields['$not']); | ||
if("$not" in fields){ | ||
return !hasFieldsAdvanced(obj, fields["$not"]); | ||
} | ||
@@ -42,7 +42,7 @@ | ||
*/ | ||
function hasFields(obj, fields){ | ||
export function hasFields(obj, fields){ | ||
const keys = Object.keys(fields); | ||
return keys.every(key => { | ||
if(obj[key]){ | ||
if(typeof fields[key] === 'object' && fields[key] !== null){ | ||
if(typeof fields[key] === "object" && fields[key] !== null){ | ||
return hasFields(obj[key], fields[key]); | ||
@@ -63,3 +63,3 @@ } | ||
*/ | ||
function updateObject(obj, newVal){ | ||
export function updateObject(obj, newVal){ | ||
for(let key in newVal){ | ||
@@ -73,6 +73,35 @@ if(newVal.hasOwnProperty(key)){ | ||
module.exports = { | ||
hasFieldsAdvanced, | ||
hasFields, | ||
updateObject | ||
/** | ||
* Updates an object with new values from a findOpts object. | ||
* @function | ||
* @param {Object} obj - The object to update. | ||
* @param {Object} findOpts - An object containing options to update the target object. | ||
* @param {function} [findOpts.transform] - A function to transform the object before applying the other options. | ||
* @param {string[]} [findOpts.select] - An array of fields to select from the target object. | ||
* @param {string[]} [findOpts.exclude] - An array of fields to exclude from the target object. | ||
* @returns {Object} The updated object. | ||
*/ | ||
export function updateFindObject(obj, findOpts){ | ||
const { | ||
transform, | ||
select, | ||
exclude, | ||
} = findOpts; | ||
if(typeof transform === "function") obj = transform(obj); | ||
if(Array.isArray(exclude)){ | ||
exclude.forEach(field => { | ||
if(obj.hasOwnProperty(field)) delete obj[field]; | ||
}); | ||
} | ||
if(Array.isArray(select)){ | ||
obj = select.reduce((acc, field) => { | ||
if(obj.hasOwnProperty(field)) acc[field] = obj[field]; | ||
return acc; | ||
}, {}); | ||
} | ||
return obj; | ||
} |
{ | ||
"name": "@wxn0brp/database.js", | ||
"version": "0.0.1", | ||
"version": "0.0.2", | ||
"main": "index.js", | ||
"description": "A simple file-based database management system with support for CRUD operations, custom queries, and graph structures.", | ||
"homepage": "https://github.com/wxn0brP/fusion-chat/blob/master/back/db/README.md", | ||
"homepage": "https://github.com/wxn0brP/database.js", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/wxn0brP/fusion-chat.git", | ||
"directory": "back/db" | ||
"url": "https://github.com/wxn0brP/database.js.git" | ||
}, | ||
"keywords": ["database", "file-based", "CRUD", "graph", "query"], | ||
"keywords": [ | ||
"database", | ||
"file-based", | ||
"CRUD", | ||
"graph", | ||
"query" | ||
], | ||
"author": "wxn0brP", | ||
"license": "MIT", | ||
"private": false, | ||
"type": "module", | ||
"dependencies": { | ||
"body-parser": "^1.20.3", | ||
"cors": "^2.8.5", | ||
"dotenv": "^16.4.5", | ||
"express": "^4.21.1", | ||
"got": "^14.4.2", | ||
"json5": "^2.2.3", | ||
"jwt-simple": "^0.5.6", | ||
"node-cache": "^5.1.2", | ||
"readline": "^1.3.0" | ||
} | ||
} | ||
} |
@@ -16,5 +16,5 @@ # DataBase Class | ||
```javascript | ||
const DataBase = require('@wxn0brp/database'); | ||
import DataBase from '@wxn0brp/database/database.js'; | ||
async function main() { | ||
async function main(){ | ||
// Initialize the database with a folder path and optional cache settings | ||
@@ -24,3 +24,3 @@ const db = new DataBase('./data'); | ||
// Create or check a collection | ||
db.checkCollection('users'); | ||
await db.checkCollection('users'); | ||
@@ -49,13 +49,14 @@ // Add a new user to the collection | ||
| ----------- | ----------- | ---------- | ------- | | ||
| `getDBs()` | Retrieves the names of all available databases. | None | `string[]` | | ||
| `checkCollection()` | Ensures that a collection exists, creating it if necessary. | `collection` (string): Name of the collection | `void` | | ||
| `add()` | Adds data to a collection, optionally generating an ID. | `collection` (string), `data` (Object), `id_gen` (boolean) | `Promise<Object>` | | ||
| `find()` | Finds data entries matching a query. | `collection` (string), `search` (function/Object), `options` (Object) - { max, reverse } | `Promise<Array<Object>>` | | ||
| `findOne()` | Finds the first data entry matching a query. | `collection` (string), `search` (function/Object) | `Promise<Object\|null>` | | ||
| `update()` | Updates data entries matching a query. | `collection` (string), `search` (function/Object), `arg` (function/Object) | `Promise<boolean>` | | ||
| `updateOne()` | Updates the first data entry matching a query. | `collection` (string), `search` (function/Object), `arg` (function/Object) | `Promise<boolean>` | | ||
| `remove()` | Removes data entries matching a query. | `collection` (string), `search` (function/Object) | `Promise<boolean>` | | ||
| `removeOne()` | Removes the first data entry matching a query. | `collection` (string), `search` (function/Object) | `Promise<boolean>` | | ||
| `updateOneOrAdd()` | Updates one entry or adds a new one if no match is found. | `collection` (string), `search` (function/Object), `arg` (function/Object), `add_arg` (function/Object) | `Promise<boolean>` | | ||
| `removeDb()` | Removes an entire database collection from the file system. | `collection` (string) | `void` | | ||
| `getCollections` | Retrieves the names of all available collections. | None | `string[]` | | ||
| `checkCollection` | Ensures that a collection exists, creating it if necessary. | `collection` (string): Name of the collection | `Promise<void>` | | ||
| `issetCollection` | Checks if a collection exists. | `collection` (string): Name of the collection | `Promise<boolean>` | | ||
| `add` | Adds data to a collection, optionally generating an ID. | `collection` (string), `data` (Object), `id_gen` (boolean) | `Promise<Object>` | | ||
| `find` | Finds data entries matching a query. | `collection` (string), `search` (function/Object), `context` (Object), `options` (Object) - { max, reverse } | `Promise<Array<Object>>` | | ||
| `findOne` | Finds the first data entry matching a query. | `collection` (string), `search` (function/Object), `context` (Object) | `Promise<Object\|null>` | | ||
| `update` | Updates data entries matching a query. | `collection` (string), `search` (function/Object), `arg` (function/Object), `context` (Object) | `Promise<boolean>` | | ||
| `updateOne` | Updates the first data entry matching a query. | `collection` (string), `search` (function/Object), `arg` (function/Object), `context` (Object) | `Promise<boolean>` | | ||
| `remove` | Removes data entries matching a query. | `collection` (string), `search` (function/Object), `context` (Object) | `Promise<boolean>` | | ||
| `removeOne` | Removes the first data entry matching a query. | `collection` (string), `search` (function/Object), `context` (Object) | `Promise<boolean>` | | ||
| `updateOneOrAdd` | Updates one entry or adds a new one if no match is found. | `collection` (string), `search` (function/Object), `arg` (function/Object), `add_arg` (function/Object), `context` (Object), `id_gen` (boolean) | `Promise<boolean>` | | ||
| `removeDb` | Removes an entire database collection from the file system. | `collection` (string) | `void` | | ||
@@ -62,0 +63,0 @@ --- |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Unpublished package
Supply chain riskPackage version was not found on the registry. It may exist on a different registry and need to be configured to pull from that registry.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 5 instances in 1 package
No website
QualityPackage does not have a website.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Deprecated
MaintenanceThe maintainer of the package marked it as deprecated. This could indicate that a single version should not be used, or that the package is no longer maintained and any new vulnerabilities will not be fixed.
Found 1 instance in 1 package
Unpopular package
QualityThis package is not very popular.
Found 1 instance in 1 package
No website
QualityPackage does not have a website.
Found 1 instance in 1 package
576161
53
4409
0
0
194
Yes
9
10
9
+ Addedbody-parser@^1.20.3
+ Addedcors@^2.8.5
+ Addeddotenv@^16.4.5
+ Addedexpress@^4.21.1
+ Addedgot@^14.4.2
+ Addedjwt-simple@^0.5.6
+ Addednode-cache@^5.1.2
+ Added@sec-ant/readable-stream@0.4.1(transitive)
+ Added@sindresorhus/is@7.0.1(transitive)
+ Added@szmarczak/http-timer@5.0.1(transitive)
+ Added@types/http-cache-semantics@4.0.4(transitive)
+ Addedaccepts@1.3.8(transitive)
+ Addedarray-flatten@1.1.1(transitive)
+ Addedbody-parser@1.20.3(transitive)
+ Addedbytes@3.1.2(transitive)
+ Addedcacheable-lookup@7.0.0(transitive)
+ Addedcacheable-request@12.0.1(transitive)
+ Addedcall-bind@1.0.7(transitive)
+ Addedclone@2.1.2(transitive)
+ Addedcontent-disposition@0.5.4(transitive)
+ Addedcontent-type@1.0.5(transitive)
+ Addedcookie@0.7.1(transitive)
+ Addedcookie-signature@1.0.6(transitive)
+ Addedcors@2.8.5(transitive)
+ Addeddebug@2.6.9(transitive)
+ Addeddecompress-response@6.0.0(transitive)
+ Addeddefer-to-connect@2.0.1(transitive)
+ Addeddefine-data-property@1.1.4(transitive)
+ Addeddepd@2.0.0(transitive)
+ Addeddestroy@1.2.0(transitive)
+ Addeddotenv@16.4.5(transitive)
+ Addedee-first@1.1.1(transitive)
+ Addedencodeurl@1.0.22.0.0(transitive)
+ Addedes-define-property@1.0.0(transitive)
+ Addedes-errors@1.3.0(transitive)
+ Addedescape-html@1.0.3(transitive)
+ Addedetag@1.8.1(transitive)
+ Addedexpress@4.21.1(transitive)
+ Addedfinalhandler@1.3.1(transitive)
+ Addedform-data-encoder@4.0.2(transitive)
+ Addedforwarded@0.2.0(transitive)
+ Addedfresh@0.5.2(transitive)
+ Addedfunction-bind@1.1.2(transitive)
+ Addedget-intrinsic@1.2.4(transitive)
+ Addedget-stream@9.0.1(transitive)
+ Addedgopd@1.0.1(transitive)
+ Addedgot@14.4.5(transitive)
+ Addedhas-property-descriptors@1.0.2(transitive)
+ Addedhas-proto@1.0.3(transitive)
+ Addedhas-symbols@1.0.3(transitive)
+ Addedhasown@2.0.2(transitive)
+ Addedhttp-cache-semantics@4.1.1(transitive)
+ Addedhttp-errors@2.0.0(transitive)
+ Addedhttp2-wrapper@2.2.1(transitive)
+ Addediconv-lite@0.4.24(transitive)
+ Addedinherits@2.0.4(transitive)
+ Addedipaddr.js@1.9.1(transitive)
+ Addedis-stream@4.0.1(transitive)
+ Addedjson-buffer@3.0.1(transitive)
+ Addedjwt-simple@0.5.6(transitive)
+ Addedkeyv@4.5.4(transitive)
+ Addedlowercase-keys@3.0.0(transitive)
+ Addedmedia-typer@0.3.0(transitive)
+ Addedmerge-descriptors@1.0.3(transitive)
+ Addedmethods@1.1.2(transitive)
+ Addedmime@1.6.0(transitive)
+ Addedmime-db@1.52.0(transitive)
+ Addedmime-types@2.1.35(transitive)
+ Addedmimic-response@3.1.04.0.0(transitive)
+ Addedms@2.0.02.1.3(transitive)
+ Addednegotiator@0.6.3(transitive)
+ Addednode-cache@5.1.2(transitive)
+ Addednormalize-url@8.0.1(transitive)
+ Addedobject-assign@4.1.1(transitive)
+ Addedobject-inspect@1.13.3(transitive)
+ Addedon-finished@2.4.1(transitive)
+ Addedp-cancelable@4.0.1(transitive)
+ Addedparseurl@1.3.3(transitive)
+ Addedpath-to-regexp@0.1.10(transitive)
+ Addedproxy-addr@2.0.7(transitive)
+ Addedqs@6.13.0(transitive)
+ Addedquick-lru@5.1.1(transitive)
+ Addedrange-parser@1.2.1(transitive)
+ Addedraw-body@2.5.2(transitive)
+ Addedresolve-alpn@1.2.1(transitive)
+ Addedresponselike@3.0.0(transitive)
+ Addedsafe-buffer@5.2.1(transitive)
+ Addedsafer-buffer@2.1.2(transitive)
+ Addedsend@0.19.0(transitive)
+ Addedserve-static@1.16.2(transitive)
+ Addedset-function-length@1.2.2(transitive)
+ Addedsetprototypeof@1.2.0(transitive)
+ Addedside-channel@1.0.6(transitive)
+ Addedstatuses@2.0.1(transitive)
+ Addedtoidentifier@1.0.1(transitive)
+ Addedtype-fest@4.28.0(transitive)
+ Addedtype-is@1.6.18(transitive)
+ Addedunpipe@1.0.0(transitive)
+ Addedutils-merge@1.0.1(transitive)
+ Addedvary@1.1.2(transitive)