@inrixia/db
Advanced tools
| export declare type JSONSafeValue = string | number | boolean | { | ||
| [key: string]: JSONSafeValue; | ||
| } | JSONSafeValue[] | null | undefined; | ||
| export declare type DBContent = { | ||
| [x: string]: JSONSafeValue; | ||
| }; | ||
| export declare type DBOptions<T> = { | ||
| template?: T; | ||
| cryptKey?: string; | ||
| pretty?: true; | ||
| forceCreate?: true; | ||
| updateOnExternalChanges?: true; | ||
| }; | ||
| /** | ||
| * Returns a new file backed object database. | ||
| * @param file Database file. | ||
| * @param options.template Template object to initialize with. | ||
| * @param options.cryptKey Key to use for encryption. | ||
| * @param options.pretty Pretty print the JSON file. | ||
| * @param options.forceCreate Write out to file if it doesn't exist. | ||
| * @param options.updateOnExternalChanges Asynchronously update the database object if the file is changed externally. | ||
| */ | ||
| export default function db<T extends DBContent>(file: string, options?: DBOptions<T>): T; | ||
| //# sourceMappingURL=index.d.ts.map |
| {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,oBAAY,aAAa,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CAAA;CAAE,GAAG,aAAa,EAAE,GAAG,IAAI,GAAG,SAAS,CAAC;AAC9H,oBAAY,SAAS,GAAG;IAAE,CAAC,CAAC,EAAE,MAAM,GAAG,aAAa,CAAA;CAAE,CAAC;AAEvD,oBAAY,SAAS,CAAC,CAAC,IAAI;IAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,IAAI,CAAC;IAAC,WAAW,CAAC,EAAE,IAAI,CAAC;IAAC,uBAAuB,CAAC,EAAE,IAAI,CAAA;CAAE,CAAC;AAElI;;;;;;;;GAQG;AACH,MAAM,CAAC,OAAO,UAAU,EAAE,CAAC,CAAC,SAAS,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,SAAS,CAAC,CAAC,CAAM,GAAG,CAAC,CAoI3F"} |
+151
| import fs from "fs"; | ||
| import crypto from "crypto"; | ||
| /** | ||
| * Returns a new file backed object database. | ||
| * @param file Database file. | ||
| * @param options.template Template object to initialize with. | ||
| * @param options.cryptKey Key to use for encryption. | ||
| * @param options.pretty Pretty print the JSON file. | ||
| * @param options.forceCreate Write out to file if it doesn't exist. | ||
| * @param options.updateOnExternalChanges Asynchronously update the database object if the file is changed externally. | ||
| */ | ||
| export default function db(file, options = {}) { | ||
| if (typeof file !== "string") | ||
| throw new Error(`file must be string! Got: ${file}`); | ||
| const folder = file.replace(/\\/g, "/").split("/").slice(0, -1).join("/"); | ||
| const cryptKey = options.cryptKey; | ||
| const pretty = options.pretty === true && options.cryptKey === undefined; | ||
| const defaultDB = (options.template || {}); | ||
| let fileExists = fs.existsSync(file); | ||
| let store; | ||
| let stat; | ||
| let decrypt; | ||
| let encrypt; | ||
| if (cryptKey !== undefined) { | ||
| const hash = crypto.createHash("sha256"); | ||
| hash.update(cryptKey); | ||
| const key = hash.digest().slice(0, 32); | ||
| decrypt = (string) => { | ||
| string = string.toString(); | ||
| const textParts = string.split(":"); | ||
| const iv = Buffer.from(textParts.shift(), "hex"); | ||
| const decipher = crypto.createDecipheriv("aes-256-cbc", key, iv); | ||
| const encryptedText = Buffer.from(textParts.join(":"), "hex"); | ||
| let decrypted = decipher.update(encryptedText); | ||
| decrypted = Buffer.concat([decrypted, decipher.final()]); | ||
| return decrypted.toString(); | ||
| }; | ||
| encrypt = (string) => { | ||
| string = string.toString(); | ||
| const iv = crypto.randomBytes(16); | ||
| const cipher = crypto.createCipheriv("aes-256-cbc", key, iv); | ||
| let encrypted = cipher.update(string); | ||
| encrypted = Buffer.concat([encrypted, cipher.final()]); | ||
| return iv.toString("hex") + ":" + encrypted.toString("hex"); | ||
| }; | ||
| } | ||
| /** | ||
| * Writes store to disk. | ||
| */ | ||
| let disableWrites = false; | ||
| const _writeStore = (store) => { | ||
| if (disableWrites === true) | ||
| return; | ||
| let rawStoreData = JSON.stringify(store, null, pretty ? " " : undefined); | ||
| if (cryptKey !== undefined) | ||
| rawStoreData = encrypt(rawStoreData); | ||
| if (!fileExists && folder !== "" && !fs.existsSync(folder)) | ||
| fs.mkdirSync(folder, { recursive: true }); | ||
| fs.writeFileSync(file, rawStoreData); | ||
| }; | ||
| /** | ||
| * Reads store from disk. | ||
| * @returns Object of expected type T containing data from `file` | ||
| */ | ||
| const _readStore = () => { | ||
| let rawStoreData = fs.readFileSync(file).toString(); | ||
| if (rawStoreData === "") | ||
| throw new Error(`Database file ${file} is empty!`); | ||
| else if (cryptKey !== undefined) { | ||
| // Data was previously unencrypted, encrypt it | ||
| if (rawStoreData[0] === "{") | ||
| _writeStore(new Proxy(JSON.parse(rawStoreData), handler)); | ||
| // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
| else | ||
| rawStoreData = decrypt(rawStoreData); | ||
| } | ||
| return JSON.parse(rawStoreData); | ||
| }; | ||
| /** | ||
| * Recursively update `_old` to mirror `_new` ensuring that sub references to store's children are maintained. | ||
| * @param _new New object. | ||
| * @param _old Old object to be updated. | ||
| */ | ||
| const _recursiveUpdate = (_new, _old) => { | ||
| disableWrites = true; | ||
| _rSet(_new, _old); | ||
| _rClean(_new, _old); | ||
| disableWrites = false; | ||
| }; | ||
| /** | ||
| * Update/Set all values from new to old. | ||
| */ | ||
| const _rSet = (_new, _old) => { | ||
| for (const key in _new) { | ||
| if (typeof _new[key] === "object" && typeof _old[key] === "object") | ||
| _rSet(_new[key], _old[key]); | ||
| else | ||
| _old[key] = _new[key]; | ||
| } | ||
| }; | ||
| /** | ||
| * Remove any values from old that are not in new. | ||
| */ | ||
| const _rClean = (_new, _old) => { | ||
| for (const key in _old) { | ||
| if (typeof _new[key] === "object" && typeof _old[key] === "object") | ||
| _rClean(_new[key], _old[key]); | ||
| else if (_new[key] === undefined) | ||
| delete _old[key]; | ||
| } | ||
| }; | ||
| const _watchFile = () => { | ||
| stat = fs.statSync(file); | ||
| let watchTimeout = setTimeout(() => null, 0); | ||
| fs.watch(file, () => { | ||
| // Debounce fs.watch to only emit one event per 5ms | ||
| clearTimeout(watchTimeout); | ||
| watchTimeout = setTimeout(() => { | ||
| // If the file modified is not the same as the one held in memory, reload it | ||
| if (stat.mtimeMs !== fs.statSync(file).mtimeMs) | ||
| _recursiveUpdate(_readStore(), store); | ||
| }, 5); | ||
| }); | ||
| }; | ||
| const handler = { | ||
| get: (target, key, receiver) => { | ||
| if (target[key] !== null && typeof target[key] === "object") | ||
| return new Proxy(target[key], handler); | ||
| else | ||
| return Reflect.get(target, key, receiver); | ||
| }, | ||
| set: (target, key, value) => { | ||
| target[key] = value; | ||
| _writeStore(store); | ||
| return true; | ||
| }, | ||
| }; | ||
| if (fileExists) | ||
| store = new Proxy(_readStore(), handler); | ||
| else { | ||
| store = new Proxy({ ...defaultDB }, handler); | ||
| if (options.forceCreate === true || options.updateOnExternalChanges === true) { | ||
| _writeStore(defaultDB); | ||
| fileExists = true; | ||
| } | ||
| } | ||
| if (options.updateOnExternalChanges === true) | ||
| _watchFile(); | ||
| return store; | ||
| } | ||
| //# sourceMappingURL=index.js.map |
| {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"} |
+2
-2
| { | ||
| "name": "@inrixia/db", | ||
| "version": "2.0.1", | ||
| "version": "2.0.2", | ||
| "description": "Transparent object storage", | ||
@@ -13,3 +13,3 @@ "type": "module", | ||
| "scripts": { | ||
| "postinstall": "tsc", | ||
| "prepare": "tsc", | ||
| "test": "vitest" | ||
@@ -16,0 +16,0 @@ }, |
-150
| import fs from "fs"; | ||
| import crypto from "crypto"; | ||
| export type JSONSafeValue = string | number | boolean | { [key: string]: JSONSafeValue } | JSONSafeValue[] | null | undefined; | ||
| export type DBContent = { [x: string]: JSONSafeValue }; | ||
| export type DBOptions<T> = { template?: T; cryptKey?: string; pretty?: true; forceCreate?: true; updateOnExternalChanges?: true }; | ||
| /** | ||
| * Returns a new file backed object database. | ||
| * @param file Database file. | ||
| * @param options.template Template object to initialize with. | ||
| * @param options.cryptKey Key to use for encryption. | ||
| * @param options.pretty Pretty print the JSON file. | ||
| * @param options.forceCreate Write out to file if it doesn't exist. | ||
| * @param options.updateOnExternalChanges Asynchronously update the database object if the file is changed externally. | ||
| */ | ||
| export default function db<T extends DBContent>(file: string, options: DBOptions<T> = {}): T { | ||
| if (typeof file !== "string") throw new Error(`file must be string! Got: ${file}`); | ||
| const folder: string = file.replace(/\\/g, "/").split("/").slice(0, -1).join("/"); | ||
| const cryptKey = options.cryptKey; | ||
| const pretty = options.pretty === true && options.cryptKey === undefined; | ||
| const defaultDB: T = <T>(options.template || {}); | ||
| let fileExists = fs.existsSync(file); | ||
| let store: T; | ||
| let stat: fs.Stats; | ||
| let decrypt: (s: string) => string; | ||
| let encrypt: (s: string) => string; | ||
| if (cryptKey !== undefined) { | ||
| const hash = crypto.createHash("sha256"); | ||
| hash.update(cryptKey); | ||
| const key = hash.digest().slice(0, 32); | ||
| decrypt = (string: string) => { | ||
| string = string.toString(); | ||
| const textParts = string.split(":"); | ||
| const iv = Buffer.from(textParts.shift() as string, "hex"); | ||
| const decipher = crypto.createDecipheriv("aes-256-cbc", key, iv); | ||
| const encryptedText = Buffer.from(textParts.join(":"), "hex"); | ||
| let decrypted = decipher.update(encryptedText); | ||
| decrypted = Buffer.concat([decrypted, decipher.final()]); | ||
| return decrypted.toString(); | ||
| }; | ||
| encrypt = (string: string) => { | ||
| string = string.toString(); | ||
| const iv = crypto.randomBytes(16); | ||
| const cipher = crypto.createCipheriv("aes-256-cbc", key, iv); | ||
| let encrypted = cipher.update(string); | ||
| encrypted = Buffer.concat([encrypted, cipher.final()]); | ||
| return iv.toString("hex") + ":" + encrypted.toString("hex"); | ||
| }; | ||
| } | ||
| /** | ||
| * Writes store to disk. | ||
| */ | ||
| let disableWrites = false; | ||
| const _writeStore = (store: T) => { | ||
| if (disableWrites === true) return; | ||
| let rawStoreData = JSON.stringify(store, null, pretty ? " " : undefined); | ||
| if (cryptKey !== undefined) rawStoreData = encrypt(rawStoreData); | ||
| if (!fileExists && folder !== "" && !fs.existsSync(folder)) fs.mkdirSync(folder, { recursive: true }); | ||
| fs.writeFileSync(file, rawStoreData); | ||
| }; | ||
| /** | ||
| * Reads store from disk. | ||
| * @returns Object of expected type T containing data from `file` | ||
| */ | ||
| const _readStore = (): T => { | ||
| let rawStoreData = fs.readFileSync(file).toString(); | ||
| if (rawStoreData === "") throw new Error(`Database file ${file} is empty!`); | ||
| else if (cryptKey !== undefined) { | ||
| // Data was previously unencrypted, encrypt it | ||
| if (rawStoreData[0] === "{") _writeStore(new Proxy(JSON.parse(rawStoreData), handler)); | ||
| // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
| else rawStoreData = decrypt!(rawStoreData); | ||
| } | ||
| return JSON.parse(rawStoreData); | ||
| }; | ||
| /** | ||
| * Recursively update `_old` to mirror `_new` ensuring that sub references to store's children are maintained. | ||
| * @param _new New object. | ||
| * @param _old Old object to be updated. | ||
| */ | ||
| const _recursiveUpdate = (_new: T, _old: T) => { | ||
| disableWrites = true; | ||
| _rSet(_new, _old); | ||
| _rClean(_new, _old); | ||
| disableWrites = false; | ||
| }; | ||
| /** | ||
| * Update/Set all values from new to old. | ||
| */ | ||
| const _rSet = (_new: DBContent, _old: DBContent) => { | ||
| for (const key in _new) { | ||
| if (typeof _new[key] === "object" && typeof _old[key] === "object") _rSet(<DBContent>_new[key], <DBContent>_old[key]); | ||
| else _old[key] = _new[key]; | ||
| } | ||
| }; | ||
| /** | ||
| * Remove any values from old that are not in new. | ||
| */ | ||
| const _rClean = (_new: DBContent, _old: DBContent) => { | ||
| for (const key in _old) { | ||
| if (typeof _new[key] === "object" && typeof _old[key] === "object") _rClean(<DBContent>_new[key], <DBContent>_old[key]); | ||
| else if (_new[key] === undefined) delete _old[key]; | ||
| } | ||
| }; | ||
| const _watchFile = () => { | ||
| stat = fs.statSync(file); | ||
| let watchTimeout = setTimeout(() => null, 0); | ||
| fs.watch(file, () => { | ||
| // Debounce fs.watch to only emit one event per 5ms | ||
| clearTimeout(watchTimeout); | ||
| watchTimeout = setTimeout(() => { | ||
| // If the file modified is not the same as the one held in memory, reload it | ||
| if (stat.mtimeMs !== fs.statSync(file).mtimeMs) _recursiveUpdate(_readStore(), store); | ||
| }, 5); | ||
| }); | ||
| }; | ||
| const handler: ProxyHandler<T> = { | ||
| get: (target, key: string, receiver) => { | ||
| if (target[key] !== null && typeof target[key] === "object") return new Proxy(target[key] as T, handler); | ||
| else return Reflect.get(target, key, receiver); | ||
| }, | ||
| set: (target: DBContent, key: string, value) => { | ||
| target[key] = value; | ||
| _writeStore(store); | ||
| return true; | ||
| }, | ||
| }; | ||
| if (fileExists) store = new Proxy(_readStore(), handler); | ||
| else { | ||
| store = new Proxy({ ...defaultDB }, handler); | ||
| if (options.forceCreate === true || options.updateOnExternalChanges === true) { | ||
| _writeStore(defaultDB); | ||
| fileExists = true; | ||
| } | ||
| } | ||
| if (options.updateOnExternalChanges === true) _watchFile(); | ||
| return store; | ||
| } |
| { | ||
| "compilerOptions": { | ||
| /* Visit https://aka.ms/tsconfig.json to read more about this file */ | ||
| /* Basic Options */ | ||
| // "incremental": true, /* Enable incremental compilation */ | ||
| "target": "ESNEXT" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, | ||
| "module": "NodeNext" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, | ||
| "declaration": true /* Generates corresponding '.d.ts' file. */, | ||
| "declarationMap": true /* Generates a sourcemap for each corresponding '.d.ts' file. */, | ||
| "sourceMap": true /* Generates corresponding '.map' file. */, | ||
| // "outFile": "./", /* Concatenate and emit output to single file. */ | ||
| "outDir": "./dist" /* Redirect output structure to the directory. */, | ||
| "rootDir": "./src" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */, | ||
| "esModuleInterop": true, | ||
| /* Strict Type-Checking Options */ | ||
| "strict": true /* Enable all strict type-checking options. */, | ||
| /* Advanced Options */ | ||
| "skipLibCheck": true /* Skip type checking of declaration files. */, | ||
| "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ | ||
| }, | ||
| "files": ["./src/index.ts"] | ||
| } |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Install scripts
Supply chain riskInstall scripts are run when the package is installed or built. Malicious packages often use scripts that run automatically to execute payloads or fetch additional code.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
14625
68.43%6
50%173
8.81%0
-100%