mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 18:36:30 +01:00 
			
		
		
		
	DB dump tool feature complete
This commit is contained in:
		| @@ -17,6 +17,18 @@ npm install | ||||
| See output of `node dump-db.js --help`: | ||||
|  | ||||
| ``` | ||||
| Trilium Notes DB dump tool. Usage: | ||||
| node dump-db.js PATH_TO_DOCUMENT_DB TARGET_PATH | ||||
| dump-db.js <path_to_document> <target_directory> | ||||
|  | ||||
| dump the contents of document.db into the target directory | ||||
|  | ||||
| Positionals: | ||||
| path_to_document  path to the document.db | ||||
| target_directory  path of the directory into which the notes should be dumped | ||||
|  | ||||
| Options: | ||||
| --help             Show help                                         [boolean] | ||||
| --version          Show version number                               [boolean] | ||||
| --password         Set password to be able to decrypt protected notes.[string] | ||||
| --include-deleted  If set to true, dump also deleted notes. | ||||
| [boolean] [default: false] | ||||
| ``` | ||||
|   | ||||
| @@ -1,132 +1,33 @@ | ||||
| #!/usr/bin/env node | ||||
|  | ||||
| const fs = require('fs'); | ||||
| const sql = require("./inc/sql"); | ||||
| const yargs = require('yargs/yargs') | ||||
| const { hideBin } = require('yargs/helpers') | ||||
| const dumpService = require("./inc/dump.js"); | ||||
|  | ||||
| const args = process.argv.slice(2); | ||||
| const sanitize = require('sanitize-filename'); | ||||
| const path = require("path"); | ||||
| const mimeTypes = require("mime-types"); | ||||
|  | ||||
| if (args[0] === '-h' || args[0] === '--help') { | ||||
|     printHelp(); | ||||
|     process.exit(0); | ||||
| } | ||||
|  | ||||
| if (args.length !== 2) { | ||||
|     console.error(`Exactly 2 arguments are expected. Run with --help to see usage.`); | ||||
|     process.exit(1); | ||||
| } | ||||
|  | ||||
| const [documentPath, targetPath] = args; | ||||
|  | ||||
| if (!fs.existsSync(documentPath)) { | ||||
|     console.error(`Path to document '${documentPath}' has not been found. Run with --help to see usage.`); | ||||
|     process.exit(1); | ||||
| } | ||||
|  | ||||
| if (!fs.existsSync(targetPath)) { | ||||
|     const ret = fs.mkdirSync(targetPath, { recursive: true }); | ||||
|  | ||||
|     if (!ret) { | ||||
|         console.error(`Target path '${targetPath}' could not be created. Run with --help to see usage.`); | ||||
|         process.exit(1); | ||||
|     } | ||||
| } | ||||
|  | ||||
| sql.openDatabase(documentPath); | ||||
|  | ||||
| const existingPaths = {}; | ||||
|  | ||||
| dumpNote(targetPath, 'root'); | ||||
|  | ||||
| function getFileName(note, childTargetPath, safeTitle) { | ||||
|     let existingExtension = path.extname(safeTitle).toLowerCase(); | ||||
|     let newExtension; | ||||
|  | ||||
|     if (note.type === 'text') { | ||||
|         newExtension = 'html'; | ||||
|     } else if (note.mime === 'application/x-javascript' || note.mime === 'text/javascript') { | ||||
|         newExtension = 'js'; | ||||
|     } else if (existingExtension.length > 0) { // if the page already has an extension, then we'll just keep it | ||||
|         newExtension = null; | ||||
|     } else { | ||||
|         if (note.mime?.toLowerCase()?.trim() === "image/jpg") { // image/jpg is invalid but pretty common | ||||
|             newExtension = 'jpg'; | ||||
|         } else { | ||||
|             newExtension = mimeTypes.extension(note.mime) || "dat"; | ||||
| yargs(hideBin(process.argv)) | ||||
|     .command('$0 <path_to_document> <target_directory>', 'dump the contents of document.db into the target directory', (yargs) => { | ||||
|         return yargs | ||||
|             .positional('path_to_document', { describe: 'path to the document.db' }) | ||||
|             .positional('target_directory', { describe: 'path of the directory into which the notes should be dumped' }) | ||||
|     }, (argv) => { | ||||
|         try { | ||||
|             dumpService.dumpDocument(argv.path_to_document, argv.target_directory, { | ||||
|                 includeDeleted: argv.includeDeleted, | ||||
|                 password: argv.password | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     let fileNameWithPath = childTargetPath; | ||||
|  | ||||
|     // if the note is already named with extension (e.g. "jquery"), then it's silly to append exact same extension again | ||||
|     if (newExtension && existingExtension !== "." + newExtension.toLowerCase()) { | ||||
|         fileNameWithPath += "." + newExtension; | ||||
|     } | ||||
|     return fileNameWithPath; | ||||
| } | ||||
|  | ||||
| function dumpNote(targetPath, noteId) { | ||||
|     console.log(`Dumping note ${noteId}`); | ||||
|  | ||||
|     const note = sql.getRow("SELECT * FROM notes WHERE noteId = ?", [noteId]); | ||||
|  | ||||
|     let safeTitle = sanitize(note.title); | ||||
|  | ||||
|     if (safeTitle.length > 20) { | ||||
|         safeTitle = safeTitle.substring(0, 20); | ||||
|     } | ||||
|  | ||||
|     let childTargetPath = targetPath + '/' + safeTitle; | ||||
|  | ||||
|     for (let i = 1; i < 100000 && childTargetPath in existingPaths; i++) { | ||||
|         childTargetPath = targetPath + '/' + safeTitle + '_' + i; | ||||
|     } | ||||
|  | ||||
|     existingPaths[childTargetPath] = true; | ||||
|  | ||||
|     try { | ||||
|         const {content} = sql.getRow("SELECT content FROM note_contents WHERE noteId = ?", [noteId]); | ||||
|  | ||||
|         if (!isContentEmpty(content)) { | ||||
|             const fileNameWithPath = getFileName(note, childTargetPath, safeTitle); | ||||
|  | ||||
|             fs.writeFileSync(fileNameWithPath, content); | ||||
|         catch (e) { | ||||
|             console.error(`Unrecoverable error:`, e); | ||||
|             process.exit(1); | ||||
|         } | ||||
|     } | ||||
|     catch (e) { | ||||
|         console.log(`Writing ${note.noteId} failed with error ${e.message}`); | ||||
|     } | ||||
|  | ||||
|     const childNoteIds = sql.getColumn("SELECT noteId FROM branches WHERE parentNoteId = ?", [noteId]); | ||||
|  | ||||
|     if (childNoteIds.length > 0) { | ||||
|         fs.mkdirSync(childTargetPath, { recursive: true }); | ||||
|  | ||||
|         for (const childNoteId of childNoteIds) { | ||||
|             dumpNote(childTargetPath, childNoteId); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| function isContentEmpty(content) { | ||||
|     if (!content) { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     if (typeof content === "string") { | ||||
|         return !content.trim() || content.trim() === '<p></p>'; | ||||
|     } | ||||
|     else if (Buffer.isBuffer(content)) { | ||||
|         return content.length === 0; | ||||
|     } | ||||
|     else { | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
|  | ||||
| function printHelp() { | ||||
|     console.log(`Trilium Notes DB dump tool. Usage: | ||||
| node dump-db.js PATH_TO_DOCUMENT_DB TARGET_PATH`); | ||||
| } | ||||
|     }) | ||||
|     .option('password', { | ||||
|         type: 'string', | ||||
|         description: 'Set password to be able to decrypt protected notes.' | ||||
|     }) | ||||
|     .option('include-deleted', { | ||||
|         type: 'boolean', | ||||
|         default: false, | ||||
|         description: 'If set to true, dump also deleted notes.' | ||||
|     }) | ||||
|     .parse(); | ||||
|   | ||||
| @@ -1,14 +1,24 @@ | ||||
| import crypto from "crypto"; | ||||
| import sql from "./sql.js"; | ||||
| const crypto = require("crypto"); | ||||
| const sql = require("./sql.js"); | ||||
| const decryptService = require("./decrypt.js"); | ||||
|  | ||||
| function getDataKey(password) { | ||||
|     const passwordDerivedKey = getPasswordDerivedKey(password); | ||||
|     if (!password) { | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     const encryptedDataKey = getOption('encryptedDataKey'); | ||||
|     try { | ||||
|         const passwordDerivedKey = getPasswordDerivedKey(password); | ||||
|  | ||||
|     const decryptedDataKey = decrypt(passwordDerivedKey, encryptedDataKey, 16); | ||||
|         const encryptedDataKey = getOption('encryptedDataKey'); | ||||
|  | ||||
|     return decryptedDataKey; | ||||
|         const decryptedDataKey = decryptService.decrypt(passwordDerivedKey, encryptedDataKey, 16); | ||||
|  | ||||
|         return decryptedDataKey; | ||||
|     } | ||||
|     catch (e) { | ||||
|         throw new Error(`Cannot read data key, the entered password might be wrong. The underlying error: '${e.message}', stack:\n${e.stack}`); | ||||
|     } | ||||
| } | ||||
|  | ||||
| function getPasswordDerivedKey(password) { | ||||
| @@ -25,7 +35,7 @@ function getScryptHash(password, salt) { | ||||
| } | ||||
|  | ||||
| function getOption(name) { | ||||
|     return sql.getValue("SELECT value FROM options WHERE name = ?", name); | ||||
|     return sql.getValue("SELECT value FROM options WHERE name = ?", [name]); | ||||
| } | ||||
|  | ||||
| module.exports = { | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import crypto from "crypto"; | ||||
| const crypto = require("crypto"); | ||||
|  | ||||
| function decryptString(dataKey, cipherText) { | ||||
|     const buffer = decrypt(dataKey, cipherText); | ||||
| @@ -59,6 +59,19 @@ function decrypt(key, cipherText, ivLength = 13) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| function pad(data) { | ||||
|     if (data.length > 16) { | ||||
|         data = data.slice(0, 16); | ||||
|     } | ||||
|     else if (data.length < 16) { | ||||
|         const zeros = Array(16 - data.length).fill(0); | ||||
|  | ||||
|         data = Buffer.concat([data, Buffer.from(zeros)]); | ||||
|     } | ||||
|  | ||||
|     return Buffer.from(data); | ||||
| } | ||||
|  | ||||
| function arraysIdentical(a, b) { | ||||
|     let i = a.length; | ||||
|     if (i !== b.length) return false; | ||||
|   | ||||
							
								
								
									
										171
									
								
								dump-db/inc/dump.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										171
									
								
								dump-db/inc/dump.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,171 @@ | ||||
| const fs = require("fs"); | ||||
| const sanitize = require("sanitize-filename"); | ||||
| const sql = require("./sql.js"); | ||||
| const decryptService = require("./decrypt.js"); | ||||
| const dataKeyService = require("./data_key.js"); | ||||
| const extensionService = require("./extension.js"); | ||||
|  | ||||
| function dumpDocument(documentPath, targetPath, options) { | ||||
|     const stats = { | ||||
|         succeeded: 0, | ||||
|         failed: 0, | ||||
|         protected: 0, | ||||
|         deleted: 0 | ||||
|     }; | ||||
|  | ||||
|     validatePaths(documentPath, targetPath); | ||||
|  | ||||
|     sql.openDatabase(documentPath); | ||||
|  | ||||
|     const dataKey = dataKeyService.getDataKey(options.password); | ||||
|  | ||||
|     const existingPaths = {}; | ||||
|     const noteIdToPath = {}; | ||||
|  | ||||
|     dumpNote(targetPath, 'root'); | ||||
|  | ||||
|     printDumpResults(stats, options); | ||||
|  | ||||
|     function dumpNote(targetPath, noteId) { | ||||
|         console.log(`Reading note '${noteId}'`); | ||||
|  | ||||
|         let childTargetPath, note, fileNameWithPath; | ||||
|  | ||||
|         try { | ||||
|             note = sql.getRow("SELECT * FROM notes WHERE noteId = ?", [noteId]); | ||||
|  | ||||
|             if (note.isDeleted) { | ||||
|                 stats.deleted++; | ||||
|  | ||||
|                 if (!options.includeDeleted) { | ||||
|                     console.log(`Note '${noteId}' is deleted and --include-deleted option is not used, skipping.`); | ||||
|  | ||||
|                     return; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             if (note.isProtected) { | ||||
|                 stats.protected++; | ||||
|  | ||||
|                 note.title = decryptService.decryptString(dataKey, note.title); | ||||
|             } | ||||
|  | ||||
|             let safeTitle = sanitize(note.title); | ||||
|  | ||||
|             if (safeTitle.length > 20) { | ||||
|                 safeTitle = safeTitle.substring(0, 20); | ||||
|             } | ||||
|  | ||||
|             childTargetPath = targetPath + '/' + safeTitle; | ||||
|  | ||||
|             for (let i = 1; i < 100000 && childTargetPath in existingPaths; i++) { | ||||
|                 childTargetPath = targetPath + '/' + safeTitle + '_' + i; | ||||
|             } | ||||
|  | ||||
|             existingPaths[childTargetPath] = true; | ||||
|  | ||||
|             if (note.noteId in noteIdToPath) { | ||||
|                 const message = `Note '${noteId}' has been already dumped to ${noteIdToPath[note.noteId]}`; | ||||
|  | ||||
|                 console.log(message); | ||||
|  | ||||
|                 fs.writeFileSync(childTargetPath, message); | ||||
|  | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             let {content} = sql.getRow("SELECT content FROM note_contents WHERE noteId = ?", [noteId]); | ||||
|  | ||||
|             if (content !== null && note.isProtected && dataKey) { | ||||
|                 content = decryptService.decrypt(dataKey, content); | ||||
|             } | ||||
|  | ||||
|             if (isContentEmpty(content)) { | ||||
|                 console.log(`Note '${noteId}' is empty, skipping.`); | ||||
|             } else { | ||||
|                 fileNameWithPath = extensionService.getFileName(note, childTargetPath, safeTitle); | ||||
|  | ||||
|                 fs.writeFileSync(fileNameWithPath, content); | ||||
|  | ||||
|                 stats.succeeded++; | ||||
|  | ||||
|                 console.log(`Dumped note '${noteId}' into ${fileNameWithPath} successfully.`); | ||||
|             } | ||||
|  | ||||
|             noteIdToPath[noteId] = childTargetPath; | ||||
|         } | ||||
|         catch (e) { | ||||
|             console.error(`DUMPERROR: Writing '${noteId}' failed with error '${e.message}':\n${e.stack}`); | ||||
|  | ||||
|             stats.failed++; | ||||
|         } | ||||
|  | ||||
|         const childNoteIds = sql.getColumn("SELECT noteId FROM branches WHERE parentNoteId = ?", [noteId]); | ||||
|  | ||||
|         if (childNoteIds.length > 0) { | ||||
|             if (childTargetPath === fileNameWithPath) { | ||||
|                 childTargetPath += '_dir'; | ||||
|             } | ||||
|  | ||||
|             try { | ||||
|                 fs.mkdirSync(childTargetPath, {recursive: true}); | ||||
|             } | ||||
|             catch (e) { | ||||
|                 console.error(`DUMPERROR: Creating directory ${childTargetPath} failed with error '${e.message}'`); | ||||
|             } | ||||
|  | ||||
|             for (const childNoteId of childNoteIds) { | ||||
|                 dumpNote(childTargetPath, childNoteId); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| function printDumpResults(stats, options) { | ||||
|     console.log('\n----------------------- STATS -----------------------'); | ||||
|     console.log('Successfully dumpted notes:   ', stats.succeeded.toString().padStart(5, ' ')); | ||||
|     console.log('Protected notes:              ', stats.protected.toString().padStart(5, ' '), options.password ? '' : '(skipped)'); | ||||
|     console.log('Failed notes:                 ', stats.failed.toString().padStart(5, ' ')); | ||||
|     console.log('Deleted notes:                ', stats.deleted.toString().padStart(5, ' '), options.includeDeleted ? "(dumped)" : "(at least, skipped)"); | ||||
|     console.log('-----------------------------------------------------'); | ||||
|  | ||||
|     if (!options.password && stats.protected > 0) { | ||||
|         console.log("\nWARNING: protected notes are present in the document but no password has been provided. Protected notes have not been dumped."); | ||||
|     } | ||||
| } | ||||
|  | ||||
| function isContentEmpty(content) { | ||||
|     if (!content) { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     if (typeof content === "string") { | ||||
|         return !content.trim() || content.trim() === '<p></p>'; | ||||
|     } | ||||
|     else if (Buffer.isBuffer(content)) { | ||||
|         return content.length === 0; | ||||
|     } | ||||
|     else { | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
|  | ||||
| function validatePaths(documentPath, targetPath) { | ||||
|     if (!fs.existsSync(documentPath)) { | ||||
|         console.error(`Path to document '${documentPath}' has not been found. Run with --help to see usage.`); | ||||
|         process.exit(1); | ||||
|     } | ||||
|  | ||||
|     if (!fs.existsSync(targetPath)) { | ||||
|         const ret = fs.mkdirSync(targetPath, {recursive: true}); | ||||
|  | ||||
|         if (!ret) { | ||||
|             console.error(`Target path '${targetPath}' could not be created. Run with --help to see usage.`); | ||||
|             process.exit(1); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| module.exports = { | ||||
|     dumpDocument | ||||
| }; | ||||
							
								
								
									
										34
									
								
								dump-db/inc/extension.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								dump-db/inc/extension.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| const path = require("path"); | ||||
| const mimeTypes = require("mime-types"); | ||||
|  | ||||
| function getFileName(note, childTargetPath, safeTitle) { | ||||
|     let existingExtension = path.extname(safeTitle).toLowerCase(); | ||||
|     let newExtension; | ||||
|  | ||||
|     if (note.type === 'text') { | ||||
|         newExtension = 'html'; | ||||
|     } else if (note.mime === 'application/x-javascript' || note.mime === 'text/javascript') { | ||||
|         newExtension = 'js'; | ||||
|     } else if (existingExtension.length > 0) { // if the page already has an extension, then we'll just keep it | ||||
|         newExtension = null; | ||||
|     } else { | ||||
|         if (note.mime?.toLowerCase()?.trim() === "image/jpg") { // image/jpg is invalid but pretty common | ||||
|             newExtension = 'jpg'; | ||||
|         } else { | ||||
|             newExtension = mimeTypes.extension(note.mime) || "dat"; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     let fileNameWithPath = childTargetPath; | ||||
|  | ||||
|     // if the note is already named with extension (e.g. "jquery"), then it's silly to append exact same extension again | ||||
|     if (newExtension && existingExtension !== "." + newExtension.toLowerCase()) { | ||||
|         fileNameWithPath += "." + newExtension; | ||||
|     } | ||||
|  | ||||
|     return fileNameWithPath; | ||||
| } | ||||
|  | ||||
| module.exports = { | ||||
|     getFileName | ||||
| }; | ||||
							
								
								
									
										418
									
								
								dump-db/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										418
									
								
								dump-db/package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -10,8 +10,9 @@ | ||||
|       "license": "ISC", | ||||
|       "dependencies": { | ||||
|         "better-sqlite3": "7.5.0", | ||||
|         "mime-types": "^2.1.34", | ||||
|         "sanitize-filename": "1.6.3" | ||||
|         "mime-types": "2.1.34", | ||||
|         "sanitize-filename": "1.6.3", | ||||
|         "yargs": "17.3.1" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/ansi-regex": { | ||||
| @@ -124,6 +125,56 @@ | ||||
|       "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", | ||||
|       "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" | ||||
|     }, | ||||
|     "node_modules/cliui": { | ||||
|       "version": "7.0.4", | ||||
|       "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", | ||||
|       "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", | ||||
|       "dependencies": { | ||||
|         "string-width": "^4.2.0", | ||||
|         "strip-ansi": "^6.0.0", | ||||
|         "wrap-ansi": "^7.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/cliui/node_modules/ansi-regex": { | ||||
|       "version": "5.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", | ||||
|       "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", | ||||
|       "engines": { | ||||
|         "node": ">=8" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/cliui/node_modules/is-fullwidth-code-point": { | ||||
|       "version": "3.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", | ||||
|       "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", | ||||
|       "engines": { | ||||
|         "node": ">=8" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/cliui/node_modules/string-width": { | ||||
|       "version": "4.2.3", | ||||
|       "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", | ||||
|       "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", | ||||
|       "dependencies": { | ||||
|         "emoji-regex": "^8.0.0", | ||||
|         "is-fullwidth-code-point": "^3.0.0", | ||||
|         "strip-ansi": "^6.0.1" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=8" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/cliui/node_modules/strip-ansi": { | ||||
|       "version": "6.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", | ||||
|       "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", | ||||
|       "dependencies": { | ||||
|         "ansi-regex": "^5.0.1" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=8" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/code-point-at": { | ||||
|       "version": "1.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", | ||||
| @@ -177,6 +228,11 @@ | ||||
|         "node": ">=8" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/emoji-regex": { | ||||
|       "version": "8.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", | ||||
|       "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" | ||||
|     }, | ||||
|     "node_modules/end-of-stream": { | ||||
|       "version": "1.4.4", | ||||
|       "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", | ||||
| @@ -185,6 +241,14 @@ | ||||
|         "once": "^1.4.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/escalade": { | ||||
|       "version": "3.1.1", | ||||
|       "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", | ||||
|       "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", | ||||
|       "engines": { | ||||
|         "node": ">=6" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/expand-template": { | ||||
|       "version": "2.0.3", | ||||
|       "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", | ||||
| @@ -218,6 +282,14 @@ | ||||
|         "wide-align": "^1.1.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/get-caller-file": { | ||||
|       "version": "2.0.5", | ||||
|       "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", | ||||
|       "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", | ||||
|       "engines": { | ||||
|         "node": "6.* || 8.* || >= 10.*" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/github-from-package": { | ||||
|       "version": "0.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", | ||||
| @@ -443,6 +515,14 @@ | ||||
|         "util-deprecate": "~1.0.1" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/require-directory": { | ||||
|       "version": "2.1.1", | ||||
|       "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", | ||||
|       "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", | ||||
|       "engines": { | ||||
|         "node": ">=0.10.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/safe-buffer": { | ||||
|       "version": "5.1.2", | ||||
|       "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", | ||||
| @@ -639,15 +719,174 @@ | ||||
|         "string-width": "^1.0.2 || 2 || 3 || 4" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/wrap-ansi": { | ||||
|       "version": "7.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", | ||||
|       "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", | ||||
|       "dependencies": { | ||||
|         "ansi-styles": "^4.0.0", | ||||
|         "string-width": "^4.1.0", | ||||
|         "strip-ansi": "^6.0.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=10" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "url": "https://github.com/chalk/wrap-ansi?sponsor=1" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/wrap-ansi/node_modules/ansi-regex": { | ||||
|       "version": "5.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", | ||||
|       "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", | ||||
|       "engines": { | ||||
|         "node": ">=8" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/wrap-ansi/node_modules/ansi-styles": { | ||||
|       "version": "4.3.0", | ||||
|       "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", | ||||
|       "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", | ||||
|       "dependencies": { | ||||
|         "color-convert": "^2.0.1" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=8" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "url": "https://github.com/chalk/ansi-styles?sponsor=1" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/wrap-ansi/node_modules/color-convert": { | ||||
|       "version": "2.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", | ||||
|       "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", | ||||
|       "dependencies": { | ||||
|         "color-name": "~1.1.4" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=7.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/wrap-ansi/node_modules/color-name": { | ||||
|       "version": "1.1.4", | ||||
|       "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", | ||||
|       "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" | ||||
|     }, | ||||
|     "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { | ||||
|       "version": "3.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", | ||||
|       "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", | ||||
|       "engines": { | ||||
|         "node": ">=8" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/wrap-ansi/node_modules/string-width": { | ||||
|       "version": "4.2.3", | ||||
|       "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", | ||||
|       "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", | ||||
|       "dependencies": { | ||||
|         "emoji-regex": "^8.0.0", | ||||
|         "is-fullwidth-code-point": "^3.0.0", | ||||
|         "strip-ansi": "^6.0.1" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=8" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/wrap-ansi/node_modules/strip-ansi": { | ||||
|       "version": "6.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", | ||||
|       "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", | ||||
|       "dependencies": { | ||||
|         "ansi-regex": "^5.0.1" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=8" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/wrappy": { | ||||
|       "version": "1.0.2", | ||||
|       "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", | ||||
|       "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" | ||||
|     }, | ||||
|     "node_modules/y18n": { | ||||
|       "version": "5.0.8", | ||||
|       "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", | ||||
|       "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", | ||||
|       "engines": { | ||||
|         "node": ">=10" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/yallist": { | ||||
|       "version": "4.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", | ||||
|       "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" | ||||
|     }, | ||||
|     "node_modules/yargs": { | ||||
|       "version": "17.3.1", | ||||
|       "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.1.tgz", | ||||
|       "integrity": "sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA==", | ||||
|       "dependencies": { | ||||
|         "cliui": "^7.0.2", | ||||
|         "escalade": "^3.1.1", | ||||
|         "get-caller-file": "^2.0.5", | ||||
|         "require-directory": "^2.1.1", | ||||
|         "string-width": "^4.2.3", | ||||
|         "y18n": "^5.0.5", | ||||
|         "yargs-parser": "^21.0.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=12" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/yargs-parser": { | ||||
|       "version": "21.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.0.tgz", | ||||
|       "integrity": "sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA==", | ||||
|       "engines": { | ||||
|         "node": ">=12" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/yargs/node_modules/ansi-regex": { | ||||
|       "version": "5.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", | ||||
|       "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", | ||||
|       "engines": { | ||||
|         "node": ">=8" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/yargs/node_modules/is-fullwidth-code-point": { | ||||
|       "version": "3.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", | ||||
|       "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", | ||||
|       "engines": { | ||||
|         "node": ">=8" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/yargs/node_modules/string-width": { | ||||
|       "version": "4.2.3", | ||||
|       "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", | ||||
|       "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", | ||||
|       "dependencies": { | ||||
|         "emoji-regex": "^8.0.0", | ||||
|         "is-fullwidth-code-point": "^3.0.0", | ||||
|         "strip-ansi": "^6.0.1" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=8" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/yargs/node_modules/strip-ansi": { | ||||
|       "version": "6.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", | ||||
|       "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", | ||||
|       "dependencies": { | ||||
|         "ansi-regex": "^5.0.1" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=8" | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   "dependencies": { | ||||
| @@ -728,6 +967,46 @@ | ||||
|       "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", | ||||
|       "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" | ||||
|     }, | ||||
|     "cliui": { | ||||
|       "version": "7.0.4", | ||||
|       "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", | ||||
|       "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", | ||||
|       "requires": { | ||||
|         "string-width": "^4.2.0", | ||||
|         "strip-ansi": "^6.0.0", | ||||
|         "wrap-ansi": "^7.0.0" | ||||
|       }, | ||||
|       "dependencies": { | ||||
|         "ansi-regex": { | ||||
|           "version": "5.0.1", | ||||
|           "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", | ||||
|           "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" | ||||
|         }, | ||||
|         "is-fullwidth-code-point": { | ||||
|           "version": "3.0.0", | ||||
|           "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", | ||||
|           "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" | ||||
|         }, | ||||
|         "string-width": { | ||||
|           "version": "4.2.3", | ||||
|           "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", | ||||
|           "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", | ||||
|           "requires": { | ||||
|             "emoji-regex": "^8.0.0", | ||||
|             "is-fullwidth-code-point": "^3.0.0", | ||||
|             "strip-ansi": "^6.0.1" | ||||
|           } | ||||
|         }, | ||||
|         "strip-ansi": { | ||||
|           "version": "6.0.1", | ||||
|           "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", | ||||
|           "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", | ||||
|           "requires": { | ||||
|             "ansi-regex": "^5.0.1" | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "code-point-at": { | ||||
|       "version": "1.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", | ||||
| @@ -766,6 +1045,11 @@ | ||||
|       "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.0.tgz", | ||||
|       "integrity": "sha512-S55LzUl8HUav8l9E2PBTlC5PAJrHK7tkM+XXFGD+fbsbkTzhCpG6K05LxJcUOEWzMa4v6ptcMZ9s3fOdJDu0Zw==" | ||||
|     }, | ||||
|     "emoji-regex": { | ||||
|       "version": "8.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", | ||||
|       "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" | ||||
|     }, | ||||
|     "end-of-stream": { | ||||
|       "version": "1.4.4", | ||||
|       "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", | ||||
| @@ -774,6 +1058,11 @@ | ||||
|         "once": "^1.4.0" | ||||
|       } | ||||
|     }, | ||||
|     "escalade": { | ||||
|       "version": "3.1.1", | ||||
|       "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", | ||||
|       "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" | ||||
|     }, | ||||
|     "expand-template": { | ||||
|       "version": "2.0.3", | ||||
|       "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", | ||||
| @@ -804,6 +1093,11 @@ | ||||
|         "wide-align": "^1.1.0" | ||||
|       } | ||||
|     }, | ||||
|     "get-caller-file": { | ||||
|       "version": "2.0.5", | ||||
|       "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", | ||||
|       "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" | ||||
|     }, | ||||
|     "github-from-package": { | ||||
|       "version": "0.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", | ||||
| @@ -979,6 +1273,11 @@ | ||||
|         "util-deprecate": "~1.0.1" | ||||
|       } | ||||
|     }, | ||||
|     "require-directory": { | ||||
|       "version": "2.1.1", | ||||
|       "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", | ||||
|       "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" | ||||
|     }, | ||||
|     "safe-buffer": { | ||||
|       "version": "5.1.2", | ||||
|       "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", | ||||
| @@ -1125,15 +1424,130 @@ | ||||
|         "string-width": "^1.0.2 || 2 || 3 || 4" | ||||
|       } | ||||
|     }, | ||||
|     "wrap-ansi": { | ||||
|       "version": "7.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", | ||||
|       "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", | ||||
|       "requires": { | ||||
|         "ansi-styles": "^4.0.0", | ||||
|         "string-width": "^4.1.0", | ||||
|         "strip-ansi": "^6.0.0" | ||||
|       }, | ||||
|       "dependencies": { | ||||
|         "ansi-regex": { | ||||
|           "version": "5.0.1", | ||||
|           "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", | ||||
|           "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" | ||||
|         }, | ||||
|         "ansi-styles": { | ||||
|           "version": "4.3.0", | ||||
|           "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", | ||||
|           "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", | ||||
|           "requires": { | ||||
|             "color-convert": "^2.0.1" | ||||
|           } | ||||
|         }, | ||||
|         "color-convert": { | ||||
|           "version": "2.0.1", | ||||
|           "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", | ||||
|           "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", | ||||
|           "requires": { | ||||
|             "color-name": "~1.1.4" | ||||
|           } | ||||
|         }, | ||||
|         "color-name": { | ||||
|           "version": "1.1.4", | ||||
|           "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", | ||||
|           "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" | ||||
|         }, | ||||
|         "is-fullwidth-code-point": { | ||||
|           "version": "3.0.0", | ||||
|           "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", | ||||
|           "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" | ||||
|         }, | ||||
|         "string-width": { | ||||
|           "version": "4.2.3", | ||||
|           "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", | ||||
|           "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", | ||||
|           "requires": { | ||||
|             "emoji-regex": "^8.0.0", | ||||
|             "is-fullwidth-code-point": "^3.0.0", | ||||
|             "strip-ansi": "^6.0.1" | ||||
|           } | ||||
|         }, | ||||
|         "strip-ansi": { | ||||
|           "version": "6.0.1", | ||||
|           "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", | ||||
|           "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", | ||||
|           "requires": { | ||||
|             "ansi-regex": "^5.0.1" | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "wrappy": { | ||||
|       "version": "1.0.2", | ||||
|       "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", | ||||
|       "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" | ||||
|     }, | ||||
|     "y18n": { | ||||
|       "version": "5.0.8", | ||||
|       "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", | ||||
|       "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" | ||||
|     }, | ||||
|     "yallist": { | ||||
|       "version": "4.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", | ||||
|       "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" | ||||
|     }, | ||||
|     "yargs": { | ||||
|       "version": "17.3.1", | ||||
|       "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.1.tgz", | ||||
|       "integrity": "sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA==", | ||||
|       "requires": { | ||||
|         "cliui": "^7.0.2", | ||||
|         "escalade": "^3.1.1", | ||||
|         "get-caller-file": "^2.0.5", | ||||
|         "require-directory": "^2.1.1", | ||||
|         "string-width": "^4.2.3", | ||||
|         "y18n": "^5.0.5", | ||||
|         "yargs-parser": "^21.0.0" | ||||
|       }, | ||||
|       "dependencies": { | ||||
|         "ansi-regex": { | ||||
|           "version": "5.0.1", | ||||
|           "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", | ||||
|           "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" | ||||
|         }, | ||||
|         "is-fullwidth-code-point": { | ||||
|           "version": "3.0.0", | ||||
|           "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", | ||||
|           "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" | ||||
|         }, | ||||
|         "string-width": { | ||||
|           "version": "4.2.3", | ||||
|           "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", | ||||
|           "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", | ||||
|           "requires": { | ||||
|             "emoji-regex": "^8.0.0", | ||||
|             "is-fullwidth-code-point": "^3.0.0", | ||||
|             "strip-ansi": "^6.0.1" | ||||
|           } | ||||
|         }, | ||||
|         "strip-ansi": { | ||||
|           "version": "6.0.1", | ||||
|           "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", | ||||
|           "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", | ||||
|           "requires": { | ||||
|             "ansi-regex": "^5.0.1" | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "yargs-parser": { | ||||
|       "version": "21.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.0.tgz", | ||||
|       "integrity": "sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA==" | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -19,6 +19,7 @@ | ||||
|   "dependencies": { | ||||
|     "better-sqlite3": "7.5.0", | ||||
|     "mime-types": "2.1.34", | ||||
|     "sanitize-filename": "1.6.3" | ||||
|     "sanitize-filename": "1.6.3", | ||||
|     "yargs": "17.3.1" | ||||
|   } | ||||
| } | ||||
|   | ||||
							
								
								
									
										913
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										913
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										10
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								package.json
									
									
									
									
									
								
							| @@ -42,7 +42,7 @@ | ||||
|     "@electron/remote": "2.0.4", | ||||
|     "express": "4.17.2", | ||||
|     "express-partial-content": "1.0.2", | ||||
|     "express-rate-limit": "6.2.0", | ||||
|     "express-rate-limit": "6.2.1", | ||||
|     "express-session": "1.17.2", | ||||
|     "fs-extra": "10.0.0", | ||||
|     "helmet": "5.0.2", | ||||
| @@ -59,7 +59,7 @@ | ||||
|     "jsdom": "19.0.0", | ||||
|     "mime-types": "2.1.34", | ||||
|     "multer": "1.4.4", | ||||
|     "node-abi": "3.7.0", | ||||
|     "node-abi": "3.8.0", | ||||
|     "normalize-strings": "1.1.1", | ||||
|     "open": "8.4.0", | ||||
|     "portscanner": "2.2.0", | ||||
| @@ -67,7 +67,7 @@ | ||||
|     "request": "2.88.2", | ||||
|     "rimraf": "3.0.2", | ||||
|     "sanitize-filename": "1.6.3", | ||||
|     "sanitize-html": "2.6.1", | ||||
|     "sanitize-html": "2.7.0", | ||||
|     "sax": "1.2.4", | ||||
|     "semver": "7.3.5", | ||||
|     "serve-favicon": "2.5.0", | ||||
| @@ -77,13 +77,13 @@ | ||||
|     "tmp": "0.2.1", | ||||
|     "turndown": "7.1.1", | ||||
|     "unescape": "1.0.1", | ||||
|     "ws": "8.4.2", | ||||
|     "ws": "8.5.0", | ||||
|     "yauzl": "2.10.0" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "cross-env": "7.0.3", | ||||
|     "electron": "16.0.8", | ||||
|     "electron-builder": "22.14.5", | ||||
|     "electron-builder": "22.14.13", | ||||
|     "electron-packager": "15.4.0", | ||||
|     "electron-rebuild": "3.2.7", | ||||
|     "esm": "3.2.25", | ||||
|   | ||||
		Reference in New Issue
	
	Block a user